blob: cc0da1568eb882b038948f88cd25f56ff82d94ef [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 Lesinski94fc9122013-09-30 17:16:09 -0700410 RESOURCE_ATTR = 0x01010025,
Adam Lesinski282e1812014-01-23 18:17:42 -0800411 DEBUGGABLE_ATTR = 0x0101000f,
412 VALUE_ATTR = 0x01010024,
413 VERSION_CODE_ATTR = 0x0101021b,
414 VERSION_NAME_ATTR = 0x0101021c,
415 SCREEN_ORIENTATION_ATTR = 0x0101001e,
416 MIN_SDK_VERSION_ATTR = 0x0101020c,
417 MAX_SDK_VERSION_ATTR = 0x01010271,
418 REQ_TOUCH_SCREEN_ATTR = 0x01010227,
419 REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
420 REQ_HARD_KEYBOARD_ATTR = 0x01010229,
421 REQ_NAVIGATION_ATTR = 0x0101022a,
422 REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
423 TARGET_SDK_VERSION_ATTR = 0x01010270,
424 TEST_ONLY_ATTR = 0x01010272,
425 ANY_DENSITY_ATTR = 0x0101026c,
426 GL_ES_VERSION_ATTR = 0x01010281,
427 SMALL_SCREEN_ATTR = 0x01010284,
428 NORMAL_SCREEN_ATTR = 0x01010285,
429 LARGE_SCREEN_ATTR = 0x01010286,
430 XLARGE_SCREEN_ATTR = 0x010102bf,
431 REQUIRED_ATTR = 0x0101028e,
432 SCREEN_SIZE_ATTR = 0x010102ca,
433 SCREEN_DENSITY_ATTR = 0x010102cb,
434 REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
435 COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
436 LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
437 PUBLIC_KEY_ATTR = 0x010103a6,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700438 CATEGORY_ATTR = 0x010103e8,
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800439 BANNER_ATTR = 0x10103f2,
Adam Lesinski282e1812014-01-23 18:17:42 -0800440};
441
Maurice Chu2675f762013-10-22 17:33:11 -0700442String8 getComponentName(String8 &pkgName, String8 &componentName) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800443 ssize_t idx = componentName.find(".");
444 String8 retStr(pkgName);
445 if (idx == 0) {
446 retStr += componentName;
447 } else if (idx < 0) {
448 retStr += ".";
449 retStr += componentName;
450 } else {
Maurice Chu2675f762013-10-22 17:33:11 -0700451 return componentName;
Adam Lesinski282e1812014-01-23 18:17:42 -0800452 }
Maurice Chu2675f762013-10-22 17:33:11 -0700453 return retStr;
Adam Lesinski282e1812014-01-23 18:17:42 -0800454}
455
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700456static void printCompatibleScreens(ResXMLTree& tree, String8* outError) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800457 size_t len;
458 ResXMLTree::event_code_t code;
459 int depth = 0;
460 bool first = true;
461 printf("compatible-screens:");
462 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
463 if (code == ResXMLTree::END_TAG) {
464 depth--;
465 if (depth < 0) {
466 break;
467 }
468 continue;
469 }
470 if (code != ResXMLTree::START_TAG) {
471 continue;
472 }
473 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700474 const char16_t* ctag16 = tree.getElementName(&len);
475 if (ctag16 == NULL) {
476 *outError = "failed to get XML element name (bad string pool)";
477 return;
478 }
479 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800480 if (tag == "screen") {
481 int32_t screenSize = getIntegerAttribute(tree,
482 SCREEN_SIZE_ATTR, NULL, -1);
483 int32_t screenDensity = getIntegerAttribute(tree,
484 SCREEN_DENSITY_ATTR, NULL, -1);
485 if (screenSize > 0 && screenDensity > 0) {
486 if (!first) {
487 printf(",");
488 }
489 first = false;
490 printf("'%d/%d'", screenSize, screenDensity);
491 }
492 }
493 }
494 printf("\n");
495}
496
Adam Lesinski58f1f362013-11-12 12:59:08 -0800497static void printUsesPermission(const String8& name, bool optional=false, int maxSdkVersion=-1) {
498 printf("uses-permission: name='%s'", ResTable::normalizeForOutput(name.string()).string());
499 if (maxSdkVersion != -1) {
500 printf(" maxSdkVersion='%d'", maxSdkVersion);
501 }
502 printf("\n");
503
504 if (optional) {
505 printf("optional-permission: name='%s'",
506 ResTable::normalizeForOutput(name.string()).string());
507 if (maxSdkVersion != -1) {
508 printf(" maxSdkVersion='%d'", maxSdkVersion);
509 }
510 printf("\n");
511 }
512}
513
514static void printUsesImpliedPermission(const String8& name, const String8& reason) {
515 printf("uses-implied-permission: name='%s' reason='%s'\n",
516 ResTable::normalizeForOutput(name.string()).string(),
517 ResTable::normalizeForOutput(reason.string()).string());
518}
519
Adam Lesinski94fc9122013-09-30 17:16:09 -0700520Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool offHost,
521 String8 *outError = NULL)
522{
523 Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER);
524 if (aidAsset == NULL) {
525 if (outError != NULL) *outError = "xml resource does not exist";
526 return Vector<String8>();
527 }
528
529 const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service");
530
531 bool withinApduService = false;
532 Vector<String8> categories;
533
534 String8 error;
535 ResXMLTree tree;
536 tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength());
537
538 size_t len;
539 int depth = 0;
540 ResXMLTree::event_code_t code;
541 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
542 if (code == ResXMLTree::END_TAG) {
543 depth--;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700544 const char16_t* ctag16 = tree.getElementName(&len);
545 if (ctag16 == NULL) {
546 *outError = "failed to get XML element name (bad string pool)";
547 return Vector<String8>();
548 }
549 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700550
551 if (depth == 0 && tag == serviceTagName) {
552 withinApduService = false;
553 }
554
555 } else if (code == ResXMLTree::START_TAG) {
556 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700557 const char16_t* ctag16 = tree.getElementName(&len);
558 if (ctag16 == NULL) {
559 *outError = "failed to get XML element name (bad string pool)";
560 return Vector<String8>();
561 }
562 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700563
564 if (depth == 1) {
565 if (tag == serviceTagName) {
566 withinApduService = true;
567 }
568 } else if (depth == 2 && withinApduService) {
569 if (tag == "aid-group") {
570 String8 category = getAttribute(tree, CATEGORY_ATTR, &error);
571 if (error != "") {
572 if (outError != NULL) *outError = error;
573 return Vector<String8>();
574 }
575
576 categories.add(category);
577 }
578 }
579 }
580 }
581 aidAsset->close();
582 return categories;
583}
584
Adam Lesinski282e1812014-01-23 18:17:42 -0800585/*
586 * Handle the "dump" command, to extract select data from an archive.
587 */
588extern char CONSOLE_DATA[2925]; // see EOF
589int doDump(Bundle* bundle)
590{
591 status_t result = UNKNOWN_ERROR;
592 Asset* asset = NULL;
593
594 if (bundle->getFileSpecCount() < 1) {
595 fprintf(stderr, "ERROR: no dump option specified\n");
596 return 1;
597 }
598
599 if (bundle->getFileSpecCount() < 2) {
600 fprintf(stderr, "ERROR: no dump file specified\n");
601 return 1;
602 }
603
604 const char* option = bundle->getFileSpecEntry(0);
605 const char* filename = bundle->getFileSpecEntry(1);
606
607 AssetManager assets;
Narayan Kamathf85e41f2014-01-24 14:14:30 +0000608 int32_t assetsCookie;
Adam Lesinski282e1812014-01-23 18:17:42 -0800609 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
610 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
611 return 1;
612 }
613
614 // Make a dummy config for retrieving resources... we need to supply
615 // non-default values for some configs so that we can retrieve resources
616 // in the app that don't have a default. The most important of these is
617 // the API version because key resources like icons will have an implicit
618 // version if they are using newer config types like density.
619 ResTable_config config;
Narayan Kamath91447d82014-01-21 15:32:36 +0000620 memset(&config, 0, sizeof(ResTable_config));
Adam Lesinski282e1812014-01-23 18:17:42 -0800621 config.language[0] = 'e';
622 config.language[1] = 'n';
623 config.country[0] = 'U';
624 config.country[1] = 'S';
625 config.orientation = ResTable_config::ORIENTATION_PORT;
626 config.density = ResTable_config::DENSITY_MEDIUM;
627 config.sdkVersion = 10000; // Very high.
628 config.screenWidthDp = 320;
629 config.screenHeightDp = 480;
630 config.smallestScreenWidthDp = 320;
631 assets.setConfiguration(config);
632
633 const ResTable& res = assets.getResources(false);
634 if (&res == NULL) {
635 fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
636 goto bail;
Adam Lesinski25e9d552014-05-19 15:01:43 -0700637 } else if (res.getError() != NO_ERROR) {
638 fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
639 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -0800640 }
641
642 if (strcmp("resources", option) == 0) {
643#ifndef HAVE_ANDROID_OS
644 res.print(bundle->getValues());
645#endif
646
647 } else if (strcmp("strings", option) == 0) {
648 const ResStringPool* pool = res.getTableStringBlock(0);
649 printStringPool(pool);
650
651 } else if (strcmp("xmltree", option) == 0) {
652 if (bundle->getFileSpecCount() < 3) {
653 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
654 goto bail;
655 }
656
657 for (int i=2; i<bundle->getFileSpecCount(); i++) {
658 const char* resname = bundle->getFileSpecEntry(i);
659 ResXMLTree tree;
660 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
661 if (asset == NULL) {
662 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
663 goto bail;
664 }
665
666 if (tree.setTo(asset->getBuffer(true),
667 asset->getLength()) != NO_ERROR) {
668 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
669 goto bail;
670 }
671 tree.restart();
672 printXMLBlock(&tree);
673 tree.uninit();
674 delete asset;
675 asset = NULL;
676 }
677
678 } else if (strcmp("xmlstrings", option) == 0) {
679 if (bundle->getFileSpecCount() < 3) {
680 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
681 goto bail;
682 }
683
684 for (int i=2; i<bundle->getFileSpecCount(); i++) {
685 const char* resname = bundle->getFileSpecEntry(i);
686 ResXMLTree tree;
687 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
688 if (asset == NULL) {
689 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
690 goto bail;
691 }
692
693 if (tree.setTo(asset->getBuffer(true),
694 asset->getLength()) != NO_ERROR) {
695 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
696 goto bail;
697 }
698 printStringPool(&tree.getStrings());
699 delete asset;
700 asset = NULL;
701 }
702
703 } else {
704 ResXMLTree tree;
705 asset = assets.openNonAsset("AndroidManifest.xml",
706 Asset::ACCESS_BUFFER);
707 if (asset == NULL) {
708 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
709 goto bail;
710 }
711
712 if (tree.setTo(asset->getBuffer(true),
713 asset->getLength()) != NO_ERROR) {
714 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
715 goto bail;
716 }
717 tree.restart();
718
719 if (strcmp("permissions", option) == 0) {
720 size_t len;
721 ResXMLTree::event_code_t code;
722 int depth = 0;
723 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
724 if (code == ResXMLTree::END_TAG) {
725 depth--;
726 continue;
727 }
728 if (code != ResXMLTree::START_TAG) {
729 continue;
730 }
731 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700732 const char16_t* ctag16 = tree.getElementName(&len);
733 if (ctag16 == NULL) {
734 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
735 goto bail;
736 }
737 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800738 //printf("Depth %d tag %s\n", depth, tag.string());
739 if (depth == 1) {
740 if (tag != "manifest") {
741 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
742 goto bail;
743 }
744 String8 pkg = getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -0700745 printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800746 } else if (depth == 2 && tag == "permission") {
747 String8 error;
748 String8 name = getAttribute(tree, NAME_ATTR, &error);
749 if (error != "") {
750 fprintf(stderr, "ERROR: %s\n", error.string());
751 goto bail;
752 }
Maurice Chu2675f762013-10-22 17:33:11 -0700753 printf("permission: %s\n",
754 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800755 } else if (depth == 2 && tag == "uses-permission") {
756 String8 error;
757 String8 name = getAttribute(tree, NAME_ATTR, &error);
758 if (error != "") {
759 fprintf(stderr, "ERROR: %s\n", error.string());
760 goto bail;
761 }
Adam Lesinski58f1f362013-11-12 12:59:08 -0800762 printUsesPermission(name,
763 getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1) == 0,
764 getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1));
Adam Lesinski282e1812014-01-23 18:17:42 -0800765 }
766 }
767 } else if (strcmp("badging", option) == 0) {
768 Vector<String8> locales;
769 res.getLocales(&locales);
770
771 Vector<ResTable_config> configs;
772 res.getConfigurations(&configs);
773 SortedVector<int> densities;
774 const size_t NC = configs.size();
775 for (size_t i=0; i<NC; i++) {
776 int dens = configs[i].density;
777 if (dens == 0) {
778 dens = 160;
779 }
780 densities.add(dens);
781 }
782
783 size_t len;
784 ResXMLTree::event_code_t code;
785 int depth = 0;
786 String8 error;
787 bool withinActivity = false;
788 bool isMainActivity = false;
789 bool isLauncherActivity = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800790 bool isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800791 bool isSearchable = false;
792 bool withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700793 bool withinSupportsInput = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800794 bool withinReceiver = false;
795 bool withinService = false;
796 bool withinIntentFilter = false;
797 bool hasMainActivity = false;
798 bool hasOtherActivities = false;
799 bool hasOtherReceivers = false;
800 bool hasOtherServices = false;
801 bool hasWallpaperService = false;
802 bool hasImeService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700803 bool hasAccessibilityService = false;
804 bool hasPrintService = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800805 bool hasWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700806 bool hasDeviceAdminReceiver = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800807 bool hasIntentFilter = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700808 bool hasPaymentService = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800809 bool actMainActivity = false;
810 bool actWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700811 bool actDeviceAdminEnabled = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800812 bool actImeService = false;
813 bool actWallpaperService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700814 bool actAccessibilityService = false;
815 bool actPrintService = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700816 bool actHostApduService = false;
817 bool actOffHostApduService = false;
818 bool hasMetaHostPaymentCategory = false;
819 bool hasMetaOffHostPaymentCategory = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700820
821 // These permissions are required by services implementing services
822 // the system binds to (IME, Accessibility, PrintServices, etc.)
823 bool hasBindDeviceAdminPermission = false;
824 bool hasBindInputMethodPermission = false;
825 bool hasBindAccessibilityServicePermission = false;
826 bool hasBindPrintServicePermission = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700827 bool hasBindNfcServicePermission = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800828
829 // These two implement the implicit permissions that are granted
830 // to pre-1.6 applications.
831 bool hasWriteExternalStoragePermission = false;
832 bool hasReadPhoneStatePermission = false;
833
834 // If an app requests write storage, they will also get read storage.
835 bool hasReadExternalStoragePermission = false;
836
837 // Implement transition to read and write call log.
838 bool hasReadContactsPermission = false;
839 bool hasWriteContactsPermission = false;
840 bool hasReadCallLogPermission = false;
841 bool hasWriteCallLogPermission = false;
842
843 // This next group of variables is used to implement a group of
844 // backward-compatibility heuristics necessitated by the addition of
845 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
846 // heuristic is "if an app requests a permission but doesn't explicitly
847 // request the corresponding <uses-feature>, presume it's there anyway".
848 bool specCameraFeature = false; // camera-related
849 bool specCameraAutofocusFeature = false;
850 bool reqCameraAutofocusFeature = false;
851 bool reqCameraFlashFeature = false;
852 bool hasCameraPermission = false;
853 bool specLocationFeature = false; // location-related
854 bool specNetworkLocFeature = false;
855 bool reqNetworkLocFeature = false;
856 bool specGpsFeature = false;
857 bool reqGpsFeature = false;
858 bool hasMockLocPermission = false;
859 bool hasCoarseLocPermission = false;
860 bool hasGpsPermission = false;
861 bool hasGeneralLocPermission = false;
862 bool specBluetoothFeature = false; // Bluetooth API-related
863 bool hasBluetoothPermission = false;
864 bool specMicrophoneFeature = false; // microphone-related
865 bool hasRecordAudioPermission = false;
866 bool specWiFiFeature = false;
867 bool hasWiFiPermission = false;
868 bool specTelephonyFeature = false; // telephony-related
869 bool reqTelephonySubFeature = false;
870 bool hasTelephonyPermission = false;
871 bool specTouchscreenFeature = false; // touchscreen-related
872 bool specMultitouchFeature = false;
873 bool reqDistinctMultitouchFeature = false;
874 bool specScreenPortraitFeature = false;
875 bool specScreenLandscapeFeature = false;
876 bool reqScreenPortraitFeature = false;
877 bool reqScreenLandscapeFeature = false;
878 // 2.2 also added some other features that apps can request, but that
879 // have no corresponding permission, so we cannot implement any
880 // back-compatibility heuristic for them. The below are thus unnecessary
881 // (but are retained here for documentary purposes.)
882 //bool specCompassFeature = false;
883 //bool specAccelerometerFeature = false;
884 //bool specProximityFeature = false;
885 //bool specAmbientLightFeature = false;
886 //bool specLiveWallpaperFeature = false;
887
888 int targetSdk = 0;
889 int smallScreen = 1;
890 int normalScreen = 1;
891 int largeScreen = 1;
892 int xlargeScreen = 1;
893 int anyDensity = 1;
894 int requiresSmallestWidthDp = 0;
895 int compatibleWidthLimitDp = 0;
896 int largestWidthLimitDp = 0;
897 String8 pkg;
898 String8 activityName;
899 String8 activityLabel;
900 String8 activityIcon;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800901 String8 activityBanner;
Adam Lesinski282e1812014-01-23 18:17:42 -0800902 String8 receiverName;
903 String8 serviceName;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700904 Vector<String8> supportedInput;
Adam Lesinski282e1812014-01-23 18:17:42 -0800905 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
906 if (code == ResXMLTree::END_TAG) {
907 depth--;
908 if (depth < 2) {
Michael Wrightec4fdec2013-09-06 16:50:52 -0700909 if (withinSupportsInput && !supportedInput.isEmpty()) {
910 printf("supports-input: '");
911 const size_t N = supportedInput.size();
912 for (size_t i=0; i<N; i++) {
Maurice Chu2675f762013-10-22 17:33:11 -0700913 printf("%s", ResTable::normalizeForOutput(
914 supportedInput[i].string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -0700915 if (i != N - 1) {
916 printf("' '");
917 } else {
918 printf("'\n");
919 }
920 }
921 supportedInput.clear();
922 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800923 withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700924 withinSupportsInput = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800925 } else if (depth < 3) {
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800926 if (withinActivity && isMainActivity) {
Maurice Chu2675f762013-10-22 17:33:11 -0700927 String8 aName(getComponentName(pkg, activityName));
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800928 if (isLauncherActivity) {
929 printf("launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800930 if (aName.length() > 0) {
931 printf(" name='%s' ",
932 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800933 }
934 printf(" label='%s' icon='%s'\n",
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800935 ResTable::normalizeForOutput(activityLabel.string()).string(),
936 ResTable::normalizeForOutput(activityIcon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800937 }
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800938 if (isLeanbackLauncherActivity) {
939 printf("leanback-launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800940 if (aName.length() > 0) {
941 printf(" name='%s' ",
942 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800943 }
944 printf(" label='%s' icon='%s' banner='%s'\n",
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800945 ResTable::normalizeForOutput(activityLabel.string()).string(),
946 ResTable::normalizeForOutput(activityIcon.string()).string(),
947 ResTable::normalizeForOutput(activityBanner.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800948 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800949 }
950 if (!hasIntentFilter) {
951 hasOtherActivities |= withinActivity;
952 hasOtherReceivers |= withinReceiver;
953 hasOtherServices |= withinService;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700954 } else {
955 if (withinService) {
956 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
957 hasBindNfcServicePermission);
958 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
959 hasBindNfcServicePermission);
960 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800961 }
962 withinActivity = false;
963 withinService = false;
964 withinReceiver = false;
965 hasIntentFilter = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800966 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800967 } else if (depth < 4) {
968 if (withinIntentFilter) {
969 if (withinActivity) {
970 hasMainActivity |= actMainActivity;
971 hasOtherActivities |= !actMainActivity;
972 } else if (withinReceiver) {
973 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700974 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
975 hasBindDeviceAdminPermission);
976 hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled);
Adam Lesinski282e1812014-01-23 18:17:42 -0800977 } else if (withinService) {
978 hasImeService |= actImeService;
979 hasWallpaperService |= actWallpaperService;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700980 hasAccessibilityService |= (actAccessibilityService &&
981 hasBindAccessibilityServicePermission);
982 hasPrintService |= (actPrintService && hasBindPrintServicePermission);
983 hasOtherServices |= (!actImeService && !actWallpaperService &&
Adam Lesinski94fc9122013-09-30 17:16:09 -0700984 !actAccessibilityService && !actPrintService &&
985 !actHostApduService && !actOffHostApduService);
Adam Lesinski282e1812014-01-23 18:17:42 -0800986 }
987 }
988 withinIntentFilter = false;
989 }
990 continue;
991 }
992 if (code != ResXMLTree::START_TAG) {
993 continue;
994 }
995 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700996
997 const char16_t* ctag16 = tree.getElementName(&len);
998 if (ctag16 == NULL) {
999 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
1000 goto bail;
1001 }
1002 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -08001003 //printf("Depth %d, %s\n", depth, tag.string());
1004 if (depth == 1) {
1005 if (tag != "manifest") {
1006 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
1007 goto bail;
1008 }
1009 pkg = getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -07001010 printf("package: name='%s' ",
1011 ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001012 int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
1013 if (error != "") {
1014 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
1015 goto bail;
1016 }
1017 if (versionCode > 0) {
1018 printf("versionCode='%d' ", versionCode);
1019 } else {
1020 printf("versionCode='' ");
1021 }
1022 String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
1023 if (error != "") {
1024 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
1025 goto bail;
1026 }
Maurice Chu2675f762013-10-22 17:33:11 -07001027 printf("versionName='%s'\n",
1028 ResTable::normalizeForOutput(versionName.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001029 } else if (depth == 2) {
1030 withinApplication = false;
1031 if (tag == "application") {
1032 withinApplication = true;
1033
1034 String8 label;
1035 const size_t NL = locales.size();
1036 for (size_t i=0; i<NL; i++) {
1037 const char* localeStr = locales[i].string();
1038 assets.setLocale(localeStr != NULL ? localeStr : "");
1039 String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1040 if (llabel != "") {
1041 if (localeStr == NULL || strlen(localeStr) == 0) {
1042 label = llabel;
Maurice Chu2675f762013-10-22 17:33:11 -07001043 printf("application-label:'%s'\n",
1044 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001045 } else {
1046 if (label == "") {
1047 label = llabel;
1048 }
1049 printf("application-label-%s:'%s'\n", localeStr,
Maurice Chu2675f762013-10-22 17:33:11 -07001050 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001051 }
1052 }
1053 }
1054
1055 ResTable_config tmpConfig = config;
1056 const size_t ND = densities.size();
1057 for (size_t i=0; i<ND; i++) {
1058 tmpConfig.density = densities[i];
1059 assets.setConfiguration(tmpConfig);
1060 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1061 if (icon != "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001062 printf("application-icon-%d:'%s'\n", densities[i],
1063 ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001064 }
1065 }
1066 assets.setConfiguration(config);
1067
1068 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1069 if (error != "") {
1070 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
1071 goto bail;
1072 }
1073 int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
1074 if (error != "") {
1075 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
1076 goto bail;
1077 }
Maurice Chu2675f762013-10-22 17:33:11 -07001078 printf("application: label='%s' ",
1079 ResTable::normalizeForOutput(label.string()).string());
1080 printf("icon='%s'\n", ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001081 if (testOnly != 0) {
1082 printf("testOnly='%d'\n", testOnly);
1083 }
1084
1085 int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0);
1086 if (error != "") {
1087 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string());
1088 goto bail;
1089 }
1090 if (debuggable != 0) {
1091 printf("application-debuggable\n");
1092 }
1093 } else if (tag == "uses-sdk") {
1094 int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
1095 if (error != "") {
1096 error = "";
1097 String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
1098 if (error != "") {
1099 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
1100 error.string());
1101 goto bail;
1102 }
1103 if (name == "Donut") targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001104 printf("sdkVersion:'%s'\n",
1105 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001106 } else if (code != -1) {
1107 targetSdk = code;
1108 printf("sdkVersion:'%d'\n", code);
1109 }
1110 code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
1111 if (code != -1) {
1112 printf("maxSdkVersion:'%d'\n", code);
1113 }
1114 code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
1115 if (error != "") {
1116 error = "";
1117 String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
1118 if (error != "") {
1119 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
1120 error.string());
1121 goto bail;
1122 }
1123 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001124 printf("targetSdkVersion:'%s'\n",
1125 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001126 } else if (code != -1) {
1127 if (targetSdk < code) {
1128 targetSdk = code;
1129 }
1130 printf("targetSdkVersion:'%d'\n", code);
1131 }
1132 } else if (tag == "uses-configuration") {
1133 int32_t reqTouchScreen = getIntegerAttribute(tree,
1134 REQ_TOUCH_SCREEN_ATTR, NULL, 0);
1135 int32_t reqKeyboardType = getIntegerAttribute(tree,
1136 REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
1137 int32_t reqHardKeyboard = getIntegerAttribute(tree,
1138 REQ_HARD_KEYBOARD_ATTR, NULL, 0);
1139 int32_t reqNavigation = getIntegerAttribute(tree,
1140 REQ_NAVIGATION_ATTR, NULL, 0);
1141 int32_t reqFiveWayNav = getIntegerAttribute(tree,
1142 REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
1143 printf("uses-configuration:");
1144 if (reqTouchScreen != 0) {
1145 printf(" reqTouchScreen='%d'", reqTouchScreen);
1146 }
1147 if (reqKeyboardType != 0) {
1148 printf(" reqKeyboardType='%d'", reqKeyboardType);
1149 }
1150 if (reqHardKeyboard != 0) {
1151 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1152 }
1153 if (reqNavigation != 0) {
1154 printf(" reqNavigation='%d'", reqNavigation);
1155 }
1156 if (reqFiveWayNav != 0) {
1157 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1158 }
1159 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001160 } else if (tag == "supports-input") {
1161 withinSupportsInput = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001162 } else if (tag == "supports-screens") {
1163 smallScreen = getIntegerAttribute(tree,
1164 SMALL_SCREEN_ATTR, NULL, 1);
1165 normalScreen = getIntegerAttribute(tree,
1166 NORMAL_SCREEN_ATTR, NULL, 1);
1167 largeScreen = getIntegerAttribute(tree,
1168 LARGE_SCREEN_ATTR, NULL, 1);
1169 xlargeScreen = getIntegerAttribute(tree,
1170 XLARGE_SCREEN_ATTR, NULL, 1);
1171 anyDensity = getIntegerAttribute(tree,
1172 ANY_DENSITY_ATTR, NULL, 1);
1173 requiresSmallestWidthDp = getIntegerAttribute(tree,
1174 REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
1175 compatibleWidthLimitDp = getIntegerAttribute(tree,
1176 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
1177 largestWidthLimitDp = getIntegerAttribute(tree,
1178 LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
1179 } else if (tag == "uses-feature") {
1180 String8 name = getAttribute(tree, NAME_ATTR, &error);
1181
1182 if (name != "" && error == "") {
1183 int req = getIntegerAttribute(tree,
1184 REQUIRED_ATTR, NULL, 1);
1185
1186 if (name == "android.hardware.camera") {
1187 specCameraFeature = true;
1188 } else if (name == "android.hardware.camera.autofocus") {
1189 // these have no corresponding permission to check for,
1190 // but should imply the foundational camera permission
1191 reqCameraAutofocusFeature = reqCameraAutofocusFeature || req;
1192 specCameraAutofocusFeature = true;
1193 } else if (req && (name == "android.hardware.camera.flash")) {
1194 // these have no corresponding permission to check for,
1195 // but should imply the foundational camera permission
1196 reqCameraFlashFeature = true;
1197 } else if (name == "android.hardware.location") {
1198 specLocationFeature = true;
1199 } else if (name == "android.hardware.location.network") {
1200 specNetworkLocFeature = true;
1201 reqNetworkLocFeature = reqNetworkLocFeature || req;
1202 } else if (name == "android.hardware.location.gps") {
1203 specGpsFeature = true;
1204 reqGpsFeature = reqGpsFeature || req;
1205 } else if (name == "android.hardware.bluetooth") {
1206 specBluetoothFeature = true;
1207 } else if (name == "android.hardware.touchscreen") {
1208 specTouchscreenFeature = true;
1209 } else if (name == "android.hardware.touchscreen.multitouch") {
1210 specMultitouchFeature = true;
1211 } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
1212 reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req;
1213 } else if (name == "android.hardware.microphone") {
1214 specMicrophoneFeature = true;
1215 } else if (name == "android.hardware.wifi") {
1216 specWiFiFeature = true;
1217 } else if (name == "android.hardware.telephony") {
1218 specTelephonyFeature = true;
1219 } else if (req && (name == "android.hardware.telephony.gsm" ||
1220 name == "android.hardware.telephony.cdma")) {
1221 // these have no corresponding permission to check for,
1222 // but should imply the foundational telephony permission
1223 reqTelephonySubFeature = true;
1224 } else if (name == "android.hardware.screen.portrait") {
1225 specScreenPortraitFeature = true;
1226 } else if (name == "android.hardware.screen.landscape") {
1227 specScreenLandscapeFeature = true;
1228 }
1229 printf("uses-feature%s:'%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001230 req ? "" : "-not-required",
1231 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001232 } else {
1233 int vers = getIntegerAttribute(tree,
1234 GL_ES_VERSION_ATTR, &error);
1235 if (error == "") {
1236 printf("uses-gl-es:'0x%x'\n", vers);
1237 }
1238 }
1239 } else if (tag == "uses-permission") {
1240 String8 name = getAttribute(tree, NAME_ATTR, &error);
1241 if (name != "" && error == "") {
1242 if (name == "android.permission.CAMERA") {
1243 hasCameraPermission = true;
1244 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
1245 hasGpsPermission = true;
1246 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
1247 hasMockLocPermission = true;
1248 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
1249 hasCoarseLocPermission = true;
1250 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
1251 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
1252 hasGeneralLocPermission = true;
1253 } else if (name == "android.permission.BLUETOOTH" ||
1254 name == "android.permission.BLUETOOTH_ADMIN") {
1255 hasBluetoothPermission = true;
1256 } else if (name == "android.permission.RECORD_AUDIO") {
1257 hasRecordAudioPermission = true;
1258 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1259 name == "android.permission.CHANGE_WIFI_STATE" ||
1260 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
1261 hasWiFiPermission = true;
1262 } else if (name == "android.permission.CALL_PHONE" ||
1263 name == "android.permission.CALL_PRIVILEGED" ||
1264 name == "android.permission.MODIFY_PHONE_STATE" ||
1265 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1266 name == "android.permission.READ_SMS" ||
1267 name == "android.permission.RECEIVE_SMS" ||
1268 name == "android.permission.RECEIVE_MMS" ||
1269 name == "android.permission.RECEIVE_WAP_PUSH" ||
1270 name == "android.permission.SEND_SMS" ||
1271 name == "android.permission.WRITE_APN_SETTINGS" ||
1272 name == "android.permission.WRITE_SMS") {
1273 hasTelephonyPermission = true;
1274 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1275 hasWriteExternalStoragePermission = true;
1276 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1277 hasReadExternalStoragePermission = true;
1278 } else if (name == "android.permission.READ_PHONE_STATE") {
1279 hasReadPhoneStatePermission = true;
1280 } else if (name == "android.permission.READ_CONTACTS") {
1281 hasReadContactsPermission = true;
1282 } else if (name == "android.permission.WRITE_CONTACTS") {
1283 hasWriteContactsPermission = true;
1284 } else if (name == "android.permission.READ_CALL_LOG") {
1285 hasReadCallLogPermission = true;
1286 } else if (name == "android.permission.WRITE_CALL_LOG") {
1287 hasWriteCallLogPermission = true;
1288 }
Adam Lesinski58f1f362013-11-12 12:59:08 -08001289
1290 printUsesPermission(name,
1291 getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1) == 0,
1292 getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1));
1293 } else {
Adam Lesinski282e1812014-01-23 18:17:42 -08001294 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1295 error.string());
1296 goto bail;
1297 }
1298 } else if (tag == "uses-package") {
1299 String8 name = getAttribute(tree, NAME_ATTR, &error);
1300 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001301 printf("uses-package:'%s'\n",
1302 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001303 } else {
1304 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1305 error.string());
1306 goto bail;
1307 }
1308 } else if (tag == "original-package") {
1309 String8 name = getAttribute(tree, NAME_ATTR, &error);
1310 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001311 printf("original-package:'%s'\n",
1312 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001313 } else {
1314 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1315 error.string());
1316 goto bail;
1317 }
1318 } else if (tag == "supports-gl-texture") {
1319 String8 name = getAttribute(tree, NAME_ATTR, &error);
1320 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001321 printf("supports-gl-texture:'%s'\n",
1322 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001323 } else {
1324 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1325 error.string());
1326 goto bail;
1327 }
1328 } else if (tag == "compatible-screens") {
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001329 printCompatibleScreens(tree, &error);
1330 if (error != "") {
1331 fprintf(stderr, "ERROR getting compatible screens: %s\n",
1332 error.string());
1333 goto bail;
1334 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001335 depth--;
1336 } else if (tag == "package-verifier") {
1337 String8 name = getAttribute(tree, NAME_ATTR, &error);
1338 if (name != "" && error == "") {
1339 String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
1340 if (publicKey != "" && error == "") {
1341 printf("package-verifier: name='%s' publicKey='%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001342 ResTable::normalizeForOutput(name.string()).string(),
1343 ResTable::normalizeForOutput(publicKey.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001344 }
1345 }
1346 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001347 } else if (depth == 3) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001348 withinActivity = false;
1349 withinReceiver = false;
1350 withinService = false;
1351 hasIntentFilter = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001352 hasMetaHostPaymentCategory = false;
1353 hasMetaOffHostPaymentCategory = false;
1354 hasBindDeviceAdminPermission = false;
1355 hasBindInputMethodPermission = false;
1356 hasBindAccessibilityServicePermission = false;
1357 hasBindPrintServicePermission = false;
1358 hasBindNfcServicePermission = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001359 if (withinApplication) {
1360 if(tag == "activity") {
1361 withinActivity = true;
1362 activityName = getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001363 if (error != "") {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001364 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1365 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001366 goto bail;
1367 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001368
Michael Wrightec4fdec2013-09-06 16:50:52 -07001369 activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1370 if (error != "") {
1371 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
1372 error.string());
1373 goto bail;
1374 }
1375
1376 activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1377 if (error != "") {
1378 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1379 error.string());
1380 goto bail;
1381 }
1382
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001383 activityBanner = getResolvedAttribute(&res, tree, BANNER_ATTR, &error);
1384 if (error != "") {
1385 fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
1386 error.string());
1387 goto bail;
1388 }
1389
Michael Wrightec4fdec2013-09-06 16:50:52 -07001390 int32_t orien = getResolvedIntegerAttribute(&res, tree,
1391 SCREEN_ORIENTATION_ATTR, &error);
1392 if (error == "") {
1393 if (orien == 0 || orien == 6 || orien == 8) {
1394 // Requests landscape, sensorLandscape, or reverseLandscape.
1395 reqScreenLandscapeFeature = true;
1396 } else if (orien == 1 || orien == 7 || orien == 9) {
1397 // Requests portrait, sensorPortrait, or reversePortrait.
1398 reqScreenPortraitFeature = true;
1399 }
1400 }
1401 } else if (tag == "uses-library") {
1402 String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1403 if (error != "") {
1404 fprintf(stderr,
1405 "ERROR getting 'android:name' attribute for uses-library"
1406 " %s\n", error.string());
1407 goto bail;
1408 }
1409 int req = getIntegerAttribute(tree,
1410 REQUIRED_ATTR, NULL, 1);
1411 printf("uses-library%s:'%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001412 req ? "" : "-not-required", ResTable::normalizeForOutput(
1413 libraryName.string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001414 } else if (tag == "receiver") {
1415 withinReceiver = true;
1416 receiverName = getAttribute(tree, NAME_ATTR, &error);
1417
1418 if (error != "") {
1419 fprintf(stderr,
1420 "ERROR getting 'android:name' attribute for receiver:"
1421 " %s\n", error.string());
1422 goto bail;
1423 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001424
1425 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1426 if (error == "") {
1427 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1428 hasBindDeviceAdminPermission = true;
1429 }
1430 } else {
1431 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1432 " receiver '%s': %s\n", receiverName.string(), error.string());
1433 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001434 } else if (tag == "service") {
1435 withinService = true;
1436 serviceName = getAttribute(tree, NAME_ATTR, &error);
1437
1438 if (error != "") {
1439 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1440 "service:%s\n", error.string());
1441 goto bail;
1442 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001443
1444 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1445 if (error == "") {
1446 if (permission == "android.permission.BIND_INPUT_METHOD") {
1447 hasBindInputMethodPermission = true;
1448 } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") {
1449 hasBindAccessibilityServicePermission = true;
1450 } else if (permission == "android.permission.BIND_PRINT_SERVICE") {
1451 hasBindPrintServicePermission = true;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001452 } else if (permission == "android.permission.BIND_NFC_SERVICE") {
1453 hasBindNfcServicePermission = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001454 }
1455 } else {
1456 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1457 " service '%s': %s\n", serviceName.string(), error.string());
1458 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001459 } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
Adam Lesinskib71adb62014-05-15 14:14:41 -07001460 String8 metaDataName = getResolvedAttribute(&res, tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001461 if (error != "") {
1462 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1463 "meta-data:%s\n", error.string());
1464 goto bail;
1465 }
Maurice Chu2675f762013-10-22 17:33:11 -07001466 printf("meta-data: name='%s' ",
1467 ResTable::normalizeForOutput(metaDataName.string()).string());
Maurice Chu76327312013-10-16 18:28:46 -07001468 printResolvedResourceAttribute(&res, tree, VALUE_ATTR, String8("value"),
1469 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001470 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001471 // Try looking for a RESOURCE_ATTR
1472 error = "";
1473 printResolvedResourceAttribute(&res, tree, RESOURCE_ATTR,
1474 String8("resource"), &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001475 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001476 fprintf(stderr, "ERROR getting 'android:value' or "
1477 "'android:resource' attribute for "
1478 "meta-data:%s\n", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001479 goto bail;
1480 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001481 }
Maurice Chu76327312013-10-16 18:28:46 -07001482 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001483 } else if (withinSupportsInput && tag == "input-type") {
1484 String8 name = getAttribute(tree, NAME_ATTR, &error);
1485 if (name != "" && error == "") {
1486 supportedInput.add(name);
1487 } else {
1488 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1489 error.string());
1490 goto bail;
1491 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001492 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001493 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001494 } else if (depth == 4) {
1495 if (tag == "intent-filter") {
1496 hasIntentFilter = true;
1497 withinIntentFilter = true;
1498 actMainActivity = false;
1499 actWidgetReceivers = false;
1500 actImeService = false;
1501 actWallpaperService = false;
1502 actAccessibilityService = false;
1503 actPrintService = false;
1504 actDeviceAdminEnabled = false;
1505 actHostApduService = false;
1506 actOffHostApduService = false;
1507 } else if (withinService && tag == "meta-data") {
1508 String8 name = getAttribute(tree, NAME_ATTR, &error);
1509 if (error != "") {
1510 fprintf(stderr, "ERROR getting 'android:name' attribute for"
1511 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1512 goto bail;
1513 }
1514
1515 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1516 name == "android.nfc.cardemulation.off_host_apdu_service") {
1517 bool offHost = true;
1518 if (name == "android.nfc.cardemulation.host_apdu_service") {
1519 offHost = false;
1520 }
1521
1522 String8 xmlPath = getResolvedAttribute(&res, tree, RESOURCE_ATTR, &error);
1523 if (error != "") {
1524 fprintf(stderr, "ERROR getting 'android:resource' attribute for"
1525 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1526 goto bail;
1527 }
1528
1529 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1530 offHost, &error);
1531 if (error != "") {
1532 fprintf(stderr, "ERROR getting AID category for service '%s'\n",
1533 serviceName.string());
1534 goto bail;
1535 }
1536
1537 const size_t catLen = categories.size();
1538 for (size_t i = 0; i < catLen; i++) {
1539 bool paymentCategory = (categories[i] == "payment");
1540 if (offHost) {
1541 hasMetaOffHostPaymentCategory |= paymentCategory;
1542 } else {
1543 hasMetaHostPaymentCategory |= paymentCategory;
1544 }
1545 }
1546 }
1547 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001548 } else if ((depth == 5) && withinIntentFilter) {
1549 String8 action;
1550 if (tag == "action") {
1551 action = getAttribute(tree, NAME_ATTR, &error);
1552 if (error != "") {
1553 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1554 error.string());
1555 goto bail;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001556 }
1557
Adam Lesinskia5018c92013-09-30 16:23:15 -07001558 if (withinActivity) {
1559 if (action == "android.intent.action.MAIN") {
1560 isMainActivity = true;
1561 actMainActivity = true;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001562 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001563 } else if (withinReceiver) {
1564 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1565 actWidgetReceivers = true;
1566 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1567 actDeviceAdminEnabled = true;
1568 }
1569 } else if (withinService) {
1570 if (action == "android.view.InputMethod") {
1571 actImeService = true;
1572 } else if (action == "android.service.wallpaper.WallpaperService") {
1573 actWallpaperService = true;
1574 } else if (action == "android.accessibilityservice.AccessibilityService") {
1575 actAccessibilityService = true;
1576 } else if (action == "android.printservice.PrintService") {
1577 actPrintService = true;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001578 } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
1579 actHostApduService = true;
1580 } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
1581 actOffHostApduService = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001582 }
1583 }
1584 if (action == "android.intent.action.SEARCH") {
1585 isSearchable = true;
1586 }
1587 }
1588
1589 if (tag == "category") {
1590 String8 category = getAttribute(tree, NAME_ATTR, &error);
1591 if (error != "") {
1592 fprintf(stderr, "ERROR getting 'name' attribute: %s\n",
1593 error.string());
1594 goto bail;
1595 }
1596 if (withinActivity) {
1597 if (category == "android.intent.category.LAUNCHER") {
1598 isLauncherActivity = true;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001599 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
1600 isLeanbackLauncherActivity = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001601 }
1602 }
1603 }
1604 }
1605 }
1606
1607 // Pre-1.6 implicitly granted permission compatibility logic
1608 if (targetSdk < 4) {
1609 if (!hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001610 printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
1611 printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
1612 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001613 hasWriteExternalStoragePermission = true;
1614 }
1615 if (!hasReadPhoneStatePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001616 printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
1617 printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
1618 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001619 }
1620 }
1621
1622 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1623 // force them to always take READ_EXTERNAL_STORAGE as well. We always
1624 // do this (regardless of target API version) because we can't have
1625 // an app with write permission but not read permission.
1626 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001627 printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"));
1628 printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
1629 String8("requested WRITE_EXTERNAL_STORAGE"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001630 }
1631
1632 // Pre-JellyBean call log permission compatibility.
1633 if (targetSdk < 16) {
1634 if (!hasReadCallLogPermission && hasReadContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001635 printUsesPermission(String8("android.permission.READ_CALL_LOG"));
1636 printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
1637 String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001638 }
1639 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001640 printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
1641 printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
1642 String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001643 }
1644 }
1645
1646 /* The following blocks handle printing "inferred" uses-features, based
1647 * on whether related features or permissions are used by the app.
1648 * Note that the various spec*Feature variables denote whether the
1649 * relevant tag was *present* in the AndroidManfest, not that it was
1650 * present and set to true.
1651 */
1652 // Camera-related back-compatibility logic
1653 if (!specCameraFeature) {
1654 if (reqCameraFlashFeature) {
1655 // if app requested a sub-feature (autofocus or flash) and didn't
1656 // request the base camera feature, we infer that it meant to
1657 printf("uses-feature:'android.hardware.camera'\n");
1658 printf("uses-implied-feature:'android.hardware.camera'," \
1659 "'requested android.hardware.camera.flash feature'\n");
1660 } else if (reqCameraAutofocusFeature) {
1661 // if app requested a sub-feature (autofocus or flash) and didn't
1662 // request the base camera feature, we infer that it meant to
1663 printf("uses-feature:'android.hardware.camera'\n");
1664 printf("uses-implied-feature:'android.hardware.camera'," \
1665 "'requested android.hardware.camera.autofocus feature'\n");
1666 } else if (hasCameraPermission) {
Maurice Chu2675f762013-10-22 17:33:11 -07001667 // if app wants to use camera but didn't request the feature, we infer
Adam Lesinski282e1812014-01-23 18:17:42 -08001668 // that it meant to, and further that it wants autofocus
1669 // (which was the 1.0 - 1.5 behavior)
1670 printf("uses-feature:'android.hardware.camera'\n");
1671 if (!specCameraAutofocusFeature) {
1672 printf("uses-feature:'android.hardware.camera.autofocus'\n");
1673 printf("uses-implied-feature:'android.hardware.camera.autofocus'," \
1674 "'requested android.permission.CAMERA permission'\n");
1675 }
1676 }
1677 }
1678
1679 // Location-related back-compatibility logic
1680 if (!specLocationFeature &&
1681 (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission ||
1682 hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) {
1683 // if app either takes a location-related permission or requests one of the
1684 // sub-features, we infer that it also meant to request the base location feature
1685 printf("uses-feature:'android.hardware.location'\n");
1686 printf("uses-implied-feature:'android.hardware.location'," \
1687 "'requested a location access permission'\n");
1688 }
1689 if (!specGpsFeature && hasGpsPermission) {
1690 // if app takes GPS (FINE location) perm but does not request the GPS
1691 // feature, we infer that it meant to
1692 printf("uses-feature:'android.hardware.location.gps'\n");
1693 printf("uses-implied-feature:'android.hardware.location.gps'," \
1694 "'requested android.permission.ACCESS_FINE_LOCATION permission'\n");
1695 }
1696 if (!specNetworkLocFeature && hasCoarseLocPermission) {
1697 // if app takes Network location (COARSE location) perm but does not request the
1698 // network location feature, we infer that it meant to
1699 printf("uses-feature:'android.hardware.location.network'\n");
1700 printf("uses-implied-feature:'android.hardware.location.network'," \
1701 "'requested android.permission.ACCESS_COARSE_LOCATION permission'\n");
1702 }
1703
1704 // Bluetooth-related compatibility logic
1705 if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
1706 // if app takes a Bluetooth permission but does not request the Bluetooth
1707 // feature, we infer that it meant to
1708 printf("uses-feature:'android.hardware.bluetooth'\n");
1709 printf("uses-implied-feature:'android.hardware.bluetooth'," \
1710 "'requested android.permission.BLUETOOTH or android.permission.BLUETOOTH_ADMIN " \
1711 "permission and targetSdkVersion > 4'\n");
1712 }
1713
1714 // Microphone-related compatibility logic
1715 if (!specMicrophoneFeature && hasRecordAudioPermission) {
1716 // if app takes the record-audio permission but does not request the microphone
1717 // feature, we infer that it meant to
1718 printf("uses-feature:'android.hardware.microphone'\n");
1719 printf("uses-implied-feature:'android.hardware.microphone'," \
1720 "'requested android.permission.RECORD_AUDIO permission'\n");
1721 }
1722
1723 // WiFi-related compatibility logic
1724 if (!specWiFiFeature && hasWiFiPermission) {
1725 // if app takes one of the WiFi permissions but does not request the WiFi
1726 // feature, we infer that it meant to
1727 printf("uses-feature:'android.hardware.wifi'\n");
1728 printf("uses-implied-feature:'android.hardware.wifi'," \
1729 "'requested android.permission.ACCESS_WIFI_STATE, " \
1730 "android.permission.CHANGE_WIFI_STATE, or " \
1731 "android.permission.CHANGE_WIFI_MULTICAST_STATE permission'\n");
1732 }
1733
1734 // Telephony-related compatibility logic
1735 if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) {
1736 // if app takes one of the telephony permissions or requests a sub-feature but
1737 // does not request the base telephony feature, we infer that it meant to
1738 printf("uses-feature:'android.hardware.telephony'\n");
1739 printf("uses-implied-feature:'android.hardware.telephony'," \
1740 "'requested a telephony-related permission or feature'\n");
1741 }
1742
1743 // Touchscreen-related back-compatibility logic
1744 if (!specTouchscreenFeature) { // not a typo!
1745 // all apps are presumed to require a touchscreen, unless they explicitly say
1746 // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
1747 // Note that specTouchscreenFeature is true if the tag is present, regardless
1748 // of whether its value is true or false, so this is safe
1749 printf("uses-feature:'android.hardware.touchscreen'\n");
1750 printf("uses-implied-feature:'android.hardware.touchscreen'," \
1751 "'assumed you require a touch screen unless explicitly made optional'\n");
1752 }
1753 if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
1754 // if app takes one of the telephony permissions or requests a sub-feature but
1755 // does not request the base telephony feature, we infer that it meant to
1756 printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
1757 printf("uses-implied-feature:'android.hardware.touchscreen.multitouch'," \
1758 "'requested android.hardware.touchscreen.multitouch.distinct feature'\n");
1759 }
1760
1761 // Landscape/portrait-related compatibility logic
1762 if (!specScreenLandscapeFeature && !specScreenPortraitFeature) {
1763 // If the app has specified any activities in its manifest
1764 // that request a specific orientation, then assume that
1765 // orientation is required.
1766 if (reqScreenLandscapeFeature) {
1767 printf("uses-feature:'android.hardware.screen.landscape'\n");
1768 printf("uses-implied-feature:'android.hardware.screen.landscape'," \
1769 "'one or more activities have specified a landscape orientation'\n");
1770 }
1771 if (reqScreenPortraitFeature) {
1772 printf("uses-feature:'android.hardware.screen.portrait'\n");
1773 printf("uses-implied-feature:'android.hardware.screen.portrait'," \
1774 "'one or more activities have specified a portrait orientation'\n");
1775 }
1776 }
1777
1778 if (hasMainActivity) {
1779 printf("main\n");
1780 }
1781 if (hasWidgetReceivers) {
1782 printf("app-widget\n");
1783 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001784 if (hasDeviceAdminReceiver) {
1785 printf("device-admin\n");
1786 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001787 if (hasImeService) {
1788 printf("ime\n");
1789 }
1790 if (hasWallpaperService) {
1791 printf("wallpaper\n");
1792 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001793 if (hasAccessibilityService) {
1794 printf("accessibility\n");
1795 }
1796 if (hasPrintService) {
1797 printf("print\n");
1798 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001799 if (hasPaymentService) {
1800 printf("payment\n");
1801 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001802 if (hasOtherActivities) {
1803 printf("other-activities\n");
1804 }
1805 if (isSearchable) {
1806 printf("search\n");
1807 }
1808 if (hasOtherReceivers) {
1809 printf("other-receivers\n");
1810 }
1811 if (hasOtherServices) {
1812 printf("other-services\n");
1813 }
1814
1815 // For modern apps, if screen size buckets haven't been specified
1816 // but the new width ranges have, then infer the buckets from them.
1817 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1818 && requiresSmallestWidthDp > 0) {
1819 int compatWidth = compatibleWidthLimitDp;
1820 if (compatWidth <= 0) {
1821 compatWidth = requiresSmallestWidthDp;
1822 }
1823 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1824 smallScreen = -1;
1825 } else {
1826 smallScreen = 0;
1827 }
1828 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1829 normalScreen = -1;
1830 } else {
1831 normalScreen = 0;
1832 }
1833 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1834 largeScreen = -1;
1835 } else {
1836 largeScreen = 0;
1837 }
1838 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1839 xlargeScreen = -1;
1840 } else {
1841 xlargeScreen = 0;
1842 }
1843 }
1844
1845 // Determine default values for any unspecified screen sizes,
1846 // based on the target SDK of the package. As of 4 (donut)
1847 // the screen size support was introduced, so all default to
1848 // enabled.
1849 if (smallScreen > 0) {
1850 smallScreen = targetSdk >= 4 ? -1 : 0;
1851 }
1852 if (normalScreen > 0) {
1853 normalScreen = -1;
1854 }
1855 if (largeScreen > 0) {
1856 largeScreen = targetSdk >= 4 ? -1 : 0;
1857 }
1858 if (xlargeScreen > 0) {
1859 // Introduced in Gingerbread.
1860 xlargeScreen = targetSdk >= 9 ? -1 : 0;
1861 }
1862 if (anyDensity > 0) {
1863 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1864 || compatibleWidthLimitDp > 0) ? -1 : 0;
1865 }
1866 printf("supports-screens:");
1867 if (smallScreen != 0) {
1868 printf(" 'small'");
1869 }
1870 if (normalScreen != 0) {
1871 printf(" 'normal'");
1872 }
1873 if (largeScreen != 0) {
1874 printf(" 'large'");
1875 }
1876 if (xlargeScreen != 0) {
1877 printf(" 'xlarge'");
1878 }
1879 printf("\n");
1880 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
1881 if (requiresSmallestWidthDp > 0) {
1882 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1883 }
1884 if (compatibleWidthLimitDp > 0) {
1885 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1886 }
1887 if (largestWidthLimitDp > 0) {
1888 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1889 }
1890
1891 printf("locales:");
1892 const size_t NL = locales.size();
1893 for (size_t i=0; i<NL; i++) {
1894 const char* localeStr = locales[i].string();
1895 if (localeStr == NULL || strlen(localeStr) == 0) {
1896 localeStr = "--_--";
1897 }
1898 printf(" '%s'", localeStr);
1899 }
1900 printf("\n");
1901
1902 printf("densities:");
1903 const size_t ND = densities.size();
1904 for (size_t i=0; i<ND; i++) {
1905 printf(" '%d'", densities[i]);
1906 }
1907 printf("\n");
1908
1909 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1910 if (dir != NULL) {
1911 if (dir->getFileCount() > 0) {
1912 printf("native-code:");
1913 for (size_t i=0; i<dir->getFileCount(); i++) {
Maurice Chu2675f762013-10-22 17:33:11 -07001914 printf(" '%s'", ResTable::normalizeForOutput(
1915 dir->getFileName(i).string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001916 }
1917 printf("\n");
1918 }
1919 delete dir;
1920 }
1921 } else if (strcmp("badger", option) == 0) {
1922 printf("%s", CONSOLE_DATA);
1923 } else if (strcmp("configurations", option) == 0) {
1924 Vector<ResTable_config> configs;
1925 res.getConfigurations(&configs);
1926 const size_t N = configs.size();
1927 for (size_t i=0; i<N; i++) {
1928 printf("%s\n", configs[i].toString().string());
1929 }
1930 } else {
1931 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
1932 goto bail;
1933 }
1934 }
1935
1936 result = NO_ERROR;
1937
1938bail:
1939 if (asset) {
1940 delete asset;
1941 }
1942 return (result != NO_ERROR);
1943}
1944
1945
1946/*
1947 * Handle the "add" command, which wants to add files to a new or
1948 * pre-existing archive.
1949 */
1950int doAdd(Bundle* bundle)
1951{
1952 ZipFile* zip = NULL;
1953 status_t result = UNKNOWN_ERROR;
1954 const char* zipFileName;
1955
1956 if (bundle->getUpdate()) {
1957 /* avoid confusion */
1958 fprintf(stderr, "ERROR: can't use '-u' with add\n");
1959 goto bail;
1960 }
1961
1962 if (bundle->getFileSpecCount() < 1) {
1963 fprintf(stderr, "ERROR: must specify zip file name\n");
1964 goto bail;
1965 }
1966 zipFileName = bundle->getFileSpecEntry(0);
1967
1968 if (bundle->getFileSpecCount() < 2) {
1969 fprintf(stderr, "NOTE: nothing to do\n");
1970 goto bail;
1971 }
1972
1973 zip = openReadWrite(zipFileName, true);
1974 if (zip == NULL) {
1975 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
1976 goto bail;
1977 }
1978
1979 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1980 const char* fileName = bundle->getFileSpecEntry(i);
1981
1982 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
1983 printf(" '%s'... (from gzip)\n", fileName);
1984 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
1985 } else {
1986 if (bundle->getJunkPath()) {
1987 String8 storageName = String8(fileName).getPathLeaf();
Maurice Chu2675f762013-10-22 17:33:11 -07001988 printf(" '%s' as '%s'...\n", fileName,
1989 ResTable::normalizeForOutput(storageName.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001990 result = zip->add(fileName, storageName.string(),
1991 bundle->getCompressionMethod(), NULL);
1992 } else {
1993 printf(" '%s'...\n", fileName);
1994 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
1995 }
1996 }
1997 if (result != NO_ERROR) {
1998 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
1999 if (result == NAME_NOT_FOUND) {
2000 fprintf(stderr, ": file not found\n");
2001 } else if (result == ALREADY_EXISTS) {
2002 fprintf(stderr, ": already exists in archive\n");
2003 } else {
2004 fprintf(stderr, "\n");
2005 }
2006 goto bail;
2007 }
2008 }
2009
2010 result = NO_ERROR;
2011
2012bail:
2013 delete zip;
2014 return (result != NO_ERROR);
2015}
2016
2017
2018/*
2019 * Delete files from an existing archive.
2020 */
2021int doRemove(Bundle* bundle)
2022{
2023 ZipFile* zip = NULL;
2024 status_t result = UNKNOWN_ERROR;
2025 const char* zipFileName;
2026
2027 if (bundle->getFileSpecCount() < 1) {
2028 fprintf(stderr, "ERROR: must specify zip file name\n");
2029 goto bail;
2030 }
2031 zipFileName = bundle->getFileSpecEntry(0);
2032
2033 if (bundle->getFileSpecCount() < 2) {
2034 fprintf(stderr, "NOTE: nothing to do\n");
2035 goto bail;
2036 }
2037
2038 zip = openReadWrite(zipFileName, false);
2039 if (zip == NULL) {
2040 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2041 zipFileName);
2042 goto bail;
2043 }
2044
2045 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2046 const char* fileName = bundle->getFileSpecEntry(i);
2047 ZipEntry* entry;
2048
2049 entry = zip->getEntryByName(fileName);
2050 if (entry == NULL) {
2051 printf(" '%s' NOT FOUND\n", fileName);
2052 continue;
2053 }
2054
2055 result = zip->remove(entry);
2056
2057 if (result != NO_ERROR) {
2058 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2059 bundle->getFileSpecEntry(i), zipFileName);
2060 goto bail;
2061 }
2062 }
2063
2064 /* update the archive */
2065 zip->flush();
2066
2067bail:
2068 delete zip;
2069 return (result != NO_ERROR);
2070}
2071
Adam Lesinski3921e872014-05-13 10:56:25 -07002072static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002073 const size_t numDirs = dir->getDirs().size();
2074 for (size_t i = 0; i < numDirs; i++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002075 bool ignore = ignoreConfig;
2076 const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2077 const char* dirStr = subDir->getLeaf().string();
2078 if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2079 ignore = true;
2080 }
2081 status_t err = addResourcesToBuilder(subDir, builder, ignore);
Adam Lesinskifab50872014-04-16 14:40:42 -07002082 if (err != NO_ERROR) {
2083 return err;
2084 }
2085 }
2086
2087 const size_t numFiles = dir->getFiles().size();
2088 for (size_t i = 0; i < numFiles; i++) {
2089 sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2090 const size_t numConfigs = gp->getFiles().size();
2091 for (size_t j = 0; j < numConfigs; j++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002092 status_t err = NO_ERROR;
2093 if (ignoreConfig) {
2094 err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2095 } else {
2096 err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2097 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002098 if (err != NO_ERROR) {
2099 fprintf(stderr, "Failed to add %s (%s) to builder.\n",
2100 gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
2101 return err;
2102 }
2103 }
2104 }
2105 return NO_ERROR;
2106}
2107
2108static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2109 if (split->isBase()) {
2110 return original;
2111 }
2112
2113 String8 ext(original.getPathExtension());
2114 if (ext == String8(".apk")) {
2115 return String8::format("%s_%s%s",
2116 original.getBasePath().string(),
2117 split->getDirectorySafeName().string(),
2118 ext.string());
2119 }
2120
2121 return String8::format("%s_%s", original.string(),
2122 split->getDirectorySafeName().string());
2123}
Adam Lesinski282e1812014-01-23 18:17:42 -08002124
2125/*
2126 * Package up an asset directory and associated application files.
2127 */
2128int doPackage(Bundle* bundle)
2129{
2130 const char* outputAPKFile;
2131 int retVal = 1;
2132 status_t err;
2133 sp<AaptAssets> assets;
2134 int N;
2135 FILE* fp;
2136 String8 dependencyFile;
Adam Lesinskifab50872014-04-16 14:40:42 -07002137 sp<ApkBuilder> builder;
Adam Lesinski282e1812014-01-23 18:17:42 -08002138
Anton Krumina2ef5c02014-03-12 14:46:44 -07002139 // -c en_XA or/and ar_XB means do pseudolocalization
Adam Lesinskifab50872014-04-16 14:40:42 -07002140 sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2141 err = configFilter->parse(bundle->getConfigurations());
Adam Lesinski282e1812014-01-23 18:17:42 -08002142 if (err != NO_ERROR) {
2143 goto bail;
2144 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002145 if (configFilter->containsPseudo()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002146 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2147 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002148 if (configFilter->containsPseudoBidi()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002149 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
Adam Lesinski282e1812014-01-23 18:17:42 -08002150 }
2151
2152 N = bundle->getFileSpecCount();
2153 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
Adam Lesinski09384302014-01-22 16:07:42 -08002154 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002155 fprintf(stderr, "ERROR: no input files\n");
2156 goto bail;
2157 }
2158
2159 outputAPKFile = bundle->getOutputAPKFile();
2160
2161 // Make sure the filenames provided exist and are of the appropriate type.
2162 if (outputAPKFile) {
2163 FileType type;
2164 type = getFileType(outputAPKFile);
2165 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2166 fprintf(stderr,
2167 "ERROR: output file '%s' exists but is not regular file\n",
2168 outputAPKFile);
2169 goto bail;
2170 }
2171 }
2172
2173 // Load the assets.
2174 assets = new AaptAssets();
2175
2176 // Set up the resource gathering in assets if we're going to generate
2177 // dependency files. Every time we encounter a resource while slurping
2178 // the tree, we'll add it to these stores so we have full resource paths
2179 // to write to a dependency file.
2180 if (bundle->getGenDependencies()) {
2181 sp<FilePathStore> resPathStore = new FilePathStore;
2182 assets->setFullResPaths(resPathStore);
2183 sp<FilePathStore> assetPathStore = new FilePathStore;
2184 assets->setFullAssetPaths(assetPathStore);
2185 }
2186
2187 err = assets->slurpFromArgs(bundle);
2188 if (err < 0) {
2189 goto bail;
2190 }
2191
2192 if (bundle->getVerbose()) {
2193 assets->print(String8());
2194 }
2195
Adam Lesinskifab50872014-04-16 14:40:42 -07002196 // Create the ApkBuilder, which will collect the compiled files
2197 // to write to the final APK (or sets of APKs if we are building
2198 // a Split APK.
2199 builder = new ApkBuilder(configFilter);
2200
2201 // If we are generating a Split APK, find out which configurations to split on.
2202 if (bundle->getSplitConfigurations().size() > 0) {
2203 const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2204 const size_t numSplits = splitStrs.size();
2205 for (size_t i = 0; i < numSplits; i++) {
2206 std::set<ConfigDescription> configs;
2207 if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2208 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2209 goto bail;
2210 }
2211
2212 err = builder->createSplitForConfigs(configs);
2213 if (err != NO_ERROR) {
2214 goto bail;
2215 }
2216 }
2217 }
2218
Adam Lesinski282e1812014-01-23 18:17:42 -08002219 // If they asked for any fileAs that need to be compiled, do so.
2220 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002221 err = buildResources(bundle, assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002222 if (err != 0) {
2223 goto bail;
2224 }
2225 }
2226
2227 // At this point we've read everything and processed everything. From here
2228 // on out it's just writing output files.
2229 if (SourcePos::hasErrors()) {
2230 goto bail;
2231 }
2232
2233 // Update symbols with information about which ones are needed as Java symbols.
2234 assets->applyJavaSymbols();
2235 if (SourcePos::hasErrors()) {
2236 goto bail;
2237 }
2238
2239 // If we've been asked to generate a dependency file, do that here
2240 if (bundle->getGenDependencies()) {
2241 // If this is the packaging step, generate the dependency file next to
2242 // the output apk (e.g. bin/resources.ap_.d)
2243 if (outputAPKFile) {
2244 dependencyFile = String8(outputAPKFile);
2245 // Add the .d extension to the dependency file.
2246 dependencyFile.append(".d");
2247 } else {
2248 // Else if this is the R.java dependency generation step,
2249 // generate the dependency file in the R.java package subdirectory
2250 // e.g. gen/com/foo/app/R.java.d
2251 dependencyFile = String8(bundle->getRClassDir());
2252 dependencyFile.appendPath("R.java.d");
2253 }
2254 // Make sure we have a clean dependency file to start with
2255 fp = fopen(dependencyFile, "w");
2256 fclose(fp);
2257 }
2258
2259 // Write out R.java constants
2260 if (!assets->havePrivateSymbols()) {
2261 if (bundle->getCustomPackage() == NULL) {
2262 // Write the R.java file into the appropriate class directory
2263 // e.g. gen/com/foo/app/R.java
2264 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
2265 } else {
2266 const String8 customPkg(bundle->getCustomPackage());
2267 err = writeResourceSymbols(bundle, assets, customPkg, true);
2268 }
2269 if (err < 0) {
2270 goto bail;
2271 }
2272 // If we have library files, we're going to write our R.java file into
2273 // the appropriate class directory for those libraries as well.
2274 // e.g. gen/com/foo/app/lib/R.java
2275 if (bundle->getExtraPackages() != NULL) {
2276 // Split on colon
2277 String8 libs(bundle->getExtraPackages());
2278 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2279 while (packageString != NULL) {
2280 // Write the R.java file out with the correct package name
2281 err = writeResourceSymbols(bundle, assets, String8(packageString), true);
2282 if (err < 0) {
2283 goto bail;
2284 }
2285 packageString = strtok(NULL, ":");
2286 }
2287 libs.unlockBuffer();
2288 }
2289 } else {
2290 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
2291 if (err < 0) {
2292 goto bail;
2293 }
2294 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
2295 if (err < 0) {
2296 goto bail;
2297 }
2298 }
2299
2300 // Write out the ProGuard file
2301 err = writeProguardFile(bundle, assets);
2302 if (err < 0) {
2303 goto bail;
2304 }
2305
2306 // Write the apk
2307 if (outputAPKFile) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002308 // Gather all resources and add them to the APK Builder. The builder will then
2309 // figure out which Split they belong in.
2310 err = addResourcesToBuilder(assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002311 if (err != NO_ERROR) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002312 goto bail;
2313 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002314
2315 const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2316 const size_t numSplits = splits.size();
2317 for (size_t i = 0; i < numSplits; i++) {
2318 const sp<ApkSplit>& split = splits[i];
2319 String8 outputPath = buildApkName(String8(outputAPKFile), split);
2320 err = writeAPK(bundle, outputPath, split);
2321 if (err != NO_ERROR) {
2322 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2323 goto bail;
2324 }
2325 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002326 }
2327
2328 // If we've been asked to generate a dependency file, we need to finish up here.
2329 // the writeResourceSymbols and writeAPK functions have already written the target
2330 // half of the dependency file, now we need to write the prerequisites. (files that
2331 // the R.java file or .ap_ file depend on)
2332 if (bundle->getGenDependencies()) {
2333 // Now that writeResourceSymbols or writeAPK has taken care of writing
2334 // the targets to our dependency file, we'll write the prereqs
2335 fp = fopen(dependencyFile, "a+");
2336 fprintf(fp, " : ");
2337 bool includeRaw = (outputAPKFile != NULL);
2338 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2339 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2340 // and therefore was not added to our pathstores during slurping
2341 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2342 fclose(fp);
2343 }
2344
2345 retVal = 0;
2346bail:
2347 if (SourcePos::hasErrors()) {
2348 SourcePos::printErrors(stderr);
2349 }
2350 return retVal;
2351}
2352
2353/*
2354 * Do PNG Crunching
2355 * PRECONDITIONS
2356 * -S flag points to a source directory containing drawable* folders
2357 * -C flag points to destination directory. The folder structure in the
2358 * source directory will be mirrored to the destination (cache) directory
2359 *
2360 * POSTCONDITIONS
2361 * Destination directory will be updated to match the PNG files in
Maurice Chu2675f762013-10-22 17:33:11 -07002362 * the source directory.
Adam Lesinski282e1812014-01-23 18:17:42 -08002363 */
2364int doCrunch(Bundle* bundle)
2365{
2366 fprintf(stdout, "Crunching PNG Files in ");
2367 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2368 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2369
2370 updatePreProcessedCache(bundle);
2371
2372 return NO_ERROR;
2373}
2374
2375/*
2376 * Do PNG Crunching on a single flag
2377 * -i points to a single png file
2378 * -o points to a single png output file
2379 */
2380int doSingleCrunch(Bundle* bundle)
2381{
2382 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2383 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2384
2385 String8 input(bundle->getSingleCrunchInputFile());
2386 String8 output(bundle->getSingleCrunchOutputFile());
2387
2388 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2389 // we can't return the status_t as it gets truncate to the lower 8 bits.
2390 return 42;
2391 }
2392
2393 return NO_ERROR;
2394}
2395
2396char CONSOLE_DATA[2925] = {
2397 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2398 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2399 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2400 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2401 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2402 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2403 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2404 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2405 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2406 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2407 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2408 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2409 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2410 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2411 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2412 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2413 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2414 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2415 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2416 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2417 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2418 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2419 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2420 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2421 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2422 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2423 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2424 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2425 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2426 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2427 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2428 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2429 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2430 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2431 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2432 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2433 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2434 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2435 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2436 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2437 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2438 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2439 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2440 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2441 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2442 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2443 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2444 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2445 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2446 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2447 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2448 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2449 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2450 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2451 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2452 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2453 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2454 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2455 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2456 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2457 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2458 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2459 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2460 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2461 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2462 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2463 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2464 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2465 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2466 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2467 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2468 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2469 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2470 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2471 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2472 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2473 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2474 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2475 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2476 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2477 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2478 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2479 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2480 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2481 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2482 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2483 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2484 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2485 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2486 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2487 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2488 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2489 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2490 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2491 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2492 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2493 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2494 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2495 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2496 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2497 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2498 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2499 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2500 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2501 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2502 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2503 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2504 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2505 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2506 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2507 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2508 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2509 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2510 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2511 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2512 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2513 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2514 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2515 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2516 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2517 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2518 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2519 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2520 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2521 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2522 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2523 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2524 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2525 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2526 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2527 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2528 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2529 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2530 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2531 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2532 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2533 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2534 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2535 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2536 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2537 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2538 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2539 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2540 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2541 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2542 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2543 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2544 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2545 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2546 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2547 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2548 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2549 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2550 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2551 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2552 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2553 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2554 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2555 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2556 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2557 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2558 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2559 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2560 };