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