blob: bdbf47b2611caa065ba9a535505daca4952af0ea [file] [log] [blame]
Adam Lesinski282e1812014-01-23 18:17:42 -08001//
2// Copyright 2006 The Android Open Source Project
3//
4// Android Asset Packaging Tool main entry point.
5//
Adam Lesinskifab50872014-04-16 14:40:42 -07006#include "ApkBuilder.h"
Adam Lesinski282e1812014-01-23 18:17:42 -08007#include "Bundle.h"
Adam Lesinski2c72b682014-06-24 09:56:01 -07008#include "Images.h"
9#include "Main.h"
Adam Lesinski282e1812014-01-23 18:17:42 -080010#include "ResourceFilter.h"
11#include "ResourceTable.h"
Adam Lesinski282e1812014-01-23 18:17:42 -080012#include "XMLNode.h"
13
Adam Lesinski282e1812014-01-23 18:17:42 -080014#include <utils/Errors.h>
Adam Lesinski2c72b682014-06-24 09:56:01 -070015#include <utils/KeyedVector.h>
16#include <utils/List.h>
17#include <utils/Log.h>
18#include <utils/SortedVector.h>
19#include <utils/threads.h>
20#include <utils/Vector.h>
Adam Lesinski282e1812014-01-23 18:17:42 -080021
Adam Lesinski282e1812014-01-23 18:17:42 -080022#include <errno.h>
Adam Lesinski2c72b682014-06-24 09:56:01 -070023#include <fcntl.h>
Adam Lesinski282e1812014-01-23 18:17:42 -080024
25using namespace android;
26
Adam Lesinskiad751222014-08-18 14:06:38 -070027#ifndef AAPT_VERSION
28 #define AAPT_VERSION ""
29#endif
30
Adam Lesinski282e1812014-01-23 18:17:42 -080031/*
32 * Show version info. All the cool kids do it.
33 */
34int doVersion(Bundle* bundle)
35{
36 if (bundle->getFileSpecCount() != 0) {
37 printf("(ignoring extra arguments)\n");
38 }
Adam Lesinskiad751222014-08-18 14:06:38 -070039 printf("Android Asset Packaging Tool, v0.2-" AAPT_VERSION "\n");
Adam Lesinski282e1812014-01-23 18:17:42 -080040
41 return 0;
42}
43
44
45/*
46 * Open the file read only. The call fails if the file doesn't exist.
47 *
48 * Returns NULL on failure.
49 */
50ZipFile* openReadOnly(const char* fileName)
51{
52 ZipFile* zip;
53 status_t result;
54
55 zip = new ZipFile;
56 result = zip->open(fileName, ZipFile::kOpenReadOnly);
57 if (result != NO_ERROR) {
58 if (result == NAME_NOT_FOUND) {
59 fprintf(stderr, "ERROR: '%s' not found\n", fileName);
60 } else if (result == PERMISSION_DENIED) {
61 fprintf(stderr, "ERROR: '%s' access denied\n", fileName);
62 } else {
63 fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n",
64 fileName);
65 }
66 delete zip;
67 return NULL;
68 }
69
70 return zip;
71}
72
73/*
74 * Open the file read-write. The file will be created if it doesn't
75 * already exist and "okayToCreate" is set.
76 *
77 * Returns NULL on failure.
78 */
79ZipFile* openReadWrite(const char* fileName, bool okayToCreate)
80{
81 ZipFile* zip = NULL;
82 status_t result;
83 int flags;
84
85 flags = ZipFile::kOpenReadWrite;
86 if (okayToCreate) {
87 flags |= ZipFile::kOpenCreate;
88 }
89
90 zip = new ZipFile;
91 result = zip->open(fileName, flags);
92 if (result != NO_ERROR) {
93 delete zip;
94 zip = NULL;
95 goto bail;
96 }
97
98bail:
99 return zip;
100}
101
102
103/*
104 * Return a short string describing the compression method.
105 */
106const char* compressionName(int method)
107{
108 if (method == ZipEntry::kCompressStored) {
109 return "Stored";
110 } else if (method == ZipEntry::kCompressDeflated) {
111 return "Deflated";
112 } else {
113 return "Unknown";
114 }
115}
116
117/*
118 * Return the percent reduction in size (0% == no compression).
119 */
120int calcPercent(long uncompressedLen, long compressedLen)
121{
122 if (!uncompressedLen) {
123 return 0;
124 } else {
125 return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5);
126 }
127}
128
129/*
130 * Handle the "list" command, which can be a simple file dump or
131 * a verbose listing.
132 *
133 * The verbose listing closely matches the output of the Info-ZIP "unzip"
134 * command.
135 */
136int doList(Bundle* bundle)
137{
138 int result = 1;
139 ZipFile* zip = NULL;
140 const ZipEntry* entry;
141 long totalUncLen, totalCompLen;
142 const char* zipFileName;
143
144 if (bundle->getFileSpecCount() != 1) {
145 fprintf(stderr, "ERROR: specify zip file name (only)\n");
146 goto bail;
147 }
148 zipFileName = bundle->getFileSpecEntry(0);
149
150 zip = openReadOnly(zipFileName);
151 if (zip == NULL) {
152 goto bail;
153 }
154
155 int count, i;
156
157 if (bundle->getVerbose()) {
158 printf("Archive: %s\n", zipFileName);
159 printf(
160 " Length Method Size Ratio Offset Date Time CRC-32 Name\n");
161 printf(
162 "-------- ------ ------- ----- ------- ---- ---- ------ ----\n");
163 }
164
165 totalUncLen = totalCompLen = 0;
166
167 count = zip->getNumEntries();
168 for (i = 0; i < count; i++) {
169 entry = zip->getEntryByIndex(i);
170 if (bundle->getVerbose()) {
171 char dateBuf[32];
172 time_t when;
173
174 when = entry->getModWhen();
175 strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M",
176 localtime(&when));
177
178 printf("%8ld %-7.7s %7ld %3d%% %8zd %s %08lx %s\n",
179 (long) entry->getUncompressedLen(),
180 compressionName(entry->getCompressionMethod()),
181 (long) entry->getCompressedLen(),
182 calcPercent(entry->getUncompressedLen(),
183 entry->getCompressedLen()),
184 (size_t) entry->getLFHOffset(),
185 dateBuf,
186 entry->getCRC32(),
187 entry->getFileName());
188 } else {
189 printf("%s\n", entry->getFileName());
190 }
191
192 totalUncLen += entry->getUncompressedLen();
193 totalCompLen += entry->getCompressedLen();
194 }
195
196 if (bundle->getVerbose()) {
197 printf(
198 "-------- ------- --- -------\n");
199 printf("%8ld %7ld %2d%% %d files\n",
200 totalUncLen,
201 totalCompLen,
202 calcPercent(totalUncLen, totalCompLen),
203 zip->getNumEntries());
204 }
205
206 if (bundle->getAndroidList()) {
207 AssetManager assets;
208 if (!assets.addAssetPath(String8(zipFileName), NULL)) {
209 fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n");
210 goto bail;
211 }
212
213 const ResTable& res = assets.getResources(false);
214 if (&res == NULL) {
215 printf("\nNo resource table found.\n");
216 } else {
217#ifndef HAVE_ANDROID_OS
218 printf("\nResource table:\n");
219 res.print(false);
220#endif
221 }
222
223 Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
224 Asset::ACCESS_BUFFER);
225 if (manifestAsset == NULL) {
226 printf("\nNo AndroidManifest.xml found.\n");
227 } else {
228 printf("\nAndroid manifest:\n");
229 ResXMLTree tree;
230 tree.setTo(manifestAsset->getBuffer(true),
231 manifestAsset->getLength());
232 printXMLBlock(&tree);
233 }
234 delete manifestAsset;
235 }
236
237 result = 0;
238
239bail:
240 delete zip;
241 return result;
242}
243
244static ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes)
245{
246 size_t N = tree.getAttributeCount();
247 for (size_t i=0; i<N; i++) {
248 if (tree.getAttributeNameResID(i) == attrRes) {
249 return (ssize_t)i;
250 }
251 }
252 return -1;
253}
254
255String8 getAttribute(const ResXMLTree& tree, const char* ns,
256 const char* attr, String8* outError)
257{
258 ssize_t idx = tree.indexOfAttribute(ns, attr);
259 if (idx < 0) {
260 return String8();
261 }
262 Res_value value;
263 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
264 if (value.dataType != Res_value::TYPE_STRING) {
265 if (outError != NULL) {
266 *outError = "attribute is not a string value";
267 }
268 return String8();
269 }
270 }
271 size_t len;
272 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
273 return str ? String8(str, len) : String8();
274}
275
276static String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError)
277{
278 ssize_t idx = indexOfAttribute(tree, attrRes);
279 if (idx < 0) {
280 return String8();
281 }
282 Res_value value;
283 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
284 if (value.dataType != Res_value::TYPE_STRING) {
285 if (outError != NULL) {
286 *outError = "attribute is not a string value";
287 }
288 return String8();
289 }
290 }
291 size_t len;
292 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
293 return str ? String8(str, len) : String8();
294}
295
296static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes,
297 String8* outError, int32_t defValue = -1)
298{
299 ssize_t idx = indexOfAttribute(tree, attrRes);
300 if (idx < 0) {
301 return defValue;
302 }
303 Res_value value;
304 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
305 if (value.dataType < Res_value::TYPE_FIRST_INT
306 || value.dataType > Res_value::TYPE_LAST_INT) {
307 if (outError != NULL) {
308 *outError = "attribute is not an integer value";
309 }
310 return defValue;
311 }
312 }
313 return value.data;
314}
315
316static int32_t getResolvedIntegerAttribute(const ResTable* resTable, const ResXMLTree& tree,
317 uint32_t attrRes, String8* outError, int32_t defValue = -1)
318{
319 ssize_t idx = indexOfAttribute(tree, attrRes);
320 if (idx < 0) {
321 return defValue;
322 }
323 Res_value value;
324 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
325 if (value.dataType == Res_value::TYPE_REFERENCE) {
326 resTable->resolveReference(&value, 0);
327 }
328 if (value.dataType < Res_value::TYPE_FIRST_INT
329 || value.dataType > Res_value::TYPE_LAST_INT) {
330 if (outError != NULL) {
331 *outError = "attribute is not an integer value";
332 }
333 return defValue;
334 }
335 }
336 return value.data;
337}
338
339static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree,
340 uint32_t attrRes, String8* outError)
341{
342 ssize_t idx = indexOfAttribute(tree, attrRes);
343 if (idx < 0) {
344 return String8();
345 }
346 Res_value value;
347 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
348 if (value.dataType == Res_value::TYPE_STRING) {
349 size_t len;
350 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
351 return str ? String8(str, len) : String8();
352 }
353 resTable->resolveReference(&value, 0);
354 if (value.dataType != Res_value::TYPE_STRING) {
355 if (outError != NULL) {
356 *outError = "attribute is not a string value";
357 }
358 return String8();
359 }
360 }
361 size_t len;
362 const Res_value* value2 = &value;
363 const char16_t* str = const_cast<ResTable*>(resTable)->valueToString(value2, 0, NULL, &len);
364 return str ? String8(str, len) : String8();
365}
366
367static void getResolvedResourceAttribute(Res_value* value, const ResTable* resTable,
368 const ResXMLTree& tree, uint32_t attrRes, String8* outError)
369{
370 ssize_t idx = indexOfAttribute(tree, attrRes);
371 if (idx < 0) {
372 if (outError != NULL) {
373 *outError = "attribute could not be found";
374 }
375 return;
376 }
377 if (tree.getAttributeValue(idx, value) != NO_ERROR) {
378 if (value->dataType == Res_value::TYPE_REFERENCE) {
379 resTable->resolveReference(value, 0);
380 }
381 // The attribute was found and was resolved if need be.
382 return;
383 }
384 if (outError != NULL) {
385 *outError = "error getting resolved resource attribute";
386 }
387}
388
Maurice Chu76327312013-10-16 18:28:46 -0700389static void printResolvedResourceAttribute(const ResTable* resTable, const ResXMLTree& tree,
390 uint32_t attrRes, String8 attrLabel, String8* outError)
391{
392 Res_value value;
393 getResolvedResourceAttribute(&value, resTable, tree, attrRes, outError);
394 if (*outError != "") {
395 *outError = "error print resolved resource attribute";
396 return;
397 }
398 if (value.dataType == Res_value::TYPE_STRING) {
399 String8 result = getResolvedAttribute(resTable, tree, attrRes, outError);
Maurice Chu2675f762013-10-22 17:33:11 -0700400 printf("%s='%s'", attrLabel.string(),
401 ResTable::normalizeForOutput(result.string()).string());
Maurice Chu76327312013-10-16 18:28:46 -0700402 } else if (Res_value::TYPE_FIRST_INT <= value.dataType &&
403 value.dataType <= Res_value::TYPE_LAST_INT) {
404 printf("%s='%d'", attrLabel.string(), value.data);
405 } else {
406 printf("%s='0x%x'", attrLabel.string(), (int)value.data);
407 }
408}
409
Adam Lesinski282e1812014-01-23 18:17:42 -0800410// These are attribute resource constants for the platform, as found
411// in android.R.attr
412enum {
413 LABEL_ATTR = 0x01010001,
414 ICON_ATTR = 0x01010002,
415 NAME_ATTR = 0x01010003,
Adam Lesinskia5018c92013-09-30 16:23:15 -0700416 PERMISSION_ATTR = 0x01010006,
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700417 EXPORTED_ATTR = 0x01010010,
418 GRANT_URI_PERMISSIONS_ATTR = 0x0101001b,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700419 RESOURCE_ATTR = 0x01010025,
Adam Lesinski282e1812014-01-23 18:17:42 -0800420 DEBUGGABLE_ATTR = 0x0101000f,
421 VALUE_ATTR = 0x01010024,
422 VERSION_CODE_ATTR = 0x0101021b,
423 VERSION_NAME_ATTR = 0x0101021c,
424 SCREEN_ORIENTATION_ATTR = 0x0101001e,
425 MIN_SDK_VERSION_ATTR = 0x0101020c,
426 MAX_SDK_VERSION_ATTR = 0x01010271,
427 REQ_TOUCH_SCREEN_ATTR = 0x01010227,
428 REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
429 REQ_HARD_KEYBOARD_ATTR = 0x01010229,
430 REQ_NAVIGATION_ATTR = 0x0101022a,
431 REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
432 TARGET_SDK_VERSION_ATTR = 0x01010270,
433 TEST_ONLY_ATTR = 0x01010272,
434 ANY_DENSITY_ATTR = 0x0101026c,
435 GL_ES_VERSION_ATTR = 0x01010281,
436 SMALL_SCREEN_ATTR = 0x01010284,
437 NORMAL_SCREEN_ATTR = 0x01010285,
438 LARGE_SCREEN_ATTR = 0x01010286,
439 XLARGE_SCREEN_ATTR = 0x010102bf,
440 REQUIRED_ATTR = 0x0101028e,
441 SCREEN_SIZE_ATTR = 0x010102ca,
442 SCREEN_DENSITY_ATTR = 0x010102cb,
443 REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
444 COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
445 LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
446 PUBLIC_KEY_ATTR = 0x010103a6,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700447 CATEGORY_ATTR = 0x010103e8,
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800448 BANNER_ATTR = 0x10103f2,
Adam Lesinski282e1812014-01-23 18:17:42 -0800449};
450
Maurice Chu2675f762013-10-22 17:33:11 -0700451String8 getComponentName(String8 &pkgName, String8 &componentName) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800452 ssize_t idx = componentName.find(".");
453 String8 retStr(pkgName);
454 if (idx == 0) {
455 retStr += componentName;
456 } else if (idx < 0) {
457 retStr += ".";
458 retStr += componentName;
459 } else {
Maurice Chu2675f762013-10-22 17:33:11 -0700460 return componentName;
Adam Lesinski282e1812014-01-23 18:17:42 -0800461 }
Maurice Chu2675f762013-10-22 17:33:11 -0700462 return retStr;
Adam Lesinski282e1812014-01-23 18:17:42 -0800463}
464
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700465static void printCompatibleScreens(ResXMLTree& tree, String8* outError) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800466 size_t len;
467 ResXMLTree::event_code_t code;
468 int depth = 0;
469 bool first = true;
470 printf("compatible-screens:");
471 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
472 if (code == ResXMLTree::END_TAG) {
473 depth--;
474 if (depth < 0) {
475 break;
476 }
477 continue;
478 }
479 if (code != ResXMLTree::START_TAG) {
480 continue;
481 }
482 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700483 const char16_t* ctag16 = tree.getElementName(&len);
484 if (ctag16 == NULL) {
485 *outError = "failed to get XML element name (bad string pool)";
486 return;
487 }
488 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800489 if (tag == "screen") {
490 int32_t screenSize = getIntegerAttribute(tree,
491 SCREEN_SIZE_ATTR, NULL, -1);
492 int32_t screenDensity = getIntegerAttribute(tree,
493 SCREEN_DENSITY_ATTR, NULL, -1);
494 if (screenSize > 0 && screenDensity > 0) {
495 if (!first) {
496 printf(",");
497 }
498 first = false;
499 printf("'%d/%d'", screenSize, screenDensity);
500 }
501 }
502 }
503 printf("\n");
504}
505
Adam Lesinski58f1f362013-11-12 12:59:08 -0800506static void printUsesPermission(const String8& name, bool optional=false, int maxSdkVersion=-1) {
507 printf("uses-permission: name='%s'", ResTable::normalizeForOutput(name.string()).string());
508 if (maxSdkVersion != -1) {
509 printf(" maxSdkVersion='%d'", maxSdkVersion);
510 }
511 printf("\n");
512
513 if (optional) {
514 printf("optional-permission: name='%s'",
515 ResTable::normalizeForOutput(name.string()).string());
516 if (maxSdkVersion != -1) {
517 printf(" maxSdkVersion='%d'", maxSdkVersion);
518 }
519 printf("\n");
520 }
521}
522
523static void printUsesImpliedPermission(const String8& name, const String8& reason) {
524 printf("uses-implied-permission: name='%s' reason='%s'\n",
525 ResTable::normalizeForOutput(name.string()).string(),
526 ResTable::normalizeForOutput(reason.string()).string());
527}
528
Adam Lesinski94fc9122013-09-30 17:16:09 -0700529Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool offHost,
530 String8 *outError = NULL)
531{
532 Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER);
533 if (aidAsset == NULL) {
534 if (outError != NULL) *outError = "xml resource does not exist";
535 return Vector<String8>();
536 }
537
538 const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service");
539
540 bool withinApduService = false;
541 Vector<String8> categories;
542
543 String8 error;
544 ResXMLTree tree;
545 tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength());
546
547 size_t len;
548 int depth = 0;
549 ResXMLTree::event_code_t code;
550 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
551 if (code == ResXMLTree::END_TAG) {
552 depth--;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700553 const char16_t* ctag16 = tree.getElementName(&len);
554 if (ctag16 == NULL) {
555 *outError = "failed to get XML element name (bad string pool)";
556 return Vector<String8>();
557 }
558 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700559
560 if (depth == 0 && tag == serviceTagName) {
561 withinApduService = false;
562 }
563
564 } else if (code == ResXMLTree::START_TAG) {
565 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700566 const char16_t* ctag16 = tree.getElementName(&len);
567 if (ctag16 == NULL) {
568 *outError = "failed to get XML element name (bad string pool)";
569 return Vector<String8>();
570 }
571 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700572
573 if (depth == 1) {
574 if (tag == serviceTagName) {
575 withinApduService = true;
576 }
577 } else if (depth == 2 && withinApduService) {
578 if (tag == "aid-group") {
579 String8 category = getAttribute(tree, CATEGORY_ATTR, &error);
580 if (error != "") {
581 if (outError != NULL) *outError = error;
582 return Vector<String8>();
583 }
584
585 categories.add(category);
586 }
587 }
588 }
589 }
590 aidAsset->close();
591 return categories;
592}
593
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700594static void printComponentPresence(const char* componentName) {
595 printf("provides-component:'%s'\n", componentName);
596}
597
Adam Lesinski2c72b682014-06-24 09:56:01 -0700598/**
599 * Represents a feature that has been automatically added due to
600 * a pre-requisite or some other reason.
601 */
602struct ImpliedFeature {
603 /**
604 * Name of the implied feature.
605 */
606 String8 name;
607
608 /**
609 * List of human-readable reasons for why this feature was implied.
610 */
611 SortedVector<String8> reasons;
612};
613
614/**
615 * Represents a <feature-group> tag in the AndroidManifest.xml
616 */
617struct FeatureGroup {
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700618 FeatureGroup() : openGLESVersion(-1) {}
619
Adam Lesinski2c72b682014-06-24 09:56:01 -0700620 /**
621 * Human readable label
622 */
623 String8 label;
624
625 /**
626 * Explicit features defined in the group
627 */
628 KeyedVector<String8, bool> features;
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700629
630 /**
631 * OpenGL ES version required
632 */
633 int openGLESVersion;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700634};
635
636static void addImpliedFeature(KeyedVector<String8, ImpliedFeature>* impliedFeatures,
637 const char* name, const char* reason) {
638 String8 name8(name);
639 ssize_t idx = impliedFeatures->indexOfKey(name8);
640 if (idx < 0) {
641 idx = impliedFeatures->add(name8, ImpliedFeature());
642 impliedFeatures->editValueAt(idx).name = name8;
643 }
644 impliedFeatures->editValueAt(idx).reasons.add(String8(reason));
645}
646
647static void printFeatureGroup(const FeatureGroup& grp,
648 const KeyedVector<String8, ImpliedFeature>* impliedFeatures = NULL) {
649 printf("feature-group: label='%s'\n", grp.label.string());
650
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700651 if (grp.openGLESVersion > 0) {
652 printf(" uses-gl-es: '0x%x'\n", grp.openGLESVersion);
653 }
654
Adam Lesinski2c72b682014-06-24 09:56:01 -0700655 const size_t numFeatures = grp.features.size();
656 for (size_t i = 0; i < numFeatures; i++) {
657 if (!grp.features[i]) {
658 continue;
659 }
660
661 const String8& featureName = grp.features.keyAt(i);
662 printf(" uses-feature: name='%s'\n",
663 ResTable::normalizeForOutput(featureName.string()).string());
664 }
665
666 const size_t numImpliedFeatures =
667 (impliedFeatures != NULL) ? impliedFeatures->size() : 0;
668 for (size_t i = 0; i < numImpliedFeatures; i++) {
669 const ImpliedFeature& impliedFeature = impliedFeatures->valueAt(i);
670 if (grp.features.indexOfKey(impliedFeature.name) >= 0) {
671 // The feature is explicitly set, no need to use implied
672 // definition.
673 continue;
674 }
675
676 String8 printableFeatureName(ResTable::normalizeForOutput(
677 impliedFeature.name.string()));
678 printf(" uses-feature: name='%s'\n", printableFeatureName.string());
679 printf(" uses-implied-feature: name='%s' reason='",
680 printableFeatureName.string());
681 const size_t numReasons = impliedFeature.reasons.size();
682 for (size_t j = 0; j < numReasons; j++) {
683 printf("%s", impliedFeature.reasons[j].string());
684 if (j + 2 < numReasons) {
685 printf(", ");
686 } else if (j + 1 < numReasons) {
687 printf(", and ");
688 }
689 }
690 printf("'\n");
691 }
692}
693
694static void addParentFeatures(FeatureGroup* grp, const String8& name) {
695 if (name == "android.hardware.camera.autofocus" ||
696 name == "android.hardware.camera.flash") {
697 grp->features.add(String8("android.hardware.camera"), true);
698 } else if (name == "android.hardware.location.gps" ||
699 name == "android.hardware.location.network") {
700 grp->features.add(String8("android.hardware.location"), true);
701 } else if (name == "android.hardware.touchscreen.multitouch") {
702 grp->features.add(String8("android.hardware.touchscreen"), true);
703 } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
704 grp->features.add(String8("android.hardware.touchscreen.multitouch"), true);
705 grp->features.add(String8("android.hardware.touchscreen"), true);
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700706 } else if (name == "android.hardware.opengles.aep") {
707 const int openGLESVersion31 = 0x00030001;
708 if (openGLESVersion31 > grp->openGLESVersion) {
709 grp->openGLESVersion = openGLESVersion31;
710 }
Adam Lesinski2c72b682014-06-24 09:56:01 -0700711 }
712}
713
Adam Lesinski282e1812014-01-23 18:17:42 -0800714/*
715 * Handle the "dump" command, to extract select data from an archive.
716 */
717extern char CONSOLE_DATA[2925]; // see EOF
718int doDump(Bundle* bundle)
719{
720 status_t result = UNKNOWN_ERROR;
Adam Lesinski282e1812014-01-23 18:17:42 -0800721
722 if (bundle->getFileSpecCount() < 1) {
723 fprintf(stderr, "ERROR: no dump option specified\n");
724 return 1;
725 }
726
727 if (bundle->getFileSpecCount() < 2) {
728 fprintf(stderr, "ERROR: no dump file specified\n");
729 return 1;
730 }
731
732 const char* option = bundle->getFileSpecEntry(0);
733 const char* filename = bundle->getFileSpecEntry(1);
734
735 AssetManager assets;
Narayan Kamathf85e41f2014-01-24 14:14:30 +0000736 int32_t assetsCookie;
Adam Lesinski282e1812014-01-23 18:17:42 -0800737 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
738 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
739 return 1;
740 }
741
742 // Make a dummy config for retrieving resources... we need to supply
743 // non-default values for some configs so that we can retrieve resources
744 // in the app that don't have a default. The most important of these is
745 // the API version because key resources like icons will have an implicit
746 // version if they are using newer config types like density.
747 ResTable_config config;
Narayan Kamath91447d82014-01-21 15:32:36 +0000748 memset(&config, 0, sizeof(ResTable_config));
Adam Lesinski282e1812014-01-23 18:17:42 -0800749 config.language[0] = 'e';
750 config.language[1] = 'n';
751 config.country[0] = 'U';
752 config.country[1] = 'S';
753 config.orientation = ResTable_config::ORIENTATION_PORT;
754 config.density = ResTable_config::DENSITY_MEDIUM;
755 config.sdkVersion = 10000; // Very high.
756 config.screenWidthDp = 320;
757 config.screenHeightDp = 480;
758 config.smallestScreenWidthDp = 320;
Adam Lesinskic2dea8d2014-08-04 16:40:41 -0700759 config.screenLayout |= ResTable_config::SCREENSIZE_NORMAL;
Adam Lesinski282e1812014-01-23 18:17:42 -0800760 assets.setConfiguration(config);
761
762 const ResTable& res = assets.getResources(false);
763 if (&res == NULL) {
764 fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
Adam Lesinski63e646e2014-07-30 11:40:39 -0700765 return 1;
Adam Lesinski25e9d552014-05-19 15:01:43 -0700766 } else if (res.getError() != NO_ERROR) {
767 fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
Adam Lesinski63e646e2014-07-30 11:40:39 -0700768 return 1;
Adam Lesinski282e1812014-01-23 18:17:42 -0800769 }
770
Adam Lesinski2cb761e2014-08-15 13:59:02 -0700771 // The dynamicRefTable can be null if there are no resources for this asset cookie.
772 // This fine.
Adam Lesinski63e646e2014-07-30 11:40:39 -0700773 const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700774
775 Asset* asset = NULL;
776
Adam Lesinski282e1812014-01-23 18:17:42 -0800777 if (strcmp("resources", option) == 0) {
778#ifndef HAVE_ANDROID_OS
779 res.print(bundle->getValues());
780#endif
781
782 } else if (strcmp("strings", option) == 0) {
783 const ResStringPool* pool = res.getTableStringBlock(0);
784 printStringPool(pool);
785
786 } else if (strcmp("xmltree", option) == 0) {
787 if (bundle->getFileSpecCount() < 3) {
788 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
789 goto bail;
790 }
791
792 for (int i=2; i<bundle->getFileSpecCount(); i++) {
793 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700794 ResXMLTree tree(dynamicRefTable);
795 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800796 if (asset == NULL) {
797 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
798 goto bail;
799 }
800
801 if (tree.setTo(asset->getBuffer(true),
802 asset->getLength()) != NO_ERROR) {
803 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
804 goto bail;
805 }
806 tree.restart();
807 printXMLBlock(&tree);
808 tree.uninit();
809 delete asset;
810 asset = NULL;
811 }
812
813 } else if (strcmp("xmlstrings", option) == 0) {
814 if (bundle->getFileSpecCount() < 3) {
815 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
816 goto bail;
817 }
818
819 for (int i=2; i<bundle->getFileSpecCount(); i++) {
820 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700821 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800822 if (asset == NULL) {
823 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
824 goto bail;
825 }
826
Adam Lesinski63e646e2014-07-30 11:40:39 -0700827 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800828 if (tree.setTo(asset->getBuffer(true),
829 asset->getLength()) != NO_ERROR) {
830 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
831 goto bail;
832 }
833 printStringPool(&tree.getStrings());
834 delete asset;
835 asset = NULL;
836 }
837
838 } else {
Adam Lesinski63e646e2014-07-30 11:40:39 -0700839 asset = assets.openNonAsset(assetsCookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800840 if (asset == NULL) {
841 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
842 goto bail;
843 }
844
Adam Lesinski63e646e2014-07-30 11:40:39 -0700845 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800846 if (tree.setTo(asset->getBuffer(true),
847 asset->getLength()) != NO_ERROR) {
848 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
849 goto bail;
850 }
851 tree.restart();
852
853 if (strcmp("permissions", option) == 0) {
854 size_t len;
855 ResXMLTree::event_code_t code;
856 int depth = 0;
857 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
858 if (code == ResXMLTree::END_TAG) {
859 depth--;
860 continue;
861 }
862 if (code != ResXMLTree::START_TAG) {
863 continue;
864 }
865 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700866 const char16_t* ctag16 = tree.getElementName(&len);
867 if (ctag16 == NULL) {
868 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
869 goto bail;
870 }
871 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800872 //printf("Depth %d tag %s\n", depth, tag.string());
873 if (depth == 1) {
874 if (tag != "manifest") {
875 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
876 goto bail;
877 }
878 String8 pkg = getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -0700879 printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800880 } else if (depth == 2 && tag == "permission") {
881 String8 error;
882 String8 name = getAttribute(tree, NAME_ATTR, &error);
883 if (error != "") {
884 fprintf(stderr, "ERROR: %s\n", error.string());
885 goto bail;
886 }
Maurice Chu2675f762013-10-22 17:33:11 -0700887 printf("permission: %s\n",
888 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800889 } else if (depth == 2 && tag == "uses-permission") {
890 String8 error;
891 String8 name = getAttribute(tree, NAME_ATTR, &error);
892 if (error != "") {
893 fprintf(stderr, "ERROR: %s\n", error.string());
894 goto bail;
895 }
Adam Lesinski58f1f362013-11-12 12:59:08 -0800896 printUsesPermission(name,
897 getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1) == 0,
898 getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1));
Adam Lesinski282e1812014-01-23 18:17:42 -0800899 }
900 }
901 } else if (strcmp("badging", option) == 0) {
902 Vector<String8> locales;
903 res.getLocales(&locales);
904
905 Vector<ResTable_config> configs;
906 res.getConfigurations(&configs);
907 SortedVector<int> densities;
908 const size_t NC = configs.size();
909 for (size_t i=0; i<NC; i++) {
910 int dens = configs[i].density;
911 if (dens == 0) {
912 dens = 160;
913 }
914 densities.add(dens);
915 }
916
917 size_t len;
918 ResXMLTree::event_code_t code;
919 int depth = 0;
920 String8 error;
921 bool withinActivity = false;
922 bool isMainActivity = false;
923 bool isLauncherActivity = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800924 bool isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800925 bool isSearchable = false;
926 bool withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700927 bool withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700928 bool withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800929 bool withinReceiver = false;
930 bool withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700931 bool withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800932 bool withinIntentFilter = false;
933 bool hasMainActivity = false;
934 bool hasOtherActivities = false;
935 bool hasOtherReceivers = false;
936 bool hasOtherServices = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700937 bool hasIntentFilter = false;
938
Adam Lesinski282e1812014-01-23 18:17:42 -0800939 bool hasWallpaperService = false;
940 bool hasImeService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700941 bool hasAccessibilityService = false;
942 bool hasPrintService = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800943 bool hasWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700944 bool hasDeviceAdminReceiver = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700945 bool hasPaymentService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700946 bool hasDocumentsProvider = false;
947 bool hasCameraActivity = false;
948 bool hasCameraSecureActivity = false;
949 bool hasLauncher = false;
950 bool hasNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400951 bool hasDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700952
Adam Lesinski282e1812014-01-23 18:17:42 -0800953 bool actMainActivity = false;
954 bool actWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700955 bool actDeviceAdminEnabled = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800956 bool actImeService = false;
957 bool actWallpaperService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700958 bool actAccessibilityService = false;
959 bool actPrintService = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700960 bool actHostApduService = false;
961 bool actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700962 bool actDocumentsProvider = false;
963 bool actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400964 bool actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700965 bool actCamera = false;
966 bool actCameraSecure = false;
967 bool catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700968 bool hasMetaHostPaymentCategory = false;
969 bool hasMetaOffHostPaymentCategory = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700970
971 // These permissions are required by services implementing services
972 // the system binds to (IME, Accessibility, PrintServices, etc.)
973 bool hasBindDeviceAdminPermission = false;
974 bool hasBindInputMethodPermission = false;
975 bool hasBindAccessibilityServicePermission = false;
976 bool hasBindPrintServicePermission = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700977 bool hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700978 bool hasRequiredSafAttributes = false;
979 bool hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400980 bool hasBindDreamServicePermission = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800981
982 // These two implement the implicit permissions that are granted
983 // to pre-1.6 applications.
984 bool hasWriteExternalStoragePermission = false;
985 bool hasReadPhoneStatePermission = false;
986
987 // If an app requests write storage, they will also get read storage.
988 bool hasReadExternalStoragePermission = false;
989
990 // Implement transition to read and write call log.
991 bool hasReadContactsPermission = false;
992 bool hasWriteContactsPermission = false;
993 bool hasReadCallLogPermission = false;
994 bool hasWriteCallLogPermission = false;
995
Adam Lesinskie47fd122014-08-15 22:25:36 -0700996 // If an app declares itself as multiArch, we report the
997 // native libraries differently.
998 bool hasMultiArch = false;
999
Adam Lesinski282e1812014-01-23 18:17:42 -08001000 // This next group of variables is used to implement a group of
1001 // backward-compatibility heuristics necessitated by the addition of
1002 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
1003 // heuristic is "if an app requests a permission but doesn't explicitly
1004 // request the corresponding <uses-feature>, presume it's there anyway".
Adam Lesinski2c72b682014-06-24 09:56:01 -07001005
Adam Lesinski282e1812014-01-23 18:17:42 -08001006 // 2.2 also added some other features that apps can request, but that
1007 // have no corresponding permission, so we cannot implement any
1008 // back-compatibility heuristic for them. The below are thus unnecessary
1009 // (but are retained here for documentary purposes.)
1010 //bool specCompassFeature = false;
1011 //bool specAccelerometerFeature = false;
1012 //bool specProximityFeature = false;
1013 //bool specAmbientLightFeature = false;
1014 //bool specLiveWallpaperFeature = false;
1015
1016 int targetSdk = 0;
1017 int smallScreen = 1;
1018 int normalScreen = 1;
1019 int largeScreen = 1;
1020 int xlargeScreen = 1;
1021 int anyDensity = 1;
1022 int requiresSmallestWidthDp = 0;
1023 int compatibleWidthLimitDp = 0;
1024 int largestWidthLimitDp = 0;
1025 String8 pkg;
1026 String8 activityName;
1027 String8 activityLabel;
1028 String8 activityIcon;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001029 String8 activityBanner;
Adam Lesinski282e1812014-01-23 18:17:42 -08001030 String8 receiverName;
1031 String8 serviceName;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001032 Vector<String8> supportedInput;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001033
1034 FeatureGroup commonFeatures;
1035 Vector<FeatureGroup> featureGroups;
1036 KeyedVector<String8, ImpliedFeature> impliedFeatures;
1037
Adam Lesinski282e1812014-01-23 18:17:42 -08001038 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
1039 if (code == ResXMLTree::END_TAG) {
1040 depth--;
1041 if (depth < 2) {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001042 if (withinSupportsInput && !supportedInput.isEmpty()) {
1043 printf("supports-input: '");
1044 const size_t N = supportedInput.size();
1045 for (size_t i=0; i<N; i++) {
Maurice Chu2675f762013-10-22 17:33:11 -07001046 printf("%s", ResTable::normalizeForOutput(
1047 supportedInput[i].string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001048 if (i != N - 1) {
1049 printf("' '");
1050 } else {
1051 printf("'\n");
1052 }
1053 }
1054 supportedInput.clear();
1055 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001056 withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001057 withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001058 withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001059 } else if (depth < 3) {
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001060 if (withinActivity && isMainActivity) {
Maurice Chu2675f762013-10-22 17:33:11 -07001061 String8 aName(getComponentName(pkg, activityName));
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001062 if (isLauncherActivity) {
1063 printf("launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001064 if (aName.length() > 0) {
1065 printf(" name='%s' ",
1066 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001067 }
1068 printf(" label='%s' icon='%s'\n",
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001069 ResTable::normalizeForOutput(activityLabel.string()).string(),
1070 ResTable::normalizeForOutput(activityIcon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001071 }
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001072 if (isLeanbackLauncherActivity) {
1073 printf("leanback-launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001074 if (aName.length() > 0) {
1075 printf(" name='%s' ",
1076 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001077 }
1078 printf(" label='%s' icon='%s' banner='%s'\n",
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001079 ResTable::normalizeForOutput(activityLabel.string()).string(),
1080 ResTable::normalizeForOutput(activityIcon.string()).string(),
1081 ResTable::normalizeForOutput(activityBanner.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001082 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001083 }
1084 if (!hasIntentFilter) {
1085 hasOtherActivities |= withinActivity;
1086 hasOtherReceivers |= withinReceiver;
1087 hasOtherServices |= withinService;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001088 } else {
1089 if (withinService) {
1090 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
1091 hasBindNfcServicePermission);
1092 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
1093 hasBindNfcServicePermission);
1094 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001095 }
1096 withinActivity = false;
1097 withinService = false;
1098 withinReceiver = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001099 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001100 hasIntentFilter = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001101 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001102 } else if (depth < 4) {
1103 if (withinIntentFilter) {
1104 if (withinActivity) {
1105 hasMainActivity |= actMainActivity;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001106 hasLauncher |= catLauncher;
1107 hasCameraActivity |= actCamera;
1108 hasCameraSecureActivity |= actCameraSecure;
1109 hasOtherActivities |= !actMainActivity && !actCamera && !actCameraSecure;
Adam Lesinski282e1812014-01-23 18:17:42 -08001110 } else if (withinReceiver) {
1111 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001112 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
1113 hasBindDeviceAdminPermission);
1114 hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled);
Adam Lesinski282e1812014-01-23 18:17:42 -08001115 } else if (withinService) {
1116 hasImeService |= actImeService;
1117 hasWallpaperService |= actWallpaperService;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001118 hasAccessibilityService |= (actAccessibilityService &&
1119 hasBindAccessibilityServicePermission);
1120 hasPrintService |= (actPrintService && hasBindPrintServicePermission);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001121 hasNotificationListenerService |= actNotificationListenerService &&
1122 hasBindNotificationListenerServicePermission;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001123 hasDreamService |= actDreamService && hasBindDreamServicePermission;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001124 hasOtherServices |= (!actImeService && !actWallpaperService &&
Adam Lesinski94fc9122013-09-30 17:16:09 -07001125 !actAccessibilityService && !actPrintService &&
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001126 !actHostApduService && !actOffHostApduService &&
1127 !actNotificationListenerService);
1128 } else if (withinProvider) {
1129 hasDocumentsProvider |= actDocumentsProvider && hasRequiredSafAttributes;
Adam Lesinski282e1812014-01-23 18:17:42 -08001130 }
1131 }
1132 withinIntentFilter = false;
1133 }
1134 continue;
1135 }
1136 if (code != ResXMLTree::START_TAG) {
1137 continue;
1138 }
1139 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001140
1141 const char16_t* ctag16 = tree.getElementName(&len);
1142 if (ctag16 == NULL) {
1143 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
1144 goto bail;
1145 }
1146 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -08001147 //printf("Depth %d, %s\n", depth, tag.string());
1148 if (depth == 1) {
1149 if (tag != "manifest") {
1150 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
1151 goto bail;
1152 }
1153 pkg = getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -07001154 printf("package: name='%s' ",
1155 ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001156 int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
1157 if (error != "") {
1158 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
1159 goto bail;
1160 }
1161 if (versionCode > 0) {
1162 printf("versionCode='%d' ", versionCode);
1163 } else {
1164 printf("versionCode='' ");
1165 }
1166 String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
1167 if (error != "") {
1168 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
1169 goto bail;
1170 }
Adam Lesinski25d35a92014-08-11 09:41:56 -07001171 printf("versionName='%s'",
Maurice Chu2675f762013-10-22 17:33:11 -07001172 ResTable::normalizeForOutput(versionName.string()).string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001173
1174 String8 splitName = getAttribute(tree, NULL, "split", NULL);
1175 if (!splitName.isEmpty()) {
1176 printf(" split='%s'", ResTable::normalizeForOutput(
1177 splitName.string()).string());
1178 }
1179 printf("\n");
Adam Lesinski282e1812014-01-23 18:17:42 -08001180 } else if (depth == 2) {
1181 withinApplication = false;
1182 if (tag == "application") {
1183 withinApplication = true;
1184
1185 String8 label;
1186 const size_t NL = locales.size();
1187 for (size_t i=0; i<NL; i++) {
1188 const char* localeStr = locales[i].string();
1189 assets.setLocale(localeStr != NULL ? localeStr : "");
1190 String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1191 if (llabel != "") {
1192 if (localeStr == NULL || strlen(localeStr) == 0) {
1193 label = llabel;
Maurice Chu2675f762013-10-22 17:33:11 -07001194 printf("application-label:'%s'\n",
1195 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001196 } else {
1197 if (label == "") {
1198 label = llabel;
1199 }
1200 printf("application-label-%s:'%s'\n", localeStr,
Maurice Chu2675f762013-10-22 17:33:11 -07001201 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001202 }
1203 }
1204 }
1205
1206 ResTable_config tmpConfig = config;
1207 const size_t ND = densities.size();
1208 for (size_t i=0; i<ND; i++) {
1209 tmpConfig.density = densities[i];
1210 assets.setConfiguration(tmpConfig);
1211 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1212 if (icon != "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001213 printf("application-icon-%d:'%s'\n", densities[i],
1214 ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001215 }
1216 }
1217 assets.setConfiguration(config);
1218
1219 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1220 if (error != "") {
1221 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
1222 goto bail;
1223 }
1224 int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
1225 if (error != "") {
1226 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
1227 goto bail;
1228 }
Maurice Chu2675f762013-10-22 17:33:11 -07001229 printf("application: label='%s' ",
1230 ResTable::normalizeForOutput(label.string()).string());
1231 printf("icon='%s'\n", ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001232 if (testOnly != 0) {
1233 printf("testOnly='%d'\n", testOnly);
1234 }
1235
1236 int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0);
1237 if (error != "") {
1238 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string());
1239 goto bail;
1240 }
1241 if (debuggable != 0) {
1242 printf("application-debuggable\n");
1243 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07001244
1245 // We must search by name because the multiArch flag hasn't been API
1246 // frozen yet.
1247 int32_t multiArchIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
1248 "multiArch");
1249 if (multiArchIndex >= 0) {
1250 Res_value value;
1251 if (tree.getAttributeValue(multiArchIndex, &value) != NO_ERROR) {
1252 if (value.dataType >= Res_value::TYPE_FIRST_INT &&
1253 value.dataType <= Res_value::TYPE_LAST_INT) {
1254 hasMultiArch = value.data;
1255 }
1256 }
1257 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001258 } else if (tag == "uses-sdk") {
1259 int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
1260 if (error != "") {
1261 error = "";
1262 String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
1263 if (error != "") {
1264 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
1265 error.string());
1266 goto bail;
1267 }
1268 if (name == "Donut") targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001269 printf("sdkVersion:'%s'\n",
1270 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001271 } else if (code != -1) {
1272 targetSdk = code;
1273 printf("sdkVersion:'%d'\n", code);
1274 }
1275 code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
1276 if (code != -1) {
1277 printf("maxSdkVersion:'%d'\n", code);
1278 }
1279 code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
1280 if (error != "") {
1281 error = "";
1282 String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
1283 if (error != "") {
1284 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
1285 error.string());
1286 goto bail;
1287 }
1288 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001289 printf("targetSdkVersion:'%s'\n",
1290 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001291 } else if (code != -1) {
1292 if (targetSdk < code) {
1293 targetSdk = code;
1294 }
1295 printf("targetSdkVersion:'%d'\n", code);
1296 }
1297 } else if (tag == "uses-configuration") {
1298 int32_t reqTouchScreen = getIntegerAttribute(tree,
1299 REQ_TOUCH_SCREEN_ATTR, NULL, 0);
1300 int32_t reqKeyboardType = getIntegerAttribute(tree,
1301 REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
1302 int32_t reqHardKeyboard = getIntegerAttribute(tree,
1303 REQ_HARD_KEYBOARD_ATTR, NULL, 0);
1304 int32_t reqNavigation = getIntegerAttribute(tree,
1305 REQ_NAVIGATION_ATTR, NULL, 0);
1306 int32_t reqFiveWayNav = getIntegerAttribute(tree,
1307 REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
1308 printf("uses-configuration:");
1309 if (reqTouchScreen != 0) {
1310 printf(" reqTouchScreen='%d'", reqTouchScreen);
1311 }
1312 if (reqKeyboardType != 0) {
1313 printf(" reqKeyboardType='%d'", reqKeyboardType);
1314 }
1315 if (reqHardKeyboard != 0) {
1316 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1317 }
1318 if (reqNavigation != 0) {
1319 printf(" reqNavigation='%d'", reqNavigation);
1320 }
1321 if (reqFiveWayNav != 0) {
1322 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1323 }
1324 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001325 } else if (tag == "supports-input") {
1326 withinSupportsInput = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001327 } else if (tag == "supports-screens") {
1328 smallScreen = getIntegerAttribute(tree,
1329 SMALL_SCREEN_ATTR, NULL, 1);
1330 normalScreen = getIntegerAttribute(tree,
1331 NORMAL_SCREEN_ATTR, NULL, 1);
1332 largeScreen = getIntegerAttribute(tree,
1333 LARGE_SCREEN_ATTR, NULL, 1);
1334 xlargeScreen = getIntegerAttribute(tree,
1335 XLARGE_SCREEN_ATTR, NULL, 1);
1336 anyDensity = getIntegerAttribute(tree,
1337 ANY_DENSITY_ATTR, NULL, 1);
1338 requiresSmallestWidthDp = getIntegerAttribute(tree,
1339 REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
1340 compatibleWidthLimitDp = getIntegerAttribute(tree,
1341 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
1342 largestWidthLimitDp = getIntegerAttribute(tree,
1343 LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001344 } else if (tag == "feature-group") {
1345 withinFeatureGroup = true;
1346 FeatureGroup group;
1347 group.label = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1348 if (error != "") {
1349 fprintf(stderr, "ERROR getting 'android:label' attribute:"
1350 " %s\n", error.string());
1351 goto bail;
1352 }
1353 featureGroups.add(group);
1354
Adam Lesinski282e1812014-01-23 18:17:42 -08001355 } else if (tag == "uses-feature") {
1356 String8 name = getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001357 if (name != "" && error == "") {
1358 int req = getIntegerAttribute(tree,
1359 REQUIRED_ATTR, NULL, 1);
1360
Adam Lesinski2c72b682014-06-24 09:56:01 -07001361 commonFeatures.features.add(name, req);
1362 if (req) {
1363 addParentFeatures(&commonFeatures, name);
Adam Lesinski282e1812014-01-23 18:17:42 -08001364 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001365 } else {
1366 int vers = getIntegerAttribute(tree,
1367 GL_ES_VERSION_ATTR, &error);
1368 if (error == "") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001369 if (vers > commonFeatures.openGLESVersion) {
1370 commonFeatures.openGLESVersion = vers;
1371 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001372 }
1373 }
1374 } else if (tag == "uses-permission") {
1375 String8 name = getAttribute(tree, NAME_ATTR, &error);
1376 if (name != "" && error == "") {
1377 if (name == "android.permission.CAMERA") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001378 addImpliedFeature(&impliedFeatures, "android.hardware.camera",
Adam Lesinski2c72b682014-06-24 09:56:01 -07001379 String8::format("requested %s permission", name.string())
1380 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001381 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001382 addImpliedFeature(&impliedFeatures, "android.hardware.location.gps",
1383 String8::format("requested %s permission", name.string())
1384 .string());
1385 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1386 String8::format("requested %s permission", name.string())
1387 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001388 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001389 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1390 String8::format("requested %s permission", name.string())
1391 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001392 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001393 addImpliedFeature(&impliedFeatures, "android.hardware.location.network",
1394 String8::format("requested %s permission", name.string())
1395 .string());
1396 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1397 String8::format("requested %s permission", name.string())
1398 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001399 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
1400 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001401 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1402 String8::format("requested %s permission", name.string())
1403 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001404 } else if (name == "android.permission.BLUETOOTH" ||
1405 name == "android.permission.BLUETOOTH_ADMIN") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001406 if (targetSdk > 4) {
1407 addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
1408 String8::format("requested %s permission", name.string())
1409 .string());
1410 addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
1411 "targetSdkVersion > 4");
1412 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001413 } else if (name == "android.permission.RECORD_AUDIO") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001414 addImpliedFeature(&impliedFeatures, "android.hardware.microphone",
1415 String8::format("requested %s permission", name.string())
1416 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001417 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1418 name == "android.permission.CHANGE_WIFI_STATE" ||
1419 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001420 addImpliedFeature(&impliedFeatures, "android.hardware.wifi",
1421 String8::format("requested %s permission", name.string())
1422 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001423 } else if (name == "android.permission.CALL_PHONE" ||
1424 name == "android.permission.CALL_PRIVILEGED" ||
1425 name == "android.permission.MODIFY_PHONE_STATE" ||
1426 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1427 name == "android.permission.READ_SMS" ||
1428 name == "android.permission.RECEIVE_SMS" ||
1429 name == "android.permission.RECEIVE_MMS" ||
1430 name == "android.permission.RECEIVE_WAP_PUSH" ||
1431 name == "android.permission.SEND_SMS" ||
1432 name == "android.permission.WRITE_APN_SETTINGS" ||
1433 name == "android.permission.WRITE_SMS") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001434 addImpliedFeature(&impliedFeatures, "android.hardware.telephony",
1435 String8("requested a telephony permission").string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001436 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1437 hasWriteExternalStoragePermission = true;
1438 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1439 hasReadExternalStoragePermission = true;
1440 } else if (name == "android.permission.READ_PHONE_STATE") {
1441 hasReadPhoneStatePermission = true;
1442 } else if (name == "android.permission.READ_CONTACTS") {
1443 hasReadContactsPermission = true;
1444 } else if (name == "android.permission.WRITE_CONTACTS") {
1445 hasWriteContactsPermission = true;
1446 } else if (name == "android.permission.READ_CALL_LOG") {
1447 hasReadCallLogPermission = true;
1448 } else if (name == "android.permission.WRITE_CALL_LOG") {
1449 hasWriteCallLogPermission = true;
1450 }
Adam Lesinski58f1f362013-11-12 12:59:08 -08001451
1452 printUsesPermission(name,
1453 getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1) == 0,
1454 getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1));
1455 } else {
Adam Lesinski282e1812014-01-23 18:17:42 -08001456 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1457 error.string());
1458 goto bail;
1459 }
1460 } else if (tag == "uses-package") {
1461 String8 name = getAttribute(tree, NAME_ATTR, &error);
1462 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001463 printf("uses-package:'%s'\n",
1464 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001465 } else {
1466 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1467 error.string());
1468 goto bail;
1469 }
1470 } else if (tag == "original-package") {
1471 String8 name = getAttribute(tree, NAME_ATTR, &error);
1472 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001473 printf("original-package:'%s'\n",
1474 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001475 } else {
1476 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1477 error.string());
1478 goto bail;
1479 }
1480 } else if (tag == "supports-gl-texture") {
1481 String8 name = getAttribute(tree, NAME_ATTR, &error);
1482 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001483 printf("supports-gl-texture:'%s'\n",
1484 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001485 } else {
1486 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1487 error.string());
1488 goto bail;
1489 }
1490 } else if (tag == "compatible-screens") {
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001491 printCompatibleScreens(tree, &error);
1492 if (error != "") {
1493 fprintf(stderr, "ERROR getting compatible screens: %s\n",
1494 error.string());
1495 goto bail;
1496 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001497 depth--;
1498 } else if (tag == "package-verifier") {
1499 String8 name = getAttribute(tree, NAME_ATTR, &error);
1500 if (name != "" && error == "") {
1501 String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
1502 if (publicKey != "" && error == "") {
1503 printf("package-verifier: name='%s' publicKey='%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001504 ResTable::normalizeForOutput(name.string()).string(),
1505 ResTable::normalizeForOutput(publicKey.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001506 }
1507 }
1508 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001509 } else if (depth == 3) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001510 withinActivity = false;
1511 withinReceiver = false;
1512 withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001513 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001514 hasIntentFilter = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001515 hasMetaHostPaymentCategory = false;
1516 hasMetaOffHostPaymentCategory = false;
1517 hasBindDeviceAdminPermission = false;
1518 hasBindInputMethodPermission = false;
1519 hasBindAccessibilityServicePermission = false;
1520 hasBindPrintServicePermission = false;
1521 hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001522 hasRequiredSafAttributes = false;
1523 hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001524 hasBindDreamServicePermission = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001525 if (withinApplication) {
1526 if(tag == "activity") {
1527 withinActivity = true;
1528 activityName = getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001529 if (error != "") {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001530 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1531 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001532 goto bail;
1533 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001534
Michael Wrightec4fdec2013-09-06 16:50:52 -07001535 activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1536 if (error != "") {
1537 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
1538 error.string());
1539 goto bail;
1540 }
1541
1542 activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1543 if (error != "") {
1544 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1545 error.string());
1546 goto bail;
1547 }
1548
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001549 activityBanner = getResolvedAttribute(&res, tree, BANNER_ATTR, &error);
1550 if (error != "") {
1551 fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
1552 error.string());
1553 goto bail;
1554 }
1555
Michael Wrightec4fdec2013-09-06 16:50:52 -07001556 int32_t orien = getResolvedIntegerAttribute(&res, tree,
1557 SCREEN_ORIENTATION_ATTR, &error);
1558 if (error == "") {
1559 if (orien == 0 || orien == 6 || orien == 8) {
1560 // Requests landscape, sensorLandscape, or reverseLandscape.
Adam Lesinski2c72b682014-06-24 09:56:01 -07001561 addImpliedFeature(&impliedFeatures, "android.hardware.screen.landscape",
1562 "one or more activities have specified a landscape orientation");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001563 } else if (orien == 1 || orien == 7 || orien == 9) {
1564 // Requests portrait, sensorPortrait, or reversePortrait.
Adam Lesinski2c72b682014-06-24 09:56:01 -07001565 addImpliedFeature(&impliedFeatures, "android.hardware.screen.portrait",
1566 "one or more activities have specified a portrait orientation");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001567 }
1568 }
1569 } else if (tag == "uses-library") {
1570 String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1571 if (error != "") {
1572 fprintf(stderr,
1573 "ERROR getting 'android:name' attribute for uses-library"
1574 " %s\n", error.string());
1575 goto bail;
1576 }
1577 int req = getIntegerAttribute(tree,
1578 REQUIRED_ATTR, NULL, 1);
1579 printf("uses-library%s:'%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001580 req ? "" : "-not-required", ResTable::normalizeForOutput(
1581 libraryName.string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001582 } else if (tag == "receiver") {
1583 withinReceiver = true;
1584 receiverName = getAttribute(tree, NAME_ATTR, &error);
1585
1586 if (error != "") {
1587 fprintf(stderr,
1588 "ERROR getting 'android:name' attribute for receiver:"
1589 " %s\n", error.string());
1590 goto bail;
1591 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001592
1593 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1594 if (error == "") {
1595 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1596 hasBindDeviceAdminPermission = true;
1597 }
1598 } else {
1599 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1600 " receiver '%s': %s\n", receiverName.string(), error.string());
1601 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001602 } else if (tag == "service") {
1603 withinService = true;
1604 serviceName = getAttribute(tree, NAME_ATTR, &error);
1605
1606 if (error != "") {
1607 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1608 "service:%s\n", error.string());
1609 goto bail;
1610 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001611
1612 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1613 if (error == "") {
1614 if (permission == "android.permission.BIND_INPUT_METHOD") {
1615 hasBindInputMethodPermission = true;
1616 } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") {
1617 hasBindAccessibilityServicePermission = true;
1618 } else if (permission == "android.permission.BIND_PRINT_SERVICE") {
1619 hasBindPrintServicePermission = true;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001620 } else if (permission == "android.permission.BIND_NFC_SERVICE") {
1621 hasBindNfcServicePermission = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001622 } else if (permission == "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
1623 hasBindNotificationListenerServicePermission = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001624 } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
1625 hasBindDreamServicePermission = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001626 }
1627 } else {
1628 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1629 " service '%s': %s\n", serviceName.string(), error.string());
1630 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001631 } else if (tag == "provider") {
1632 withinProvider = true;
1633
1634 bool exported = getResolvedIntegerAttribute(&res, tree, EXPORTED_ATTR, &error);
1635 if (error != "") {
1636 fprintf(stderr, "ERROR getting 'android:exported' attribute for provider:"
1637 " %s\n", error.string());
1638 goto bail;
1639 }
1640
1641 bool grantUriPermissions = getResolvedIntegerAttribute(&res, tree,
1642 GRANT_URI_PERMISSIONS_ATTR, &error);
1643 if (error != "") {
1644 fprintf(stderr, "ERROR getting 'android:grantUriPermissions' attribute for provider:"
1645 " %s\n", error.string());
1646 goto bail;
1647 }
1648
1649 String8 permission = getResolvedAttribute(&res, tree, PERMISSION_ATTR, &error);
1650 if (error != "") {
1651 fprintf(stderr, "ERROR getting 'android:permission' attribute for provider:"
1652 " %s\n", error.string());
1653 goto bail;
1654 }
1655
1656 hasRequiredSafAttributes |= exported && grantUriPermissions &&
1657 permission == "android.permission.MANAGE_DOCUMENTS";
1658
Michael Wrightec4fdec2013-09-06 16:50:52 -07001659 } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
Adam Lesinskib71adb62014-05-15 14:14:41 -07001660 String8 metaDataName = getResolvedAttribute(&res, tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001661 if (error != "") {
1662 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1663 "meta-data:%s\n", error.string());
1664 goto bail;
1665 }
Maurice Chu2675f762013-10-22 17:33:11 -07001666 printf("meta-data: name='%s' ",
1667 ResTable::normalizeForOutput(metaDataName.string()).string());
Maurice Chu76327312013-10-16 18:28:46 -07001668 printResolvedResourceAttribute(&res, tree, VALUE_ATTR, String8("value"),
1669 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001670 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001671 // Try looking for a RESOURCE_ATTR
1672 error = "";
1673 printResolvedResourceAttribute(&res, tree, RESOURCE_ATTR,
1674 String8("resource"), &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001675 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001676 fprintf(stderr, "ERROR getting 'android:value' or "
1677 "'android:resource' attribute for "
1678 "meta-data:%s\n", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001679 goto bail;
1680 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001681 }
Maurice Chu76327312013-10-16 18:28:46 -07001682 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001683 } else if (withinSupportsInput && tag == "input-type") {
1684 String8 name = getAttribute(tree, NAME_ATTR, &error);
1685 if (name != "" && error == "") {
1686 supportedInput.add(name);
1687 } else {
1688 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1689 error.string());
1690 goto bail;
1691 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001692 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001693 } else if (withinFeatureGroup && tag == "uses-feature") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001694 FeatureGroup& top = featureGroups.editTop();
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001695
1696 String8 name = getResolvedAttribute(&res, tree, NAME_ATTR, &error);
1697 if (name != "" && error == "") {
Adam Lesinskid3edfde2014-08-08 17:32:44 -07001698 top.features.add(name, true);
1699 addParentFeatures(&top, name);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001700 } else {
1701 int vers = getIntegerAttribute(tree, GL_ES_VERSION_ATTR, &error);
1702 if (error == "") {
1703 if (vers > top.openGLESVersion) {
1704 top.openGLESVersion = vers;
1705 }
1706 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001707 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001708 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001709 } else if (depth == 4) {
1710 if (tag == "intent-filter") {
1711 hasIntentFilter = true;
1712 withinIntentFilter = true;
1713 actMainActivity = false;
1714 actWidgetReceivers = false;
1715 actImeService = false;
1716 actWallpaperService = false;
1717 actAccessibilityService = false;
1718 actPrintService = false;
1719 actDeviceAdminEnabled = false;
1720 actHostApduService = false;
1721 actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001722 actDocumentsProvider = false;
1723 actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001724 actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001725 actCamera = false;
1726 actCameraSecure = false;
1727 catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001728 } else if (withinService && tag == "meta-data") {
1729 String8 name = getAttribute(tree, NAME_ATTR, &error);
1730 if (error != "") {
1731 fprintf(stderr, "ERROR getting 'android:name' attribute for"
1732 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1733 goto bail;
1734 }
1735
1736 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1737 name == "android.nfc.cardemulation.off_host_apdu_service") {
1738 bool offHost = true;
1739 if (name == "android.nfc.cardemulation.host_apdu_service") {
1740 offHost = false;
1741 }
1742
1743 String8 xmlPath = getResolvedAttribute(&res, tree, RESOURCE_ATTR, &error);
1744 if (error != "") {
1745 fprintf(stderr, "ERROR getting 'android:resource' attribute for"
1746 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1747 goto bail;
1748 }
1749
1750 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1751 offHost, &error);
1752 if (error != "") {
1753 fprintf(stderr, "ERROR getting AID category for service '%s'\n",
1754 serviceName.string());
1755 goto bail;
1756 }
1757
1758 const size_t catLen = categories.size();
1759 for (size_t i = 0; i < catLen; i++) {
1760 bool paymentCategory = (categories[i] == "payment");
1761 if (offHost) {
1762 hasMetaOffHostPaymentCategory |= paymentCategory;
1763 } else {
1764 hasMetaHostPaymentCategory |= paymentCategory;
1765 }
1766 }
1767 }
1768 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001769 } else if ((depth == 5) && withinIntentFilter) {
1770 String8 action;
1771 if (tag == "action") {
1772 action = getAttribute(tree, NAME_ATTR, &error);
1773 if (error != "") {
1774 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1775 error.string());
1776 goto bail;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001777 }
1778
Adam Lesinskia5018c92013-09-30 16:23:15 -07001779 if (withinActivity) {
1780 if (action == "android.intent.action.MAIN") {
1781 isMainActivity = true;
1782 actMainActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001783 } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
1784 action == "android.media.action.VIDEO_CAMERA") {
1785 actCamera = true;
1786 } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
1787 actCameraSecure = true;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001788 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001789 } else if (withinReceiver) {
1790 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1791 actWidgetReceivers = true;
1792 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1793 actDeviceAdminEnabled = true;
1794 }
1795 } else if (withinService) {
1796 if (action == "android.view.InputMethod") {
1797 actImeService = true;
1798 } else if (action == "android.service.wallpaper.WallpaperService") {
1799 actWallpaperService = true;
1800 } else if (action == "android.accessibilityservice.AccessibilityService") {
1801 actAccessibilityService = true;
1802 } else if (action == "android.printservice.PrintService") {
1803 actPrintService = true;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001804 } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
1805 actHostApduService = true;
1806 } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
1807 actOffHostApduService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001808 } else if (action == "android.service.notification.NotificationListenerService") {
1809 actNotificationListenerService = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001810 } else if (action == "android.service.dreams.DreamService") {
1811 actDreamService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001812 }
1813 } else if (withinProvider) {
1814 if (action == "android.content.action.DOCUMENTS_PROVIDER") {
1815 actDocumentsProvider = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001816 }
1817 }
1818 if (action == "android.intent.action.SEARCH") {
1819 isSearchable = true;
1820 }
1821 }
1822
1823 if (tag == "category") {
1824 String8 category = getAttribute(tree, NAME_ATTR, &error);
1825 if (error != "") {
1826 fprintf(stderr, "ERROR getting 'name' attribute: %s\n",
1827 error.string());
1828 goto bail;
1829 }
1830 if (withinActivity) {
1831 if (category == "android.intent.category.LAUNCHER") {
1832 isLauncherActivity = true;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001833 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
1834 isLeanbackLauncherActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001835 } else if (category == "android.intent.category.HOME") {
1836 catLauncher = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001837 }
1838 }
1839 }
1840 }
1841 }
1842
1843 // Pre-1.6 implicitly granted permission compatibility logic
1844 if (targetSdk < 4) {
1845 if (!hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001846 printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
1847 printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
1848 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001849 hasWriteExternalStoragePermission = true;
1850 }
1851 if (!hasReadPhoneStatePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001852 printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
1853 printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
1854 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001855 }
1856 }
1857
1858 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1859 // force them to always take READ_EXTERNAL_STORAGE as well. We always
1860 // do this (regardless of target API version) because we can't have
1861 // an app with write permission but not read permission.
1862 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001863 printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"));
1864 printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
1865 String8("requested WRITE_EXTERNAL_STORAGE"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001866 }
1867
1868 // Pre-JellyBean call log permission compatibility.
1869 if (targetSdk < 16) {
1870 if (!hasReadCallLogPermission && hasReadContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001871 printUsesPermission(String8("android.permission.READ_CALL_LOG"));
1872 printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
1873 String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001874 }
1875 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001876 printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
1877 printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
1878 String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001879 }
1880 }
1881
Adam Lesinski2c72b682014-06-24 09:56:01 -07001882 addImpliedFeature(&impliedFeatures, "android.hardware.touchscreen",
1883 "default feature for all apps");
1884
1885 const size_t numFeatureGroups = featureGroups.size();
1886 if (numFeatureGroups == 0) {
1887 // If no <feature-group> tags were defined, apply auto-implied features.
1888 printFeatureGroup(commonFeatures, &impliedFeatures);
1889
1890 } else {
1891 // <feature-group> tags are defined, so we ignore implied features and
1892 for (size_t i = 0; i < numFeatureGroups; i++) {
1893 FeatureGroup& grp = featureGroups.editItemAt(i);
1894
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001895 if (commonFeatures.openGLESVersion > grp.openGLESVersion) {
1896 grp.openGLESVersion = commonFeatures.openGLESVersion;
1897 }
1898
Adam Lesinski2c72b682014-06-24 09:56:01 -07001899 // Merge the features defined in the top level (not inside a <feature-group>)
1900 // with this feature group.
1901 const size_t numCommonFeatures = commonFeatures.features.size();
1902 for (size_t j = 0; j < numCommonFeatures; j++) {
1903 if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001904 grp.features.add(commonFeatures.features.keyAt(j),
1905 commonFeatures.features[j]);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001906 }
1907 }
1908
1909 if (!grp.features.isEmpty()) {
1910 printFeatureGroup(grp);
Adam Lesinski282e1812014-01-23 18:17:42 -08001911 }
1912 }
1913 }
1914
Adam Lesinski282e1812014-01-23 18:17:42 -08001915
Adam Lesinski282e1812014-01-23 18:17:42 -08001916 if (hasWidgetReceivers) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001917 printComponentPresence("app-widget");
Adam Lesinski282e1812014-01-23 18:17:42 -08001918 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001919 if (hasDeviceAdminReceiver) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001920 printComponentPresence("device-admin");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001921 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001922 if (hasImeService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001923 printComponentPresence("ime");
Adam Lesinski282e1812014-01-23 18:17:42 -08001924 }
1925 if (hasWallpaperService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001926 printComponentPresence("wallpaper");
Adam Lesinski282e1812014-01-23 18:17:42 -08001927 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001928 if (hasAccessibilityService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001929 printComponentPresence("accessibility");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001930 }
1931 if (hasPrintService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001932 printComponentPresence("print-service");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001933 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001934 if (hasPaymentService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001935 printComponentPresence("payment");
1936 }
1937 if (isSearchable) {
1938 printComponentPresence("search");
1939 }
1940 if (hasDocumentsProvider) {
1941 printComponentPresence("document-provider");
1942 }
1943 if (hasLauncher) {
1944 printComponentPresence("launcher");
1945 }
1946 if (hasNotificationListenerService) {
1947 printComponentPresence("notification-listener");
1948 }
John Spurlockeb8d1be2014-06-25 17:46:15 -04001949 if (hasDreamService) {
1950 printComponentPresence("dream");
1951 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001952 if (hasCameraActivity) {
1953 printComponentPresence("camera");
1954 }
1955 if (hasCameraSecureActivity) {
1956 printComponentPresence("camera-secure");
1957 }
1958
1959 if (hasMainActivity) {
1960 printf("main\n");
Adam Lesinski94fc9122013-09-30 17:16:09 -07001961 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001962 if (hasOtherActivities) {
1963 printf("other-activities\n");
1964 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001965 if (hasOtherReceivers) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001966 printf("other-receivers\n");
1967 }
1968 if (hasOtherServices) {
1969 printf("other-services\n");
1970 }
1971
1972 // For modern apps, if screen size buckets haven't been specified
1973 // but the new width ranges have, then infer the buckets from them.
1974 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1975 && requiresSmallestWidthDp > 0) {
1976 int compatWidth = compatibleWidthLimitDp;
1977 if (compatWidth <= 0) {
1978 compatWidth = requiresSmallestWidthDp;
1979 }
1980 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1981 smallScreen = -1;
1982 } else {
1983 smallScreen = 0;
1984 }
1985 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1986 normalScreen = -1;
1987 } else {
1988 normalScreen = 0;
1989 }
1990 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1991 largeScreen = -1;
1992 } else {
1993 largeScreen = 0;
1994 }
1995 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1996 xlargeScreen = -1;
1997 } else {
1998 xlargeScreen = 0;
1999 }
2000 }
2001
2002 // Determine default values for any unspecified screen sizes,
2003 // based on the target SDK of the package. As of 4 (donut)
2004 // the screen size support was introduced, so all default to
2005 // enabled.
2006 if (smallScreen > 0) {
2007 smallScreen = targetSdk >= 4 ? -1 : 0;
2008 }
2009 if (normalScreen > 0) {
2010 normalScreen = -1;
2011 }
2012 if (largeScreen > 0) {
2013 largeScreen = targetSdk >= 4 ? -1 : 0;
2014 }
2015 if (xlargeScreen > 0) {
2016 // Introduced in Gingerbread.
2017 xlargeScreen = targetSdk >= 9 ? -1 : 0;
2018 }
2019 if (anyDensity > 0) {
2020 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
2021 || compatibleWidthLimitDp > 0) ? -1 : 0;
2022 }
2023 printf("supports-screens:");
2024 if (smallScreen != 0) {
2025 printf(" 'small'");
2026 }
2027 if (normalScreen != 0) {
2028 printf(" 'normal'");
2029 }
2030 if (largeScreen != 0) {
2031 printf(" 'large'");
2032 }
2033 if (xlargeScreen != 0) {
2034 printf(" 'xlarge'");
2035 }
2036 printf("\n");
2037 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
2038 if (requiresSmallestWidthDp > 0) {
2039 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
2040 }
2041 if (compatibleWidthLimitDp > 0) {
2042 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
2043 }
2044 if (largestWidthLimitDp > 0) {
2045 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
2046 }
2047
2048 printf("locales:");
2049 const size_t NL = locales.size();
2050 for (size_t i=0; i<NL; i++) {
2051 const char* localeStr = locales[i].string();
2052 if (localeStr == NULL || strlen(localeStr) == 0) {
2053 localeStr = "--_--";
2054 }
2055 printf(" '%s'", localeStr);
2056 }
2057 printf("\n");
2058
2059 printf("densities:");
2060 const size_t ND = densities.size();
2061 for (size_t i=0; i<ND; i++) {
2062 printf(" '%d'", densities[i]);
2063 }
2064 printf("\n");
2065
2066 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
2067 if (dir != NULL) {
2068 if (dir->getFileCount() > 0) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002069 SortedVector<String8> architectures;
Adam Lesinski282e1812014-01-23 18:17:42 -08002070 for (size_t i=0; i<dir->getFileCount(); i++) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002071 architectures.add(ResTable::normalizeForOutput(
2072 dir->getFileName(i).string()));
Adam Lesinski282e1812014-01-23 18:17:42 -08002073 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07002074
2075 bool outputAltNativeCode = false;
2076 // A multiArch package is one that contains 64-bit and
2077 // 32-bit versions of native code and expects 3rd-party
2078 // apps to load these native code libraries. Since most
2079 // 64-bit systems also support 32-bit apps, the apps
2080 // loading this multiArch package's code may be either
2081 // 32-bit or 64-bit.
2082 if (hasMultiArch) {
2083 // If this is a multiArch package, report the 64-bit
2084 // version only. Then as a separate entry, report the
2085 // rest.
2086 //
2087 // If we report the 32-bit architecture, this APK will
2088 // be installed on a 32-bit device, causing a large waste
2089 // of bandwidth and disk space. This assumes that
2090 // the developer of the multiArch package has also
2091 // made a version that is 32-bit only.
2092 String8 intel64("x86_64");
2093 String8 arm64("arm64-v8a");
2094 ssize_t index = architectures.indexOf(intel64);
2095 if (index < 0) {
2096 index = architectures.indexOf(arm64);
2097 }
2098
2099 if (index >= 0) {
2100 printf("native-code: '%s'\n", architectures[index].string());
2101 architectures.removeAt(index);
2102 outputAltNativeCode = true;
2103 }
2104 }
2105
2106 const size_t archCount = architectures.size();
2107 if (archCount > 0) {
2108 if (outputAltNativeCode) {
2109 printf("alt-");
2110 }
2111 printf("native-code:");
2112 for (size_t i = 0; i < archCount; i++) {
2113 printf(" '%s'", architectures[i].string());
2114 }
2115 printf("\n");
2116 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002117 }
2118 delete dir;
2119 }
2120 } else if (strcmp("badger", option) == 0) {
2121 printf("%s", CONSOLE_DATA);
2122 } else if (strcmp("configurations", option) == 0) {
2123 Vector<ResTable_config> configs;
2124 res.getConfigurations(&configs);
2125 const size_t N = configs.size();
2126 for (size_t i=0; i<N; i++) {
2127 printf("%s\n", configs[i].toString().string());
2128 }
2129 } else {
2130 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2131 goto bail;
2132 }
2133 }
2134
2135 result = NO_ERROR;
2136
2137bail:
2138 if (asset) {
2139 delete asset;
2140 }
2141 return (result != NO_ERROR);
2142}
2143
2144
2145/*
2146 * Handle the "add" command, which wants to add files to a new or
2147 * pre-existing archive.
2148 */
2149int doAdd(Bundle* bundle)
2150{
2151 ZipFile* zip = NULL;
2152 status_t result = UNKNOWN_ERROR;
2153 const char* zipFileName;
2154
2155 if (bundle->getUpdate()) {
2156 /* avoid confusion */
2157 fprintf(stderr, "ERROR: can't use '-u' with add\n");
2158 goto bail;
2159 }
2160
2161 if (bundle->getFileSpecCount() < 1) {
2162 fprintf(stderr, "ERROR: must specify zip file name\n");
2163 goto bail;
2164 }
2165 zipFileName = bundle->getFileSpecEntry(0);
2166
2167 if (bundle->getFileSpecCount() < 2) {
2168 fprintf(stderr, "NOTE: nothing to do\n");
2169 goto bail;
2170 }
2171
2172 zip = openReadWrite(zipFileName, true);
2173 if (zip == NULL) {
2174 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2175 goto bail;
2176 }
2177
2178 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2179 const char* fileName = bundle->getFileSpecEntry(i);
2180
2181 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
2182 printf(" '%s'... (from gzip)\n", fileName);
2183 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
2184 } else {
2185 if (bundle->getJunkPath()) {
2186 String8 storageName = String8(fileName).getPathLeaf();
Maurice Chu2675f762013-10-22 17:33:11 -07002187 printf(" '%s' as '%s'...\n", fileName,
2188 ResTable::normalizeForOutput(storageName.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08002189 result = zip->add(fileName, storageName.string(),
2190 bundle->getCompressionMethod(), NULL);
2191 } else {
2192 printf(" '%s'...\n", fileName);
2193 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2194 }
2195 }
2196 if (result != NO_ERROR) {
2197 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2198 if (result == NAME_NOT_FOUND) {
2199 fprintf(stderr, ": file not found\n");
2200 } else if (result == ALREADY_EXISTS) {
2201 fprintf(stderr, ": already exists in archive\n");
2202 } else {
2203 fprintf(stderr, "\n");
2204 }
2205 goto bail;
2206 }
2207 }
2208
2209 result = NO_ERROR;
2210
2211bail:
2212 delete zip;
2213 return (result != NO_ERROR);
2214}
2215
2216
2217/*
2218 * Delete files from an existing archive.
2219 */
2220int doRemove(Bundle* bundle)
2221{
2222 ZipFile* zip = NULL;
2223 status_t result = UNKNOWN_ERROR;
2224 const char* zipFileName;
2225
2226 if (bundle->getFileSpecCount() < 1) {
2227 fprintf(stderr, "ERROR: must specify zip file name\n");
2228 goto bail;
2229 }
2230 zipFileName = bundle->getFileSpecEntry(0);
2231
2232 if (bundle->getFileSpecCount() < 2) {
2233 fprintf(stderr, "NOTE: nothing to do\n");
2234 goto bail;
2235 }
2236
2237 zip = openReadWrite(zipFileName, false);
2238 if (zip == NULL) {
2239 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2240 zipFileName);
2241 goto bail;
2242 }
2243
2244 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2245 const char* fileName = bundle->getFileSpecEntry(i);
2246 ZipEntry* entry;
2247
2248 entry = zip->getEntryByName(fileName);
2249 if (entry == NULL) {
2250 printf(" '%s' NOT FOUND\n", fileName);
2251 continue;
2252 }
2253
2254 result = zip->remove(entry);
2255
2256 if (result != NO_ERROR) {
2257 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2258 bundle->getFileSpecEntry(i), zipFileName);
2259 goto bail;
2260 }
2261 }
2262
2263 /* update the archive */
2264 zip->flush();
2265
2266bail:
2267 delete zip;
2268 return (result != NO_ERROR);
2269}
2270
Adam Lesinski3921e872014-05-13 10:56:25 -07002271static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002272 const size_t numDirs = dir->getDirs().size();
2273 for (size_t i = 0; i < numDirs; i++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002274 bool ignore = ignoreConfig;
2275 const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2276 const char* dirStr = subDir->getLeaf().string();
2277 if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2278 ignore = true;
2279 }
2280 status_t err = addResourcesToBuilder(subDir, builder, ignore);
Adam Lesinskifab50872014-04-16 14:40:42 -07002281 if (err != NO_ERROR) {
2282 return err;
2283 }
2284 }
2285
2286 const size_t numFiles = dir->getFiles().size();
2287 for (size_t i = 0; i < numFiles; i++) {
2288 sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2289 const size_t numConfigs = gp->getFiles().size();
2290 for (size_t j = 0; j < numConfigs; j++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002291 status_t err = NO_ERROR;
2292 if (ignoreConfig) {
2293 err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2294 } else {
2295 err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2296 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002297 if (err != NO_ERROR) {
2298 fprintf(stderr, "Failed to add %s (%s) to builder.\n",
2299 gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
2300 return err;
2301 }
2302 }
2303 }
2304 return NO_ERROR;
2305}
2306
2307static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2308 if (split->isBase()) {
2309 return original;
2310 }
2311
2312 String8 ext(original.getPathExtension());
2313 if (ext == String8(".apk")) {
2314 return String8::format("%s_%s%s",
2315 original.getBasePath().string(),
2316 split->getDirectorySafeName().string(),
2317 ext.string());
2318 }
2319
2320 return String8::format("%s_%s", original.string(),
2321 split->getDirectorySafeName().string());
2322}
Adam Lesinski282e1812014-01-23 18:17:42 -08002323
2324/*
2325 * Package up an asset directory and associated application files.
2326 */
2327int doPackage(Bundle* bundle)
2328{
2329 const char* outputAPKFile;
2330 int retVal = 1;
2331 status_t err;
2332 sp<AaptAssets> assets;
2333 int N;
2334 FILE* fp;
2335 String8 dependencyFile;
Adam Lesinskifab50872014-04-16 14:40:42 -07002336 sp<ApkBuilder> builder;
Adam Lesinski282e1812014-01-23 18:17:42 -08002337
Anton Krumina2ef5c02014-03-12 14:46:44 -07002338 // -c en_XA or/and ar_XB means do pseudolocalization
Adam Lesinskifab50872014-04-16 14:40:42 -07002339 sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2340 err = configFilter->parse(bundle->getConfigurations());
Adam Lesinski282e1812014-01-23 18:17:42 -08002341 if (err != NO_ERROR) {
2342 goto bail;
2343 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002344 if (configFilter->containsPseudo()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002345 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2346 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002347 if (configFilter->containsPseudoBidi()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002348 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
Adam Lesinski282e1812014-01-23 18:17:42 -08002349 }
2350
2351 N = bundle->getFileSpecCount();
2352 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
Adam Lesinski09384302014-01-22 16:07:42 -08002353 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002354 fprintf(stderr, "ERROR: no input files\n");
2355 goto bail;
2356 }
2357
2358 outputAPKFile = bundle->getOutputAPKFile();
2359
2360 // Make sure the filenames provided exist and are of the appropriate type.
2361 if (outputAPKFile) {
2362 FileType type;
2363 type = getFileType(outputAPKFile);
2364 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2365 fprintf(stderr,
2366 "ERROR: output file '%s' exists but is not regular file\n",
2367 outputAPKFile);
2368 goto bail;
2369 }
2370 }
2371
2372 // Load the assets.
2373 assets = new AaptAssets();
2374
2375 // Set up the resource gathering in assets if we're going to generate
2376 // dependency files. Every time we encounter a resource while slurping
2377 // the tree, we'll add it to these stores so we have full resource paths
2378 // to write to a dependency file.
2379 if (bundle->getGenDependencies()) {
2380 sp<FilePathStore> resPathStore = new FilePathStore;
2381 assets->setFullResPaths(resPathStore);
2382 sp<FilePathStore> assetPathStore = new FilePathStore;
2383 assets->setFullAssetPaths(assetPathStore);
2384 }
2385
2386 err = assets->slurpFromArgs(bundle);
2387 if (err < 0) {
2388 goto bail;
2389 }
2390
2391 if (bundle->getVerbose()) {
2392 assets->print(String8());
2393 }
2394
Adam Lesinskifab50872014-04-16 14:40:42 -07002395 // Create the ApkBuilder, which will collect the compiled files
2396 // to write to the final APK (or sets of APKs if we are building
2397 // a Split APK.
2398 builder = new ApkBuilder(configFilter);
2399
2400 // If we are generating a Split APK, find out which configurations to split on.
2401 if (bundle->getSplitConfigurations().size() > 0) {
2402 const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2403 const size_t numSplits = splitStrs.size();
2404 for (size_t i = 0; i < numSplits; i++) {
2405 std::set<ConfigDescription> configs;
2406 if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2407 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2408 goto bail;
2409 }
2410
2411 err = builder->createSplitForConfigs(configs);
2412 if (err != NO_ERROR) {
2413 goto bail;
2414 }
2415 }
2416 }
2417
Adam Lesinski282e1812014-01-23 18:17:42 -08002418 // If they asked for any fileAs that need to be compiled, do so.
2419 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002420 err = buildResources(bundle, assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002421 if (err != 0) {
2422 goto bail;
2423 }
2424 }
2425
2426 // At this point we've read everything and processed everything. From here
2427 // on out it's just writing output files.
2428 if (SourcePos::hasErrors()) {
2429 goto bail;
2430 }
2431
2432 // Update symbols with information about which ones are needed as Java symbols.
2433 assets->applyJavaSymbols();
2434 if (SourcePos::hasErrors()) {
2435 goto bail;
2436 }
2437
2438 // If we've been asked to generate a dependency file, do that here
2439 if (bundle->getGenDependencies()) {
2440 // If this is the packaging step, generate the dependency file next to
2441 // the output apk (e.g. bin/resources.ap_.d)
2442 if (outputAPKFile) {
2443 dependencyFile = String8(outputAPKFile);
2444 // Add the .d extension to the dependency file.
2445 dependencyFile.append(".d");
2446 } else {
2447 // Else if this is the R.java dependency generation step,
2448 // generate the dependency file in the R.java package subdirectory
2449 // e.g. gen/com/foo/app/R.java.d
2450 dependencyFile = String8(bundle->getRClassDir());
2451 dependencyFile.appendPath("R.java.d");
2452 }
2453 // Make sure we have a clean dependency file to start with
2454 fp = fopen(dependencyFile, "w");
2455 fclose(fp);
2456 }
2457
2458 // Write out R.java constants
2459 if (!assets->havePrivateSymbols()) {
2460 if (bundle->getCustomPackage() == NULL) {
2461 // Write the R.java file into the appropriate class directory
2462 // e.g. gen/com/foo/app/R.java
2463 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
2464 } else {
2465 const String8 customPkg(bundle->getCustomPackage());
2466 err = writeResourceSymbols(bundle, assets, customPkg, true);
2467 }
2468 if (err < 0) {
2469 goto bail;
2470 }
2471 // If we have library files, we're going to write our R.java file into
2472 // the appropriate class directory for those libraries as well.
2473 // e.g. gen/com/foo/app/lib/R.java
2474 if (bundle->getExtraPackages() != NULL) {
2475 // Split on colon
2476 String8 libs(bundle->getExtraPackages());
2477 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2478 while (packageString != NULL) {
2479 // Write the R.java file out with the correct package name
2480 err = writeResourceSymbols(bundle, assets, String8(packageString), true);
2481 if (err < 0) {
2482 goto bail;
2483 }
2484 packageString = strtok(NULL, ":");
2485 }
2486 libs.unlockBuffer();
2487 }
2488 } else {
2489 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
2490 if (err < 0) {
2491 goto bail;
2492 }
2493 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
2494 if (err < 0) {
2495 goto bail;
2496 }
2497 }
2498
2499 // Write out the ProGuard file
2500 err = writeProguardFile(bundle, assets);
2501 if (err < 0) {
2502 goto bail;
2503 }
2504
2505 // Write the apk
2506 if (outputAPKFile) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002507 // Gather all resources and add them to the APK Builder. The builder will then
2508 // figure out which Split they belong in.
2509 err = addResourcesToBuilder(assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002510 if (err != NO_ERROR) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002511 goto bail;
2512 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002513
2514 const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2515 const size_t numSplits = splits.size();
2516 for (size_t i = 0; i < numSplits; i++) {
2517 const sp<ApkSplit>& split = splits[i];
2518 String8 outputPath = buildApkName(String8(outputAPKFile), split);
2519 err = writeAPK(bundle, outputPath, split);
2520 if (err != NO_ERROR) {
2521 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2522 goto bail;
2523 }
2524 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002525 }
2526
2527 // If we've been asked to generate a dependency file, we need to finish up here.
2528 // the writeResourceSymbols and writeAPK functions have already written the target
2529 // half of the dependency file, now we need to write the prerequisites. (files that
2530 // the R.java file or .ap_ file depend on)
2531 if (bundle->getGenDependencies()) {
2532 // Now that writeResourceSymbols or writeAPK has taken care of writing
2533 // the targets to our dependency file, we'll write the prereqs
2534 fp = fopen(dependencyFile, "a+");
2535 fprintf(fp, " : ");
2536 bool includeRaw = (outputAPKFile != NULL);
2537 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2538 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2539 // and therefore was not added to our pathstores during slurping
2540 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2541 fclose(fp);
2542 }
2543
2544 retVal = 0;
2545bail:
2546 if (SourcePos::hasErrors()) {
2547 SourcePos::printErrors(stderr);
2548 }
2549 return retVal;
2550}
2551
2552/*
2553 * Do PNG Crunching
2554 * PRECONDITIONS
2555 * -S flag points to a source directory containing drawable* folders
2556 * -C flag points to destination directory. The folder structure in the
2557 * source directory will be mirrored to the destination (cache) directory
2558 *
2559 * POSTCONDITIONS
2560 * Destination directory will be updated to match the PNG files in
Maurice Chu2675f762013-10-22 17:33:11 -07002561 * the source directory.
Adam Lesinski282e1812014-01-23 18:17:42 -08002562 */
2563int doCrunch(Bundle* bundle)
2564{
2565 fprintf(stdout, "Crunching PNG Files in ");
2566 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2567 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2568
2569 updatePreProcessedCache(bundle);
2570
2571 return NO_ERROR;
2572}
2573
2574/*
2575 * Do PNG Crunching on a single flag
2576 * -i points to a single png file
2577 * -o points to a single png output file
2578 */
2579int doSingleCrunch(Bundle* bundle)
2580{
2581 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2582 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2583
2584 String8 input(bundle->getSingleCrunchInputFile());
2585 String8 output(bundle->getSingleCrunchOutputFile());
2586
2587 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2588 // we can't return the status_t as it gets truncate to the lower 8 bits.
2589 return 42;
2590 }
2591
2592 return NO_ERROR;
2593}
2594
2595char CONSOLE_DATA[2925] = {
2596 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2597 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2598 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2599 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2600 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2601 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2602 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2603 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2604 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2605 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2606 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2607 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2608 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2609 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2610 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2611 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2612 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2613 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2614 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2615 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2616 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2617 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2618 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2619 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2620 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2621 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2622 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2623 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2624 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2625 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2626 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2627 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2628 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2629 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2630 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2631 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2632 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2633 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2634 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2635 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2636 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2637 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2638 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2639 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2640 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2641 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2642 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2643 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2644 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2645 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2646 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2647 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2648 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2649 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2650 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2651 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2652 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2653 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2654 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2655 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2656 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2657 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2658 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2659 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2660 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2661 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2662 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2663 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2664 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2665 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2666 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2667 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2668 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2669 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2670 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2671 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2672 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2673 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2674 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2675 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2676 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2677 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2678 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2679 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2680 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2681 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2682 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2683 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2684 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2685 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2686 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2687 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2688 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2689 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2690 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2691 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2692 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2693 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2694 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2695 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2696 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2697 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2698 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2699 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2700 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2701 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2702 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2703 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2704 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2705 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2706 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2707 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2708 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2709 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2710 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2711 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2712 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2713 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2714 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2715 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2716 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2717 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2718 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2719 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2720 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2721 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2722 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2723 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2724 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2725 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2726 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2727 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2728 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2729 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2730 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2731 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2732 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2733 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2734 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2735 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2736 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2737 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2738 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2739 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2740 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2741 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2742 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2743 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2744 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2745 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2746 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2747 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2748 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2749 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2750 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2751 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2752 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2753 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2754 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2755 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2756 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2757 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2758 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2759 };