blob: 5d146d65bded410ec6a6178459f1d1f439e163d1 [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
27/*
28 * Show version info. All the cool kids do it.
29 */
30int doVersion(Bundle* bundle)
31{
32 if (bundle->getFileSpecCount() != 0) {
33 printf("(ignoring extra arguments)\n");
34 }
35 printf("Android Asset Packaging Tool, v0.2\n");
36
37 return 0;
38}
39
40
41/*
42 * Open the file read only. The call fails if the file doesn't exist.
43 *
44 * Returns NULL on failure.
45 */
46ZipFile* openReadOnly(const char* fileName)
47{
48 ZipFile* zip;
49 status_t result;
50
51 zip = new ZipFile;
52 result = zip->open(fileName, ZipFile::kOpenReadOnly);
53 if (result != NO_ERROR) {
54 if (result == NAME_NOT_FOUND) {
55 fprintf(stderr, "ERROR: '%s' not found\n", fileName);
56 } else if (result == PERMISSION_DENIED) {
57 fprintf(stderr, "ERROR: '%s' access denied\n", fileName);
58 } else {
59 fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n",
60 fileName);
61 }
62 delete zip;
63 return NULL;
64 }
65
66 return zip;
67}
68
69/*
70 * Open the file read-write. The file will be created if it doesn't
71 * already exist and "okayToCreate" is set.
72 *
73 * Returns NULL on failure.
74 */
75ZipFile* openReadWrite(const char* fileName, bool okayToCreate)
76{
77 ZipFile* zip = NULL;
78 status_t result;
79 int flags;
80
81 flags = ZipFile::kOpenReadWrite;
82 if (okayToCreate) {
83 flags |= ZipFile::kOpenCreate;
84 }
85
86 zip = new ZipFile;
87 result = zip->open(fileName, flags);
88 if (result != NO_ERROR) {
89 delete zip;
90 zip = NULL;
91 goto bail;
92 }
93
94bail:
95 return zip;
96}
97
98
99/*
100 * Return a short string describing the compression method.
101 */
102const char* compressionName(int method)
103{
104 if (method == ZipEntry::kCompressStored) {
105 return "Stored";
106 } else if (method == ZipEntry::kCompressDeflated) {
107 return "Deflated";
108 } else {
109 return "Unknown";
110 }
111}
112
113/*
114 * Return the percent reduction in size (0% == no compression).
115 */
116int calcPercent(long uncompressedLen, long compressedLen)
117{
118 if (!uncompressedLen) {
119 return 0;
120 } else {
121 return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5);
122 }
123}
124
125/*
126 * Handle the "list" command, which can be a simple file dump or
127 * a verbose listing.
128 *
129 * The verbose listing closely matches the output of the Info-ZIP "unzip"
130 * command.
131 */
132int doList(Bundle* bundle)
133{
134 int result = 1;
135 ZipFile* zip = NULL;
136 const ZipEntry* entry;
137 long totalUncLen, totalCompLen;
138 const char* zipFileName;
139
140 if (bundle->getFileSpecCount() != 1) {
141 fprintf(stderr, "ERROR: specify zip file name (only)\n");
142 goto bail;
143 }
144 zipFileName = bundle->getFileSpecEntry(0);
145
146 zip = openReadOnly(zipFileName);
147 if (zip == NULL) {
148 goto bail;
149 }
150
151 int count, i;
152
153 if (bundle->getVerbose()) {
154 printf("Archive: %s\n", zipFileName);
155 printf(
156 " Length Method Size Ratio Offset Date Time CRC-32 Name\n");
157 printf(
158 "-------- ------ ------- ----- ------- ---- ---- ------ ----\n");
159 }
160
161 totalUncLen = totalCompLen = 0;
162
163 count = zip->getNumEntries();
164 for (i = 0; i < count; i++) {
165 entry = zip->getEntryByIndex(i);
166 if (bundle->getVerbose()) {
167 char dateBuf[32];
168 time_t when;
169
170 when = entry->getModWhen();
171 strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M",
172 localtime(&when));
173
174 printf("%8ld %-7.7s %7ld %3d%% %8zd %s %08lx %s\n",
175 (long) entry->getUncompressedLen(),
176 compressionName(entry->getCompressionMethod()),
177 (long) entry->getCompressedLen(),
178 calcPercent(entry->getUncompressedLen(),
179 entry->getCompressedLen()),
180 (size_t) entry->getLFHOffset(),
181 dateBuf,
182 entry->getCRC32(),
183 entry->getFileName());
184 } else {
185 printf("%s\n", entry->getFileName());
186 }
187
188 totalUncLen += entry->getUncompressedLen();
189 totalCompLen += entry->getCompressedLen();
190 }
191
192 if (bundle->getVerbose()) {
193 printf(
194 "-------- ------- --- -------\n");
195 printf("%8ld %7ld %2d%% %d files\n",
196 totalUncLen,
197 totalCompLen,
198 calcPercent(totalUncLen, totalCompLen),
199 zip->getNumEntries());
200 }
201
202 if (bundle->getAndroidList()) {
203 AssetManager assets;
204 if (!assets.addAssetPath(String8(zipFileName), NULL)) {
205 fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n");
206 goto bail;
207 }
208
209 const ResTable& res = assets.getResources(false);
210 if (&res == NULL) {
211 printf("\nNo resource table found.\n");
212 } else {
213#ifndef HAVE_ANDROID_OS
214 printf("\nResource table:\n");
215 res.print(false);
216#endif
217 }
218
219 Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
220 Asset::ACCESS_BUFFER);
221 if (manifestAsset == NULL) {
222 printf("\nNo AndroidManifest.xml found.\n");
223 } else {
224 printf("\nAndroid manifest:\n");
225 ResXMLTree tree;
226 tree.setTo(manifestAsset->getBuffer(true),
227 manifestAsset->getLength());
228 printXMLBlock(&tree);
229 }
230 delete manifestAsset;
231 }
232
233 result = 0;
234
235bail:
236 delete zip;
237 return result;
238}
239
240static ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes)
241{
242 size_t N = tree.getAttributeCount();
243 for (size_t i=0; i<N; i++) {
244 if (tree.getAttributeNameResID(i) == attrRes) {
245 return (ssize_t)i;
246 }
247 }
248 return -1;
249}
250
251String8 getAttribute(const ResXMLTree& tree, const char* ns,
252 const char* attr, String8* outError)
253{
254 ssize_t idx = tree.indexOfAttribute(ns, attr);
255 if (idx < 0) {
256 return String8();
257 }
258 Res_value value;
259 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
260 if (value.dataType != Res_value::TYPE_STRING) {
261 if (outError != NULL) {
262 *outError = "attribute is not a string value";
263 }
264 return String8();
265 }
266 }
267 size_t len;
268 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
269 return str ? String8(str, len) : String8();
270}
271
272static String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError)
273{
274 ssize_t idx = indexOfAttribute(tree, attrRes);
275 if (idx < 0) {
276 return String8();
277 }
278 Res_value value;
279 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
280 if (value.dataType != Res_value::TYPE_STRING) {
281 if (outError != NULL) {
282 *outError = "attribute is not a string value";
283 }
284 return String8();
285 }
286 }
287 size_t len;
288 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
289 return str ? String8(str, len) : String8();
290}
291
292static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes,
293 String8* outError, int32_t defValue = -1)
294{
295 ssize_t idx = indexOfAttribute(tree, attrRes);
296 if (idx < 0) {
297 return defValue;
298 }
299 Res_value value;
300 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
301 if (value.dataType < Res_value::TYPE_FIRST_INT
302 || value.dataType > Res_value::TYPE_LAST_INT) {
303 if (outError != NULL) {
304 *outError = "attribute is not an integer value";
305 }
306 return defValue;
307 }
308 }
309 return value.data;
310}
311
312static int32_t getResolvedIntegerAttribute(const ResTable* resTable, const ResXMLTree& tree,
313 uint32_t attrRes, String8* outError, int32_t defValue = -1)
314{
315 ssize_t idx = indexOfAttribute(tree, attrRes);
316 if (idx < 0) {
317 return defValue;
318 }
319 Res_value value;
320 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
321 if (value.dataType == Res_value::TYPE_REFERENCE) {
322 resTable->resolveReference(&value, 0);
323 }
324 if (value.dataType < Res_value::TYPE_FIRST_INT
325 || value.dataType > Res_value::TYPE_LAST_INT) {
326 if (outError != NULL) {
327 *outError = "attribute is not an integer value";
328 }
329 return defValue;
330 }
331 }
332 return value.data;
333}
334
335static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree,
336 uint32_t attrRes, String8* outError)
337{
338 ssize_t idx = indexOfAttribute(tree, attrRes);
339 if (idx < 0) {
340 return String8();
341 }
342 Res_value value;
343 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
344 if (value.dataType == Res_value::TYPE_STRING) {
345 size_t len;
346 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
347 return str ? String8(str, len) : String8();
348 }
349 resTable->resolveReference(&value, 0);
350 if (value.dataType != Res_value::TYPE_STRING) {
351 if (outError != NULL) {
352 *outError = "attribute is not a string value";
353 }
354 return String8();
355 }
356 }
357 size_t len;
358 const Res_value* value2 = &value;
359 const char16_t* str = const_cast<ResTable*>(resTable)->valueToString(value2, 0, NULL, &len);
360 return str ? String8(str, len) : String8();
361}
362
363static void getResolvedResourceAttribute(Res_value* value, const ResTable* resTable,
364 const ResXMLTree& tree, uint32_t attrRes, String8* outError)
365{
366 ssize_t idx = indexOfAttribute(tree, attrRes);
367 if (idx < 0) {
368 if (outError != NULL) {
369 *outError = "attribute could not be found";
370 }
371 return;
372 }
373 if (tree.getAttributeValue(idx, value) != NO_ERROR) {
374 if (value->dataType == Res_value::TYPE_REFERENCE) {
375 resTable->resolveReference(value, 0);
376 }
377 // The attribute was found and was resolved if need be.
378 return;
379 }
380 if (outError != NULL) {
381 *outError = "error getting resolved resource attribute";
382 }
383}
384
Maurice Chu76327312013-10-16 18:28:46 -0700385static void printResolvedResourceAttribute(const ResTable* resTable, const ResXMLTree& tree,
386 uint32_t attrRes, String8 attrLabel, String8* outError)
387{
388 Res_value value;
389 getResolvedResourceAttribute(&value, resTable, tree, attrRes, outError);
390 if (*outError != "") {
391 *outError = "error print resolved resource attribute";
392 return;
393 }
394 if (value.dataType == Res_value::TYPE_STRING) {
395 String8 result = getResolvedAttribute(resTable, tree, attrRes, outError);
Maurice Chu2675f762013-10-22 17:33:11 -0700396 printf("%s='%s'", attrLabel.string(),
397 ResTable::normalizeForOutput(result.string()).string());
Maurice Chu76327312013-10-16 18:28:46 -0700398 } else if (Res_value::TYPE_FIRST_INT <= value.dataType &&
399 value.dataType <= Res_value::TYPE_LAST_INT) {
400 printf("%s='%d'", attrLabel.string(), value.data);
401 } else {
402 printf("%s='0x%x'", attrLabel.string(), (int)value.data);
403 }
404}
405
Adam Lesinski282e1812014-01-23 18:17:42 -0800406// These are attribute resource constants for the platform, as found
407// in android.R.attr
408enum {
409 LABEL_ATTR = 0x01010001,
410 ICON_ATTR = 0x01010002,
411 NAME_ATTR = 0x01010003,
Adam Lesinskia5018c92013-09-30 16:23:15 -0700412 PERMISSION_ATTR = 0x01010006,
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700413 EXPORTED_ATTR = 0x01010010,
414 GRANT_URI_PERMISSIONS_ATTR = 0x0101001b,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700415 RESOURCE_ATTR = 0x01010025,
Adam Lesinski282e1812014-01-23 18:17:42 -0800416 DEBUGGABLE_ATTR = 0x0101000f,
417 VALUE_ATTR = 0x01010024,
418 VERSION_CODE_ATTR = 0x0101021b,
419 VERSION_NAME_ATTR = 0x0101021c,
420 SCREEN_ORIENTATION_ATTR = 0x0101001e,
421 MIN_SDK_VERSION_ATTR = 0x0101020c,
422 MAX_SDK_VERSION_ATTR = 0x01010271,
423 REQ_TOUCH_SCREEN_ATTR = 0x01010227,
424 REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
425 REQ_HARD_KEYBOARD_ATTR = 0x01010229,
426 REQ_NAVIGATION_ATTR = 0x0101022a,
427 REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
428 TARGET_SDK_VERSION_ATTR = 0x01010270,
429 TEST_ONLY_ATTR = 0x01010272,
430 ANY_DENSITY_ATTR = 0x0101026c,
431 GL_ES_VERSION_ATTR = 0x01010281,
432 SMALL_SCREEN_ATTR = 0x01010284,
433 NORMAL_SCREEN_ATTR = 0x01010285,
434 LARGE_SCREEN_ATTR = 0x01010286,
435 XLARGE_SCREEN_ATTR = 0x010102bf,
436 REQUIRED_ATTR = 0x0101028e,
437 SCREEN_SIZE_ATTR = 0x010102ca,
438 SCREEN_DENSITY_ATTR = 0x010102cb,
439 REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
440 COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
441 LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
442 PUBLIC_KEY_ATTR = 0x010103a6,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700443 CATEGORY_ATTR = 0x010103e8,
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800444 BANNER_ATTR = 0x10103f2,
Adam Lesinski282e1812014-01-23 18:17:42 -0800445};
446
Maurice Chu2675f762013-10-22 17:33:11 -0700447String8 getComponentName(String8 &pkgName, String8 &componentName) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800448 ssize_t idx = componentName.find(".");
449 String8 retStr(pkgName);
450 if (idx == 0) {
451 retStr += componentName;
452 } else if (idx < 0) {
453 retStr += ".";
454 retStr += componentName;
455 } else {
Maurice Chu2675f762013-10-22 17:33:11 -0700456 return componentName;
Adam Lesinski282e1812014-01-23 18:17:42 -0800457 }
Maurice Chu2675f762013-10-22 17:33:11 -0700458 return retStr;
Adam Lesinski282e1812014-01-23 18:17:42 -0800459}
460
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700461static void printCompatibleScreens(ResXMLTree& tree, String8* outError) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800462 size_t len;
463 ResXMLTree::event_code_t code;
464 int depth = 0;
465 bool first = true;
466 printf("compatible-screens:");
467 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
468 if (code == ResXMLTree::END_TAG) {
469 depth--;
470 if (depth < 0) {
471 break;
472 }
473 continue;
474 }
475 if (code != ResXMLTree::START_TAG) {
476 continue;
477 }
478 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700479 const char16_t* ctag16 = tree.getElementName(&len);
480 if (ctag16 == NULL) {
481 *outError = "failed to get XML element name (bad string pool)";
482 return;
483 }
484 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800485 if (tag == "screen") {
486 int32_t screenSize = getIntegerAttribute(tree,
487 SCREEN_SIZE_ATTR, NULL, -1);
488 int32_t screenDensity = getIntegerAttribute(tree,
489 SCREEN_DENSITY_ATTR, NULL, -1);
490 if (screenSize > 0 && screenDensity > 0) {
491 if (!first) {
492 printf(",");
493 }
494 first = false;
495 printf("'%d/%d'", screenSize, screenDensity);
496 }
497 }
498 }
499 printf("\n");
500}
501
Adam Lesinski58f1f362013-11-12 12:59:08 -0800502static void printUsesPermission(const String8& name, bool optional=false, int maxSdkVersion=-1) {
503 printf("uses-permission: name='%s'", ResTable::normalizeForOutput(name.string()).string());
504 if (maxSdkVersion != -1) {
505 printf(" maxSdkVersion='%d'", maxSdkVersion);
506 }
507 printf("\n");
508
509 if (optional) {
510 printf("optional-permission: name='%s'",
511 ResTable::normalizeForOutput(name.string()).string());
512 if (maxSdkVersion != -1) {
513 printf(" maxSdkVersion='%d'", maxSdkVersion);
514 }
515 printf("\n");
516 }
517}
518
519static void printUsesImpliedPermission(const String8& name, const String8& reason) {
520 printf("uses-implied-permission: name='%s' reason='%s'\n",
521 ResTable::normalizeForOutput(name.string()).string(),
522 ResTable::normalizeForOutput(reason.string()).string());
523}
524
Adam Lesinski94fc9122013-09-30 17:16:09 -0700525Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool offHost,
526 String8 *outError = NULL)
527{
528 Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER);
529 if (aidAsset == NULL) {
530 if (outError != NULL) *outError = "xml resource does not exist";
531 return Vector<String8>();
532 }
533
534 const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service");
535
536 bool withinApduService = false;
537 Vector<String8> categories;
538
539 String8 error;
540 ResXMLTree tree;
541 tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength());
542
543 size_t len;
544 int depth = 0;
545 ResXMLTree::event_code_t code;
546 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
547 if (code == ResXMLTree::END_TAG) {
548 depth--;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700549 const char16_t* ctag16 = tree.getElementName(&len);
550 if (ctag16 == NULL) {
551 *outError = "failed to get XML element name (bad string pool)";
552 return Vector<String8>();
553 }
554 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700555
556 if (depth == 0 && tag == serviceTagName) {
557 withinApduService = false;
558 }
559
560 } else if (code == ResXMLTree::START_TAG) {
561 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700562 const char16_t* ctag16 = tree.getElementName(&len);
563 if (ctag16 == NULL) {
564 *outError = "failed to get XML element name (bad string pool)";
565 return Vector<String8>();
566 }
567 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700568
569 if (depth == 1) {
570 if (tag == serviceTagName) {
571 withinApduService = true;
572 }
573 } else if (depth == 2 && withinApduService) {
574 if (tag == "aid-group") {
575 String8 category = getAttribute(tree, CATEGORY_ATTR, &error);
576 if (error != "") {
577 if (outError != NULL) *outError = error;
578 return Vector<String8>();
579 }
580
581 categories.add(category);
582 }
583 }
584 }
585 }
586 aidAsset->close();
587 return categories;
588}
589
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700590static void printComponentPresence(const char* componentName) {
591 printf("provides-component:'%s'\n", componentName);
592}
593
Adam Lesinski2c72b682014-06-24 09:56:01 -0700594/**
595 * Represents a feature that has been automatically added due to
596 * a pre-requisite or some other reason.
597 */
598struct ImpliedFeature {
599 /**
600 * Name of the implied feature.
601 */
602 String8 name;
603
604 /**
605 * List of human-readable reasons for why this feature was implied.
606 */
607 SortedVector<String8> reasons;
608};
609
610/**
611 * Represents a <feature-group> tag in the AndroidManifest.xml
612 */
613struct FeatureGroup {
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700614 FeatureGroup() : openGLESVersion(-1) {}
615
Adam Lesinski2c72b682014-06-24 09:56:01 -0700616 /**
617 * Human readable label
618 */
619 String8 label;
620
621 /**
622 * Explicit features defined in the group
623 */
624 KeyedVector<String8, bool> features;
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700625
626 /**
627 * OpenGL ES version required
628 */
629 int openGLESVersion;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700630};
631
632static void addImpliedFeature(KeyedVector<String8, ImpliedFeature>* impliedFeatures,
633 const char* name, const char* reason) {
634 String8 name8(name);
635 ssize_t idx = impliedFeatures->indexOfKey(name8);
636 if (idx < 0) {
637 idx = impliedFeatures->add(name8, ImpliedFeature());
638 impliedFeatures->editValueAt(idx).name = name8;
639 }
640 impliedFeatures->editValueAt(idx).reasons.add(String8(reason));
641}
642
643static void printFeatureGroup(const FeatureGroup& grp,
644 const KeyedVector<String8, ImpliedFeature>* impliedFeatures = NULL) {
645 printf("feature-group: label='%s'\n", grp.label.string());
646
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700647 if (grp.openGLESVersion > 0) {
648 printf(" uses-gl-es: '0x%x'\n", grp.openGLESVersion);
649 }
650
Adam Lesinski2c72b682014-06-24 09:56:01 -0700651 const size_t numFeatures = grp.features.size();
652 for (size_t i = 0; i < numFeatures; i++) {
653 if (!grp.features[i]) {
654 continue;
655 }
656
657 const String8& featureName = grp.features.keyAt(i);
658 printf(" uses-feature: name='%s'\n",
659 ResTable::normalizeForOutput(featureName.string()).string());
660 }
661
662 const size_t numImpliedFeatures =
663 (impliedFeatures != NULL) ? impliedFeatures->size() : 0;
664 for (size_t i = 0; i < numImpliedFeatures; i++) {
665 const ImpliedFeature& impliedFeature = impliedFeatures->valueAt(i);
666 if (grp.features.indexOfKey(impliedFeature.name) >= 0) {
667 // The feature is explicitly set, no need to use implied
668 // definition.
669 continue;
670 }
671
672 String8 printableFeatureName(ResTable::normalizeForOutput(
673 impliedFeature.name.string()));
674 printf(" uses-feature: name='%s'\n", printableFeatureName.string());
675 printf(" uses-implied-feature: name='%s' reason='",
676 printableFeatureName.string());
677 const size_t numReasons = impliedFeature.reasons.size();
678 for (size_t j = 0; j < numReasons; j++) {
679 printf("%s", impliedFeature.reasons[j].string());
680 if (j + 2 < numReasons) {
681 printf(", ");
682 } else if (j + 1 < numReasons) {
683 printf(", and ");
684 }
685 }
686 printf("'\n");
687 }
688}
689
690static void addParentFeatures(FeatureGroup* grp, const String8& name) {
691 if (name == "android.hardware.camera.autofocus" ||
692 name == "android.hardware.camera.flash") {
693 grp->features.add(String8("android.hardware.camera"), true);
694 } else if (name == "android.hardware.location.gps" ||
695 name == "android.hardware.location.network") {
696 grp->features.add(String8("android.hardware.location"), true);
697 } else if (name == "android.hardware.touchscreen.multitouch") {
698 grp->features.add(String8("android.hardware.touchscreen"), true);
699 } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
700 grp->features.add(String8("android.hardware.touchscreen.multitouch"), true);
701 grp->features.add(String8("android.hardware.touchscreen"), true);
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700702 } else if (name == "android.hardware.opengles.aep") {
703 const int openGLESVersion31 = 0x00030001;
704 if (openGLESVersion31 > grp->openGLESVersion) {
705 grp->openGLESVersion = openGLESVersion31;
706 }
Adam Lesinski2c72b682014-06-24 09:56:01 -0700707 }
708}
709
Adam Lesinski282e1812014-01-23 18:17:42 -0800710/*
711 * Handle the "dump" command, to extract select data from an archive.
712 */
713extern char CONSOLE_DATA[2925]; // see EOF
714int doDump(Bundle* bundle)
715{
716 status_t result = UNKNOWN_ERROR;
Adam Lesinski282e1812014-01-23 18:17:42 -0800717
718 if (bundle->getFileSpecCount() < 1) {
719 fprintf(stderr, "ERROR: no dump option specified\n");
720 return 1;
721 }
722
723 if (bundle->getFileSpecCount() < 2) {
724 fprintf(stderr, "ERROR: no dump file specified\n");
725 return 1;
726 }
727
728 const char* option = bundle->getFileSpecEntry(0);
729 const char* filename = bundle->getFileSpecEntry(1);
730
731 AssetManager assets;
Narayan Kamathf85e41f2014-01-24 14:14:30 +0000732 int32_t assetsCookie;
Adam Lesinski282e1812014-01-23 18:17:42 -0800733 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
734 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
735 return 1;
736 }
737
738 // Make a dummy config for retrieving resources... we need to supply
739 // non-default values for some configs so that we can retrieve resources
740 // in the app that don't have a default. The most important of these is
741 // the API version because key resources like icons will have an implicit
742 // version if they are using newer config types like density.
743 ResTable_config config;
Narayan Kamath91447d82014-01-21 15:32:36 +0000744 memset(&config, 0, sizeof(ResTable_config));
Adam Lesinski282e1812014-01-23 18:17:42 -0800745 config.language[0] = 'e';
746 config.language[1] = 'n';
747 config.country[0] = 'U';
748 config.country[1] = 'S';
749 config.orientation = ResTable_config::ORIENTATION_PORT;
750 config.density = ResTable_config::DENSITY_MEDIUM;
751 config.sdkVersion = 10000; // Very high.
752 config.screenWidthDp = 320;
753 config.screenHeightDp = 480;
754 config.smallestScreenWidthDp = 320;
Adam Lesinskic2dea8d2014-08-04 16:40:41 -0700755 config.screenLayout |= ResTable_config::SCREENSIZE_NORMAL;
Adam Lesinski282e1812014-01-23 18:17:42 -0800756 assets.setConfiguration(config);
757
758 const ResTable& res = assets.getResources(false);
759 if (&res == NULL) {
760 fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
Adam Lesinski63e646e2014-07-30 11:40:39 -0700761 return 1;
Adam Lesinski25e9d552014-05-19 15:01:43 -0700762 } else if (res.getError() != NO_ERROR) {
763 fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
Adam Lesinski63e646e2014-07-30 11:40:39 -0700764 return 1;
Adam Lesinski282e1812014-01-23 18:17:42 -0800765 }
766
Adam Lesinski2cb761e2014-08-15 13:59:02 -0700767 // The dynamicRefTable can be null if there are no resources for this asset cookie.
768 // This fine.
Adam Lesinski63e646e2014-07-30 11:40:39 -0700769 const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700770
771 Asset* asset = NULL;
772
Adam Lesinski282e1812014-01-23 18:17:42 -0800773 if (strcmp("resources", option) == 0) {
774#ifndef HAVE_ANDROID_OS
775 res.print(bundle->getValues());
776#endif
777
778 } else if (strcmp("strings", option) == 0) {
779 const ResStringPool* pool = res.getTableStringBlock(0);
780 printStringPool(pool);
781
782 } else if (strcmp("xmltree", option) == 0) {
783 if (bundle->getFileSpecCount() < 3) {
784 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
785 goto bail;
786 }
787
788 for (int i=2; i<bundle->getFileSpecCount(); i++) {
789 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700790 ResXMLTree tree(dynamicRefTable);
791 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800792 if (asset == NULL) {
793 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
794 goto bail;
795 }
796
797 if (tree.setTo(asset->getBuffer(true),
798 asset->getLength()) != NO_ERROR) {
799 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
800 goto bail;
801 }
802 tree.restart();
803 printXMLBlock(&tree);
804 tree.uninit();
805 delete asset;
806 asset = NULL;
807 }
808
809 } else if (strcmp("xmlstrings", option) == 0) {
810 if (bundle->getFileSpecCount() < 3) {
811 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
812 goto bail;
813 }
814
815 for (int i=2; i<bundle->getFileSpecCount(); i++) {
816 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700817 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800818 if (asset == NULL) {
819 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
820 goto bail;
821 }
822
Adam Lesinski63e646e2014-07-30 11:40:39 -0700823 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800824 if (tree.setTo(asset->getBuffer(true),
825 asset->getLength()) != NO_ERROR) {
826 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
827 goto bail;
828 }
829 printStringPool(&tree.getStrings());
830 delete asset;
831 asset = NULL;
832 }
833
834 } else {
Adam Lesinski63e646e2014-07-30 11:40:39 -0700835 asset = assets.openNonAsset(assetsCookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800836 if (asset == NULL) {
837 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
838 goto bail;
839 }
840
Adam Lesinski63e646e2014-07-30 11:40:39 -0700841 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800842 if (tree.setTo(asset->getBuffer(true),
843 asset->getLength()) != NO_ERROR) {
844 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
845 goto bail;
846 }
847 tree.restart();
848
849 if (strcmp("permissions", option) == 0) {
850 size_t len;
851 ResXMLTree::event_code_t code;
852 int depth = 0;
853 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
854 if (code == ResXMLTree::END_TAG) {
855 depth--;
856 continue;
857 }
858 if (code != ResXMLTree::START_TAG) {
859 continue;
860 }
861 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700862 const char16_t* ctag16 = tree.getElementName(&len);
863 if (ctag16 == NULL) {
864 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
865 goto bail;
866 }
867 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800868 //printf("Depth %d tag %s\n", depth, tag.string());
869 if (depth == 1) {
870 if (tag != "manifest") {
871 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
872 goto bail;
873 }
874 String8 pkg = getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -0700875 printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800876 } else if (depth == 2 && tag == "permission") {
877 String8 error;
878 String8 name = getAttribute(tree, NAME_ATTR, &error);
879 if (error != "") {
880 fprintf(stderr, "ERROR: %s\n", error.string());
881 goto bail;
882 }
Maurice Chu2675f762013-10-22 17:33:11 -0700883 printf("permission: %s\n",
884 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800885 } else if (depth == 2 && tag == "uses-permission") {
886 String8 error;
887 String8 name = getAttribute(tree, NAME_ATTR, &error);
888 if (error != "") {
889 fprintf(stderr, "ERROR: %s\n", error.string());
890 goto bail;
891 }
Adam Lesinski58f1f362013-11-12 12:59:08 -0800892 printUsesPermission(name,
893 getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1) == 0,
894 getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1));
Adam Lesinski282e1812014-01-23 18:17:42 -0800895 }
896 }
897 } else if (strcmp("badging", option) == 0) {
898 Vector<String8> locales;
899 res.getLocales(&locales);
900
901 Vector<ResTable_config> configs;
902 res.getConfigurations(&configs);
903 SortedVector<int> densities;
904 const size_t NC = configs.size();
905 for (size_t i=0; i<NC; i++) {
906 int dens = configs[i].density;
907 if (dens == 0) {
908 dens = 160;
909 }
910 densities.add(dens);
911 }
912
913 size_t len;
914 ResXMLTree::event_code_t code;
915 int depth = 0;
916 String8 error;
917 bool withinActivity = false;
918 bool isMainActivity = false;
919 bool isLauncherActivity = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800920 bool isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800921 bool isSearchable = false;
922 bool withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700923 bool withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700924 bool withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800925 bool withinReceiver = false;
926 bool withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700927 bool withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800928 bool withinIntentFilter = false;
929 bool hasMainActivity = false;
930 bool hasOtherActivities = false;
931 bool hasOtherReceivers = false;
932 bool hasOtherServices = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700933 bool hasIntentFilter = false;
934
Adam Lesinski282e1812014-01-23 18:17:42 -0800935 bool hasWallpaperService = false;
936 bool hasImeService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700937 bool hasAccessibilityService = false;
938 bool hasPrintService = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800939 bool hasWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700940 bool hasDeviceAdminReceiver = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700941 bool hasPaymentService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700942 bool hasDocumentsProvider = false;
943 bool hasCameraActivity = false;
944 bool hasCameraSecureActivity = false;
945 bool hasLauncher = false;
946 bool hasNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400947 bool hasDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700948
Adam Lesinski282e1812014-01-23 18:17:42 -0800949 bool actMainActivity = false;
950 bool actWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700951 bool actDeviceAdminEnabled = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800952 bool actImeService = false;
953 bool actWallpaperService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700954 bool actAccessibilityService = false;
955 bool actPrintService = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700956 bool actHostApduService = false;
957 bool actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700958 bool actDocumentsProvider = false;
959 bool actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400960 bool actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700961 bool actCamera = false;
962 bool actCameraSecure = false;
963 bool catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700964 bool hasMetaHostPaymentCategory = false;
965 bool hasMetaOffHostPaymentCategory = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700966
967 // These permissions are required by services implementing services
968 // the system binds to (IME, Accessibility, PrintServices, etc.)
969 bool hasBindDeviceAdminPermission = false;
970 bool hasBindInputMethodPermission = false;
971 bool hasBindAccessibilityServicePermission = false;
972 bool hasBindPrintServicePermission = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700973 bool hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700974 bool hasRequiredSafAttributes = false;
975 bool hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400976 bool hasBindDreamServicePermission = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800977
978 // These two implement the implicit permissions that are granted
979 // to pre-1.6 applications.
980 bool hasWriteExternalStoragePermission = false;
981 bool hasReadPhoneStatePermission = false;
982
983 // If an app requests write storage, they will also get read storage.
984 bool hasReadExternalStoragePermission = false;
985
986 // Implement transition to read and write call log.
987 bool hasReadContactsPermission = false;
988 bool hasWriteContactsPermission = false;
989 bool hasReadCallLogPermission = false;
990 bool hasWriteCallLogPermission = false;
991
Adam Lesinskie47fd122014-08-15 22:25:36 -0700992 // If an app declares itself as multiArch, we report the
993 // native libraries differently.
994 bool hasMultiArch = false;
995
Adam Lesinski282e1812014-01-23 18:17:42 -0800996 // This next group of variables is used to implement a group of
997 // backward-compatibility heuristics necessitated by the addition of
998 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
999 // heuristic is "if an app requests a permission but doesn't explicitly
1000 // request the corresponding <uses-feature>, presume it's there anyway".
Adam Lesinski2c72b682014-06-24 09:56:01 -07001001
Adam Lesinski282e1812014-01-23 18:17:42 -08001002 // 2.2 also added some other features that apps can request, but that
1003 // have no corresponding permission, so we cannot implement any
1004 // back-compatibility heuristic for them. The below are thus unnecessary
1005 // (but are retained here for documentary purposes.)
1006 //bool specCompassFeature = false;
1007 //bool specAccelerometerFeature = false;
1008 //bool specProximityFeature = false;
1009 //bool specAmbientLightFeature = false;
1010 //bool specLiveWallpaperFeature = false;
1011
1012 int targetSdk = 0;
1013 int smallScreen = 1;
1014 int normalScreen = 1;
1015 int largeScreen = 1;
1016 int xlargeScreen = 1;
1017 int anyDensity = 1;
1018 int requiresSmallestWidthDp = 0;
1019 int compatibleWidthLimitDp = 0;
1020 int largestWidthLimitDp = 0;
1021 String8 pkg;
1022 String8 activityName;
1023 String8 activityLabel;
1024 String8 activityIcon;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001025 String8 activityBanner;
Adam Lesinski282e1812014-01-23 18:17:42 -08001026 String8 receiverName;
1027 String8 serviceName;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001028 Vector<String8> supportedInput;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001029
1030 FeatureGroup commonFeatures;
1031 Vector<FeatureGroup> featureGroups;
1032 KeyedVector<String8, ImpliedFeature> impliedFeatures;
1033
Adam Lesinski282e1812014-01-23 18:17:42 -08001034 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
1035 if (code == ResXMLTree::END_TAG) {
1036 depth--;
1037 if (depth < 2) {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001038 if (withinSupportsInput && !supportedInput.isEmpty()) {
1039 printf("supports-input: '");
1040 const size_t N = supportedInput.size();
1041 for (size_t i=0; i<N; i++) {
Maurice Chu2675f762013-10-22 17:33:11 -07001042 printf("%s", ResTable::normalizeForOutput(
1043 supportedInput[i].string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001044 if (i != N - 1) {
1045 printf("' '");
1046 } else {
1047 printf("'\n");
1048 }
1049 }
1050 supportedInput.clear();
1051 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001052 withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001053 withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001054 withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001055 } else if (depth < 3) {
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001056 if (withinActivity && isMainActivity) {
Maurice Chu2675f762013-10-22 17:33:11 -07001057 String8 aName(getComponentName(pkg, activityName));
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001058 if (isLauncherActivity) {
1059 printf("launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001060 if (aName.length() > 0) {
1061 printf(" name='%s' ",
1062 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001063 }
1064 printf(" label='%s' icon='%s'\n",
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001065 ResTable::normalizeForOutput(activityLabel.string()).string(),
1066 ResTable::normalizeForOutput(activityIcon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001067 }
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001068 if (isLeanbackLauncherActivity) {
1069 printf("leanback-launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001070 if (aName.length() > 0) {
1071 printf(" name='%s' ",
1072 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001073 }
1074 printf(" label='%s' icon='%s' banner='%s'\n",
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001075 ResTable::normalizeForOutput(activityLabel.string()).string(),
1076 ResTable::normalizeForOutput(activityIcon.string()).string(),
1077 ResTable::normalizeForOutput(activityBanner.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001078 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001079 }
1080 if (!hasIntentFilter) {
1081 hasOtherActivities |= withinActivity;
1082 hasOtherReceivers |= withinReceiver;
1083 hasOtherServices |= withinService;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001084 } else {
1085 if (withinService) {
1086 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
1087 hasBindNfcServicePermission);
1088 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
1089 hasBindNfcServicePermission);
1090 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001091 }
1092 withinActivity = false;
1093 withinService = false;
1094 withinReceiver = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001095 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001096 hasIntentFilter = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001097 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001098 } else if (depth < 4) {
1099 if (withinIntentFilter) {
1100 if (withinActivity) {
1101 hasMainActivity |= actMainActivity;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001102 hasLauncher |= catLauncher;
1103 hasCameraActivity |= actCamera;
1104 hasCameraSecureActivity |= actCameraSecure;
1105 hasOtherActivities |= !actMainActivity && !actCamera && !actCameraSecure;
Adam Lesinski282e1812014-01-23 18:17:42 -08001106 } else if (withinReceiver) {
1107 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001108 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
1109 hasBindDeviceAdminPermission);
1110 hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled);
Adam Lesinski282e1812014-01-23 18:17:42 -08001111 } else if (withinService) {
1112 hasImeService |= actImeService;
1113 hasWallpaperService |= actWallpaperService;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001114 hasAccessibilityService |= (actAccessibilityService &&
1115 hasBindAccessibilityServicePermission);
1116 hasPrintService |= (actPrintService && hasBindPrintServicePermission);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001117 hasNotificationListenerService |= actNotificationListenerService &&
1118 hasBindNotificationListenerServicePermission;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001119 hasDreamService |= actDreamService && hasBindDreamServicePermission;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001120 hasOtherServices |= (!actImeService && !actWallpaperService &&
Adam Lesinski94fc9122013-09-30 17:16:09 -07001121 !actAccessibilityService && !actPrintService &&
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001122 !actHostApduService && !actOffHostApduService &&
1123 !actNotificationListenerService);
1124 } else if (withinProvider) {
1125 hasDocumentsProvider |= actDocumentsProvider && hasRequiredSafAttributes;
Adam Lesinski282e1812014-01-23 18:17:42 -08001126 }
1127 }
1128 withinIntentFilter = false;
1129 }
1130 continue;
1131 }
1132 if (code != ResXMLTree::START_TAG) {
1133 continue;
1134 }
1135 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001136
1137 const char16_t* ctag16 = tree.getElementName(&len);
1138 if (ctag16 == NULL) {
1139 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
1140 goto bail;
1141 }
1142 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -08001143 //printf("Depth %d, %s\n", depth, tag.string());
1144 if (depth == 1) {
1145 if (tag != "manifest") {
1146 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
1147 goto bail;
1148 }
1149 pkg = getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -07001150 printf("package: name='%s' ",
1151 ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001152 int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
1153 if (error != "") {
1154 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
1155 goto bail;
1156 }
1157 if (versionCode > 0) {
1158 printf("versionCode='%d' ", versionCode);
1159 } else {
1160 printf("versionCode='' ");
1161 }
1162 String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
1163 if (error != "") {
1164 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
1165 goto bail;
1166 }
Adam Lesinski25d35a92014-08-11 09:41:56 -07001167 printf("versionName='%s'",
Maurice Chu2675f762013-10-22 17:33:11 -07001168 ResTable::normalizeForOutput(versionName.string()).string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001169
1170 String8 splitName = getAttribute(tree, NULL, "split", NULL);
1171 if (!splitName.isEmpty()) {
1172 printf(" split='%s'", ResTable::normalizeForOutput(
1173 splitName.string()).string());
1174 }
1175 printf("\n");
Adam Lesinski282e1812014-01-23 18:17:42 -08001176 } else if (depth == 2) {
1177 withinApplication = false;
1178 if (tag == "application") {
1179 withinApplication = true;
1180
1181 String8 label;
1182 const size_t NL = locales.size();
1183 for (size_t i=0; i<NL; i++) {
1184 const char* localeStr = locales[i].string();
1185 assets.setLocale(localeStr != NULL ? localeStr : "");
1186 String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1187 if (llabel != "") {
1188 if (localeStr == NULL || strlen(localeStr) == 0) {
1189 label = llabel;
Maurice Chu2675f762013-10-22 17:33:11 -07001190 printf("application-label:'%s'\n",
1191 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001192 } else {
1193 if (label == "") {
1194 label = llabel;
1195 }
1196 printf("application-label-%s:'%s'\n", localeStr,
Maurice Chu2675f762013-10-22 17:33:11 -07001197 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001198 }
1199 }
1200 }
1201
1202 ResTable_config tmpConfig = config;
1203 const size_t ND = densities.size();
1204 for (size_t i=0; i<ND; i++) {
1205 tmpConfig.density = densities[i];
1206 assets.setConfiguration(tmpConfig);
1207 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1208 if (icon != "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001209 printf("application-icon-%d:'%s'\n", densities[i],
1210 ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001211 }
1212 }
1213 assets.setConfiguration(config);
1214
1215 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1216 if (error != "") {
1217 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
1218 goto bail;
1219 }
1220 int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
1221 if (error != "") {
1222 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
1223 goto bail;
1224 }
Maurice Chu2675f762013-10-22 17:33:11 -07001225 printf("application: label='%s' ",
1226 ResTable::normalizeForOutput(label.string()).string());
1227 printf("icon='%s'\n", ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001228 if (testOnly != 0) {
1229 printf("testOnly='%d'\n", testOnly);
1230 }
1231
1232 int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0);
1233 if (error != "") {
1234 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string());
1235 goto bail;
1236 }
1237 if (debuggable != 0) {
1238 printf("application-debuggable\n");
1239 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07001240
1241 // We must search by name because the multiArch flag hasn't been API
1242 // frozen yet.
1243 int32_t multiArchIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
1244 "multiArch");
1245 if (multiArchIndex >= 0) {
1246 Res_value value;
1247 if (tree.getAttributeValue(multiArchIndex, &value) != NO_ERROR) {
1248 if (value.dataType >= Res_value::TYPE_FIRST_INT &&
1249 value.dataType <= Res_value::TYPE_LAST_INT) {
1250 hasMultiArch = value.data;
1251 }
1252 }
1253 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001254 } else if (tag == "uses-sdk") {
1255 int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
1256 if (error != "") {
1257 error = "";
1258 String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
1259 if (error != "") {
1260 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
1261 error.string());
1262 goto bail;
1263 }
1264 if (name == "Donut") targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001265 printf("sdkVersion:'%s'\n",
1266 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001267 } else if (code != -1) {
1268 targetSdk = code;
1269 printf("sdkVersion:'%d'\n", code);
1270 }
1271 code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
1272 if (code != -1) {
1273 printf("maxSdkVersion:'%d'\n", code);
1274 }
1275 code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
1276 if (error != "") {
1277 error = "";
1278 String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
1279 if (error != "") {
1280 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
1281 error.string());
1282 goto bail;
1283 }
1284 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001285 printf("targetSdkVersion:'%s'\n",
1286 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001287 } else if (code != -1) {
1288 if (targetSdk < code) {
1289 targetSdk = code;
1290 }
1291 printf("targetSdkVersion:'%d'\n", code);
1292 }
1293 } else if (tag == "uses-configuration") {
1294 int32_t reqTouchScreen = getIntegerAttribute(tree,
1295 REQ_TOUCH_SCREEN_ATTR, NULL, 0);
1296 int32_t reqKeyboardType = getIntegerAttribute(tree,
1297 REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
1298 int32_t reqHardKeyboard = getIntegerAttribute(tree,
1299 REQ_HARD_KEYBOARD_ATTR, NULL, 0);
1300 int32_t reqNavigation = getIntegerAttribute(tree,
1301 REQ_NAVIGATION_ATTR, NULL, 0);
1302 int32_t reqFiveWayNav = getIntegerAttribute(tree,
1303 REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
1304 printf("uses-configuration:");
1305 if (reqTouchScreen != 0) {
1306 printf(" reqTouchScreen='%d'", reqTouchScreen);
1307 }
1308 if (reqKeyboardType != 0) {
1309 printf(" reqKeyboardType='%d'", reqKeyboardType);
1310 }
1311 if (reqHardKeyboard != 0) {
1312 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1313 }
1314 if (reqNavigation != 0) {
1315 printf(" reqNavigation='%d'", reqNavigation);
1316 }
1317 if (reqFiveWayNav != 0) {
1318 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1319 }
1320 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001321 } else if (tag == "supports-input") {
1322 withinSupportsInput = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001323 } else if (tag == "supports-screens") {
1324 smallScreen = getIntegerAttribute(tree,
1325 SMALL_SCREEN_ATTR, NULL, 1);
1326 normalScreen = getIntegerAttribute(tree,
1327 NORMAL_SCREEN_ATTR, NULL, 1);
1328 largeScreen = getIntegerAttribute(tree,
1329 LARGE_SCREEN_ATTR, NULL, 1);
1330 xlargeScreen = getIntegerAttribute(tree,
1331 XLARGE_SCREEN_ATTR, NULL, 1);
1332 anyDensity = getIntegerAttribute(tree,
1333 ANY_DENSITY_ATTR, NULL, 1);
1334 requiresSmallestWidthDp = getIntegerAttribute(tree,
1335 REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
1336 compatibleWidthLimitDp = getIntegerAttribute(tree,
1337 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
1338 largestWidthLimitDp = getIntegerAttribute(tree,
1339 LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001340 } else if (tag == "feature-group") {
1341 withinFeatureGroup = true;
1342 FeatureGroup group;
1343 group.label = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1344 if (error != "") {
1345 fprintf(stderr, "ERROR getting 'android:label' attribute:"
1346 " %s\n", error.string());
1347 goto bail;
1348 }
1349 featureGroups.add(group);
1350
Adam Lesinski282e1812014-01-23 18:17:42 -08001351 } else if (tag == "uses-feature") {
1352 String8 name = getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001353 if (name != "" && error == "") {
1354 int req = getIntegerAttribute(tree,
1355 REQUIRED_ATTR, NULL, 1);
1356
Adam Lesinski2c72b682014-06-24 09:56:01 -07001357 commonFeatures.features.add(name, req);
1358 if (req) {
1359 addParentFeatures(&commonFeatures, name);
Adam Lesinski282e1812014-01-23 18:17:42 -08001360 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001361 } else {
1362 int vers = getIntegerAttribute(tree,
1363 GL_ES_VERSION_ATTR, &error);
1364 if (error == "") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001365 if (vers > commonFeatures.openGLESVersion) {
1366 commonFeatures.openGLESVersion = vers;
1367 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001368 }
1369 }
1370 } else if (tag == "uses-permission") {
1371 String8 name = getAttribute(tree, NAME_ATTR, &error);
1372 if (name != "" && error == "") {
1373 if (name == "android.permission.CAMERA") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001374 addImpliedFeature(&impliedFeatures, "android.hardware.camera",
Adam Lesinski2c72b682014-06-24 09:56:01 -07001375 String8::format("requested %s permission", name.string())
1376 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001377 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001378 addImpliedFeature(&impliedFeatures, "android.hardware.location.gps",
1379 String8::format("requested %s permission", name.string())
1380 .string());
1381 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1382 String8::format("requested %s permission", name.string())
1383 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001384 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001385 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_COARSE_LOCATION") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001389 addImpliedFeature(&impliedFeatures, "android.hardware.location.network",
1390 String8::format("requested %s permission", name.string())
1391 .string());
1392 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1393 String8::format("requested %s permission", name.string())
1394 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001395 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
1396 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001397 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1398 String8::format("requested %s permission", name.string())
1399 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001400 } else if (name == "android.permission.BLUETOOTH" ||
1401 name == "android.permission.BLUETOOTH_ADMIN") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001402 if (targetSdk > 4) {
1403 addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
1404 String8::format("requested %s permission", name.string())
1405 .string());
1406 addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
1407 "targetSdkVersion > 4");
1408 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001409 } else if (name == "android.permission.RECORD_AUDIO") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001410 addImpliedFeature(&impliedFeatures, "android.hardware.microphone",
1411 String8::format("requested %s permission", name.string())
1412 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001413 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1414 name == "android.permission.CHANGE_WIFI_STATE" ||
1415 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001416 addImpliedFeature(&impliedFeatures, "android.hardware.wifi",
1417 String8::format("requested %s permission", name.string())
1418 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001419 } else if (name == "android.permission.CALL_PHONE" ||
1420 name == "android.permission.CALL_PRIVILEGED" ||
1421 name == "android.permission.MODIFY_PHONE_STATE" ||
1422 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1423 name == "android.permission.READ_SMS" ||
1424 name == "android.permission.RECEIVE_SMS" ||
1425 name == "android.permission.RECEIVE_MMS" ||
1426 name == "android.permission.RECEIVE_WAP_PUSH" ||
1427 name == "android.permission.SEND_SMS" ||
1428 name == "android.permission.WRITE_APN_SETTINGS" ||
1429 name == "android.permission.WRITE_SMS") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001430 addImpliedFeature(&impliedFeatures, "android.hardware.telephony",
1431 String8("requested a telephony permission").string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001432 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1433 hasWriteExternalStoragePermission = true;
1434 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1435 hasReadExternalStoragePermission = true;
1436 } else if (name == "android.permission.READ_PHONE_STATE") {
1437 hasReadPhoneStatePermission = true;
1438 } else if (name == "android.permission.READ_CONTACTS") {
1439 hasReadContactsPermission = true;
1440 } else if (name == "android.permission.WRITE_CONTACTS") {
1441 hasWriteContactsPermission = true;
1442 } else if (name == "android.permission.READ_CALL_LOG") {
1443 hasReadCallLogPermission = true;
1444 } else if (name == "android.permission.WRITE_CALL_LOG") {
1445 hasWriteCallLogPermission = true;
1446 }
Adam Lesinski58f1f362013-11-12 12:59:08 -08001447
1448 printUsesPermission(name,
1449 getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1) == 0,
1450 getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1));
1451 } else {
Adam Lesinski282e1812014-01-23 18:17:42 -08001452 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1453 error.string());
1454 goto bail;
1455 }
1456 } else if (tag == "uses-package") {
1457 String8 name = getAttribute(tree, NAME_ATTR, &error);
1458 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001459 printf("uses-package:'%s'\n",
1460 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001461 } else {
1462 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1463 error.string());
1464 goto bail;
1465 }
1466 } else if (tag == "original-package") {
1467 String8 name = getAttribute(tree, NAME_ATTR, &error);
1468 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001469 printf("original-package:'%s'\n",
1470 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001471 } else {
1472 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1473 error.string());
1474 goto bail;
1475 }
1476 } else if (tag == "supports-gl-texture") {
1477 String8 name = getAttribute(tree, NAME_ATTR, &error);
1478 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001479 printf("supports-gl-texture:'%s'\n",
1480 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001481 } else {
1482 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1483 error.string());
1484 goto bail;
1485 }
1486 } else if (tag == "compatible-screens") {
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001487 printCompatibleScreens(tree, &error);
1488 if (error != "") {
1489 fprintf(stderr, "ERROR getting compatible screens: %s\n",
1490 error.string());
1491 goto bail;
1492 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001493 depth--;
1494 } else if (tag == "package-verifier") {
1495 String8 name = getAttribute(tree, NAME_ATTR, &error);
1496 if (name != "" && error == "") {
1497 String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
1498 if (publicKey != "" && error == "") {
1499 printf("package-verifier: name='%s' publicKey='%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001500 ResTable::normalizeForOutput(name.string()).string(),
1501 ResTable::normalizeForOutput(publicKey.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001502 }
1503 }
1504 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001505 } else if (depth == 3) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001506 withinActivity = false;
1507 withinReceiver = false;
1508 withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001509 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001510 hasIntentFilter = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001511 hasMetaHostPaymentCategory = false;
1512 hasMetaOffHostPaymentCategory = false;
1513 hasBindDeviceAdminPermission = false;
1514 hasBindInputMethodPermission = false;
1515 hasBindAccessibilityServicePermission = false;
1516 hasBindPrintServicePermission = false;
1517 hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001518 hasRequiredSafAttributes = false;
1519 hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001520 hasBindDreamServicePermission = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001521 if (withinApplication) {
1522 if(tag == "activity") {
1523 withinActivity = true;
1524 activityName = getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001525 if (error != "") {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001526 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1527 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001528 goto bail;
1529 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001530
Michael Wrightec4fdec2013-09-06 16:50:52 -07001531 activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1532 if (error != "") {
1533 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
1534 error.string());
1535 goto bail;
1536 }
1537
1538 activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1539 if (error != "") {
1540 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1541 error.string());
1542 goto bail;
1543 }
1544
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001545 activityBanner = getResolvedAttribute(&res, tree, BANNER_ATTR, &error);
1546 if (error != "") {
1547 fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
1548 error.string());
1549 goto bail;
1550 }
1551
Michael Wrightec4fdec2013-09-06 16:50:52 -07001552 int32_t orien = getResolvedIntegerAttribute(&res, tree,
1553 SCREEN_ORIENTATION_ATTR, &error);
1554 if (error == "") {
1555 if (orien == 0 || orien == 6 || orien == 8) {
1556 // Requests landscape, sensorLandscape, or reverseLandscape.
Adam Lesinski2c72b682014-06-24 09:56:01 -07001557 addImpliedFeature(&impliedFeatures, "android.hardware.screen.landscape",
1558 "one or more activities have specified a landscape orientation");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001559 } else if (orien == 1 || orien == 7 || orien == 9) {
1560 // Requests portrait, sensorPortrait, or reversePortrait.
Adam Lesinski2c72b682014-06-24 09:56:01 -07001561 addImpliedFeature(&impliedFeatures, "android.hardware.screen.portrait",
1562 "one or more activities have specified a portrait orientation");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001563 }
1564 }
1565 } else if (tag == "uses-library") {
1566 String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1567 if (error != "") {
1568 fprintf(stderr,
1569 "ERROR getting 'android:name' attribute for uses-library"
1570 " %s\n", error.string());
1571 goto bail;
1572 }
1573 int req = getIntegerAttribute(tree,
1574 REQUIRED_ATTR, NULL, 1);
1575 printf("uses-library%s:'%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001576 req ? "" : "-not-required", ResTable::normalizeForOutput(
1577 libraryName.string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001578 } else if (tag == "receiver") {
1579 withinReceiver = true;
1580 receiverName = getAttribute(tree, NAME_ATTR, &error);
1581
1582 if (error != "") {
1583 fprintf(stderr,
1584 "ERROR getting 'android:name' attribute for receiver:"
1585 " %s\n", error.string());
1586 goto bail;
1587 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001588
1589 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1590 if (error == "") {
1591 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1592 hasBindDeviceAdminPermission = true;
1593 }
1594 } else {
1595 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1596 " receiver '%s': %s\n", receiverName.string(), error.string());
1597 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001598 } else if (tag == "service") {
1599 withinService = true;
1600 serviceName = getAttribute(tree, NAME_ATTR, &error);
1601
1602 if (error != "") {
1603 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1604 "service:%s\n", error.string());
1605 goto bail;
1606 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001607
1608 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1609 if (error == "") {
1610 if (permission == "android.permission.BIND_INPUT_METHOD") {
1611 hasBindInputMethodPermission = true;
1612 } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") {
1613 hasBindAccessibilityServicePermission = true;
1614 } else if (permission == "android.permission.BIND_PRINT_SERVICE") {
1615 hasBindPrintServicePermission = true;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001616 } else if (permission == "android.permission.BIND_NFC_SERVICE") {
1617 hasBindNfcServicePermission = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001618 } else if (permission == "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
1619 hasBindNotificationListenerServicePermission = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001620 } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
1621 hasBindDreamServicePermission = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001622 }
1623 } else {
1624 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1625 " service '%s': %s\n", serviceName.string(), error.string());
1626 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001627 } else if (tag == "provider") {
1628 withinProvider = true;
1629
1630 bool exported = getResolvedIntegerAttribute(&res, tree, EXPORTED_ATTR, &error);
1631 if (error != "") {
1632 fprintf(stderr, "ERROR getting 'android:exported' attribute for provider:"
1633 " %s\n", error.string());
1634 goto bail;
1635 }
1636
1637 bool grantUriPermissions = getResolvedIntegerAttribute(&res, tree,
1638 GRANT_URI_PERMISSIONS_ATTR, &error);
1639 if (error != "") {
1640 fprintf(stderr, "ERROR getting 'android:grantUriPermissions' attribute for provider:"
1641 " %s\n", error.string());
1642 goto bail;
1643 }
1644
1645 String8 permission = getResolvedAttribute(&res, tree, PERMISSION_ATTR, &error);
1646 if (error != "") {
1647 fprintf(stderr, "ERROR getting 'android:permission' attribute for provider:"
1648 " %s\n", error.string());
1649 goto bail;
1650 }
1651
1652 hasRequiredSafAttributes |= exported && grantUriPermissions &&
1653 permission == "android.permission.MANAGE_DOCUMENTS";
1654
Michael Wrightec4fdec2013-09-06 16:50:52 -07001655 } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
Adam Lesinskib71adb62014-05-15 14:14:41 -07001656 String8 metaDataName = getResolvedAttribute(&res, tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001657 if (error != "") {
1658 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1659 "meta-data:%s\n", error.string());
1660 goto bail;
1661 }
Maurice Chu2675f762013-10-22 17:33:11 -07001662 printf("meta-data: name='%s' ",
1663 ResTable::normalizeForOutput(metaDataName.string()).string());
Maurice Chu76327312013-10-16 18:28:46 -07001664 printResolvedResourceAttribute(&res, tree, VALUE_ATTR, String8("value"),
1665 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001666 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001667 // Try looking for a RESOURCE_ATTR
1668 error = "";
1669 printResolvedResourceAttribute(&res, tree, RESOURCE_ATTR,
1670 String8("resource"), &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001671 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001672 fprintf(stderr, "ERROR getting 'android:value' or "
1673 "'android:resource' attribute for "
1674 "meta-data:%s\n", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001675 goto bail;
1676 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001677 }
Maurice Chu76327312013-10-16 18:28:46 -07001678 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001679 } else if (withinSupportsInput && tag == "input-type") {
1680 String8 name = getAttribute(tree, NAME_ATTR, &error);
1681 if (name != "" && error == "") {
1682 supportedInput.add(name);
1683 } else {
1684 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1685 error.string());
1686 goto bail;
1687 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001688 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001689 } else if (withinFeatureGroup && tag == "uses-feature") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001690 FeatureGroup& top = featureGroups.editTop();
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001691
1692 String8 name = getResolvedAttribute(&res, tree, NAME_ATTR, &error);
1693 if (name != "" && error == "") {
Adam Lesinskid3edfde2014-08-08 17:32:44 -07001694 top.features.add(name, true);
1695 addParentFeatures(&top, name);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001696 } else {
1697 int vers = getIntegerAttribute(tree, GL_ES_VERSION_ATTR, &error);
1698 if (error == "") {
1699 if (vers > top.openGLESVersion) {
1700 top.openGLESVersion = vers;
1701 }
1702 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001703 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001704 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001705 } else if (depth == 4) {
1706 if (tag == "intent-filter") {
1707 hasIntentFilter = true;
1708 withinIntentFilter = true;
1709 actMainActivity = false;
1710 actWidgetReceivers = false;
1711 actImeService = false;
1712 actWallpaperService = false;
1713 actAccessibilityService = false;
1714 actPrintService = false;
1715 actDeviceAdminEnabled = false;
1716 actHostApduService = false;
1717 actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001718 actDocumentsProvider = false;
1719 actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001720 actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001721 actCamera = false;
1722 actCameraSecure = false;
1723 catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001724 } else if (withinService && tag == "meta-data") {
1725 String8 name = getAttribute(tree, NAME_ATTR, &error);
1726 if (error != "") {
1727 fprintf(stderr, "ERROR getting 'android:name' attribute for"
1728 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1729 goto bail;
1730 }
1731
1732 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1733 name == "android.nfc.cardemulation.off_host_apdu_service") {
1734 bool offHost = true;
1735 if (name == "android.nfc.cardemulation.host_apdu_service") {
1736 offHost = false;
1737 }
1738
1739 String8 xmlPath = getResolvedAttribute(&res, tree, RESOURCE_ATTR, &error);
1740 if (error != "") {
1741 fprintf(stderr, "ERROR getting 'android:resource' attribute for"
1742 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1743 goto bail;
1744 }
1745
1746 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1747 offHost, &error);
1748 if (error != "") {
1749 fprintf(stderr, "ERROR getting AID category for service '%s'\n",
1750 serviceName.string());
1751 goto bail;
1752 }
1753
1754 const size_t catLen = categories.size();
1755 for (size_t i = 0; i < catLen; i++) {
1756 bool paymentCategory = (categories[i] == "payment");
1757 if (offHost) {
1758 hasMetaOffHostPaymentCategory |= paymentCategory;
1759 } else {
1760 hasMetaHostPaymentCategory |= paymentCategory;
1761 }
1762 }
1763 }
1764 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001765 } else if ((depth == 5) && withinIntentFilter) {
1766 String8 action;
1767 if (tag == "action") {
1768 action = getAttribute(tree, NAME_ATTR, &error);
1769 if (error != "") {
1770 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1771 error.string());
1772 goto bail;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001773 }
1774
Adam Lesinskia5018c92013-09-30 16:23:15 -07001775 if (withinActivity) {
1776 if (action == "android.intent.action.MAIN") {
1777 isMainActivity = true;
1778 actMainActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001779 } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
1780 action == "android.media.action.VIDEO_CAMERA") {
1781 actCamera = true;
1782 } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
1783 actCameraSecure = true;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001784 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001785 } else if (withinReceiver) {
1786 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1787 actWidgetReceivers = true;
1788 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1789 actDeviceAdminEnabled = true;
1790 }
1791 } else if (withinService) {
1792 if (action == "android.view.InputMethod") {
1793 actImeService = true;
1794 } else if (action == "android.service.wallpaper.WallpaperService") {
1795 actWallpaperService = true;
1796 } else if (action == "android.accessibilityservice.AccessibilityService") {
1797 actAccessibilityService = true;
1798 } else if (action == "android.printservice.PrintService") {
1799 actPrintService = true;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001800 } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
1801 actHostApduService = true;
1802 } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
1803 actOffHostApduService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001804 } else if (action == "android.service.notification.NotificationListenerService") {
1805 actNotificationListenerService = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001806 } else if (action == "android.service.dreams.DreamService") {
1807 actDreamService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001808 }
1809 } else if (withinProvider) {
1810 if (action == "android.content.action.DOCUMENTS_PROVIDER") {
1811 actDocumentsProvider = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001812 }
1813 }
1814 if (action == "android.intent.action.SEARCH") {
1815 isSearchable = true;
1816 }
1817 }
1818
1819 if (tag == "category") {
1820 String8 category = getAttribute(tree, NAME_ATTR, &error);
1821 if (error != "") {
1822 fprintf(stderr, "ERROR getting 'name' attribute: %s\n",
1823 error.string());
1824 goto bail;
1825 }
1826 if (withinActivity) {
1827 if (category == "android.intent.category.LAUNCHER") {
1828 isLauncherActivity = true;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001829 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
1830 isLeanbackLauncherActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001831 } else if (category == "android.intent.category.HOME") {
1832 catLauncher = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001833 }
1834 }
1835 }
1836 }
1837 }
1838
1839 // Pre-1.6 implicitly granted permission compatibility logic
1840 if (targetSdk < 4) {
1841 if (!hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001842 printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
1843 printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
1844 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001845 hasWriteExternalStoragePermission = true;
1846 }
1847 if (!hasReadPhoneStatePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001848 printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
1849 printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
1850 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001851 }
1852 }
1853
1854 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1855 // force them to always take READ_EXTERNAL_STORAGE as well. We always
1856 // do this (regardless of target API version) because we can't have
1857 // an app with write permission but not read permission.
1858 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001859 printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"));
1860 printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
1861 String8("requested WRITE_EXTERNAL_STORAGE"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001862 }
1863
1864 // Pre-JellyBean call log permission compatibility.
1865 if (targetSdk < 16) {
1866 if (!hasReadCallLogPermission && hasReadContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001867 printUsesPermission(String8("android.permission.READ_CALL_LOG"));
1868 printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
1869 String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001870 }
1871 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001872 printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
1873 printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
1874 String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001875 }
1876 }
1877
Adam Lesinski2c72b682014-06-24 09:56:01 -07001878 addImpliedFeature(&impliedFeatures, "android.hardware.touchscreen",
1879 "default feature for all apps");
1880
1881 const size_t numFeatureGroups = featureGroups.size();
1882 if (numFeatureGroups == 0) {
1883 // If no <feature-group> tags were defined, apply auto-implied features.
1884 printFeatureGroup(commonFeatures, &impliedFeatures);
1885
1886 } else {
1887 // <feature-group> tags are defined, so we ignore implied features and
1888 for (size_t i = 0; i < numFeatureGroups; i++) {
1889 FeatureGroup& grp = featureGroups.editItemAt(i);
1890
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001891 if (commonFeatures.openGLESVersion > grp.openGLESVersion) {
1892 grp.openGLESVersion = commonFeatures.openGLESVersion;
1893 }
1894
Adam Lesinski2c72b682014-06-24 09:56:01 -07001895 // Merge the features defined in the top level (not inside a <feature-group>)
1896 // with this feature group.
1897 const size_t numCommonFeatures = commonFeatures.features.size();
1898 for (size_t j = 0; j < numCommonFeatures; j++) {
1899 if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001900 grp.features.add(commonFeatures.features.keyAt(j),
1901 commonFeatures.features[j]);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001902 }
1903 }
1904
1905 if (!grp.features.isEmpty()) {
1906 printFeatureGroup(grp);
Adam Lesinski282e1812014-01-23 18:17:42 -08001907 }
1908 }
1909 }
1910
Adam Lesinski282e1812014-01-23 18:17:42 -08001911
Adam Lesinski282e1812014-01-23 18:17:42 -08001912 if (hasWidgetReceivers) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001913 printComponentPresence("app-widget");
Adam Lesinski282e1812014-01-23 18:17:42 -08001914 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001915 if (hasDeviceAdminReceiver) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001916 printComponentPresence("device-admin");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001917 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001918 if (hasImeService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001919 printComponentPresence("ime");
Adam Lesinski282e1812014-01-23 18:17:42 -08001920 }
1921 if (hasWallpaperService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001922 printComponentPresence("wallpaper");
Adam Lesinski282e1812014-01-23 18:17:42 -08001923 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001924 if (hasAccessibilityService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001925 printComponentPresence("accessibility");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001926 }
1927 if (hasPrintService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001928 printComponentPresence("print-service");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001929 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001930 if (hasPaymentService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001931 printComponentPresence("payment");
1932 }
1933 if (isSearchable) {
1934 printComponentPresence("search");
1935 }
1936 if (hasDocumentsProvider) {
1937 printComponentPresence("document-provider");
1938 }
1939 if (hasLauncher) {
1940 printComponentPresence("launcher");
1941 }
1942 if (hasNotificationListenerService) {
1943 printComponentPresence("notification-listener");
1944 }
John Spurlockeb8d1be2014-06-25 17:46:15 -04001945 if (hasDreamService) {
1946 printComponentPresence("dream");
1947 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001948 if (hasCameraActivity) {
1949 printComponentPresence("camera");
1950 }
1951 if (hasCameraSecureActivity) {
1952 printComponentPresence("camera-secure");
1953 }
1954
1955 if (hasMainActivity) {
1956 printf("main\n");
Adam Lesinski94fc9122013-09-30 17:16:09 -07001957 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001958 if (hasOtherActivities) {
1959 printf("other-activities\n");
1960 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001961 if (hasOtherReceivers) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001962 printf("other-receivers\n");
1963 }
1964 if (hasOtherServices) {
1965 printf("other-services\n");
1966 }
1967
1968 // For modern apps, if screen size buckets haven't been specified
1969 // but the new width ranges have, then infer the buckets from them.
1970 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1971 && requiresSmallestWidthDp > 0) {
1972 int compatWidth = compatibleWidthLimitDp;
1973 if (compatWidth <= 0) {
1974 compatWidth = requiresSmallestWidthDp;
1975 }
1976 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1977 smallScreen = -1;
1978 } else {
1979 smallScreen = 0;
1980 }
1981 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1982 normalScreen = -1;
1983 } else {
1984 normalScreen = 0;
1985 }
1986 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1987 largeScreen = -1;
1988 } else {
1989 largeScreen = 0;
1990 }
1991 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1992 xlargeScreen = -1;
1993 } else {
1994 xlargeScreen = 0;
1995 }
1996 }
1997
1998 // Determine default values for any unspecified screen sizes,
1999 // based on the target SDK of the package. As of 4 (donut)
2000 // the screen size support was introduced, so all default to
2001 // enabled.
2002 if (smallScreen > 0) {
2003 smallScreen = targetSdk >= 4 ? -1 : 0;
2004 }
2005 if (normalScreen > 0) {
2006 normalScreen = -1;
2007 }
2008 if (largeScreen > 0) {
2009 largeScreen = targetSdk >= 4 ? -1 : 0;
2010 }
2011 if (xlargeScreen > 0) {
2012 // Introduced in Gingerbread.
2013 xlargeScreen = targetSdk >= 9 ? -1 : 0;
2014 }
2015 if (anyDensity > 0) {
2016 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
2017 || compatibleWidthLimitDp > 0) ? -1 : 0;
2018 }
2019 printf("supports-screens:");
2020 if (smallScreen != 0) {
2021 printf(" 'small'");
2022 }
2023 if (normalScreen != 0) {
2024 printf(" 'normal'");
2025 }
2026 if (largeScreen != 0) {
2027 printf(" 'large'");
2028 }
2029 if (xlargeScreen != 0) {
2030 printf(" 'xlarge'");
2031 }
2032 printf("\n");
2033 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
2034 if (requiresSmallestWidthDp > 0) {
2035 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
2036 }
2037 if (compatibleWidthLimitDp > 0) {
2038 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
2039 }
2040 if (largestWidthLimitDp > 0) {
2041 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
2042 }
2043
2044 printf("locales:");
2045 const size_t NL = locales.size();
2046 for (size_t i=0; i<NL; i++) {
2047 const char* localeStr = locales[i].string();
2048 if (localeStr == NULL || strlen(localeStr) == 0) {
2049 localeStr = "--_--";
2050 }
2051 printf(" '%s'", localeStr);
2052 }
2053 printf("\n");
2054
2055 printf("densities:");
2056 const size_t ND = densities.size();
2057 for (size_t i=0; i<ND; i++) {
2058 printf(" '%d'", densities[i]);
2059 }
2060 printf("\n");
2061
2062 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
2063 if (dir != NULL) {
2064 if (dir->getFileCount() > 0) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002065 SortedVector<String8> architectures;
Adam Lesinski282e1812014-01-23 18:17:42 -08002066 for (size_t i=0; i<dir->getFileCount(); i++) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002067 architectures.add(ResTable::normalizeForOutput(
2068 dir->getFileName(i).string()));
Adam Lesinski282e1812014-01-23 18:17:42 -08002069 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07002070
2071 bool outputAltNativeCode = false;
2072 // A multiArch package is one that contains 64-bit and
2073 // 32-bit versions of native code and expects 3rd-party
2074 // apps to load these native code libraries. Since most
2075 // 64-bit systems also support 32-bit apps, the apps
2076 // loading this multiArch package's code may be either
2077 // 32-bit or 64-bit.
2078 if (hasMultiArch) {
2079 // If this is a multiArch package, report the 64-bit
2080 // version only. Then as a separate entry, report the
2081 // rest.
2082 //
2083 // If we report the 32-bit architecture, this APK will
2084 // be installed on a 32-bit device, causing a large waste
2085 // of bandwidth and disk space. This assumes that
2086 // the developer of the multiArch package has also
2087 // made a version that is 32-bit only.
2088 String8 intel64("x86_64");
2089 String8 arm64("arm64-v8a");
2090 ssize_t index = architectures.indexOf(intel64);
2091 if (index < 0) {
2092 index = architectures.indexOf(arm64);
2093 }
2094
2095 if (index >= 0) {
2096 printf("native-code: '%s'\n", architectures[index].string());
2097 architectures.removeAt(index);
2098 outputAltNativeCode = true;
2099 }
2100 }
2101
2102 const size_t archCount = architectures.size();
2103 if (archCount > 0) {
2104 if (outputAltNativeCode) {
2105 printf("alt-");
2106 }
2107 printf("native-code:");
2108 for (size_t i = 0; i < archCount; i++) {
2109 printf(" '%s'", architectures[i].string());
2110 }
2111 printf("\n");
2112 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002113 }
2114 delete dir;
2115 }
2116 } else if (strcmp("badger", option) == 0) {
2117 printf("%s", CONSOLE_DATA);
2118 } else if (strcmp("configurations", option) == 0) {
2119 Vector<ResTable_config> configs;
2120 res.getConfigurations(&configs);
2121 const size_t N = configs.size();
2122 for (size_t i=0; i<N; i++) {
2123 printf("%s\n", configs[i].toString().string());
2124 }
2125 } else {
2126 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2127 goto bail;
2128 }
2129 }
2130
2131 result = NO_ERROR;
2132
2133bail:
2134 if (asset) {
2135 delete asset;
2136 }
2137 return (result != NO_ERROR);
2138}
2139
2140
2141/*
2142 * Handle the "add" command, which wants to add files to a new or
2143 * pre-existing archive.
2144 */
2145int doAdd(Bundle* bundle)
2146{
2147 ZipFile* zip = NULL;
2148 status_t result = UNKNOWN_ERROR;
2149 const char* zipFileName;
2150
2151 if (bundle->getUpdate()) {
2152 /* avoid confusion */
2153 fprintf(stderr, "ERROR: can't use '-u' with add\n");
2154 goto bail;
2155 }
2156
2157 if (bundle->getFileSpecCount() < 1) {
2158 fprintf(stderr, "ERROR: must specify zip file name\n");
2159 goto bail;
2160 }
2161 zipFileName = bundle->getFileSpecEntry(0);
2162
2163 if (bundle->getFileSpecCount() < 2) {
2164 fprintf(stderr, "NOTE: nothing to do\n");
2165 goto bail;
2166 }
2167
2168 zip = openReadWrite(zipFileName, true);
2169 if (zip == NULL) {
2170 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2171 goto bail;
2172 }
2173
2174 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2175 const char* fileName = bundle->getFileSpecEntry(i);
2176
2177 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
2178 printf(" '%s'... (from gzip)\n", fileName);
2179 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
2180 } else {
2181 if (bundle->getJunkPath()) {
2182 String8 storageName = String8(fileName).getPathLeaf();
Maurice Chu2675f762013-10-22 17:33:11 -07002183 printf(" '%s' as '%s'...\n", fileName,
2184 ResTable::normalizeForOutput(storageName.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08002185 result = zip->add(fileName, storageName.string(),
2186 bundle->getCompressionMethod(), NULL);
2187 } else {
2188 printf(" '%s'...\n", fileName);
2189 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2190 }
2191 }
2192 if (result != NO_ERROR) {
2193 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2194 if (result == NAME_NOT_FOUND) {
2195 fprintf(stderr, ": file not found\n");
2196 } else if (result == ALREADY_EXISTS) {
2197 fprintf(stderr, ": already exists in archive\n");
2198 } else {
2199 fprintf(stderr, "\n");
2200 }
2201 goto bail;
2202 }
2203 }
2204
2205 result = NO_ERROR;
2206
2207bail:
2208 delete zip;
2209 return (result != NO_ERROR);
2210}
2211
2212
2213/*
2214 * Delete files from an existing archive.
2215 */
2216int doRemove(Bundle* bundle)
2217{
2218 ZipFile* zip = NULL;
2219 status_t result = UNKNOWN_ERROR;
2220 const char* zipFileName;
2221
2222 if (bundle->getFileSpecCount() < 1) {
2223 fprintf(stderr, "ERROR: must specify zip file name\n");
2224 goto bail;
2225 }
2226 zipFileName = bundle->getFileSpecEntry(0);
2227
2228 if (bundle->getFileSpecCount() < 2) {
2229 fprintf(stderr, "NOTE: nothing to do\n");
2230 goto bail;
2231 }
2232
2233 zip = openReadWrite(zipFileName, false);
2234 if (zip == NULL) {
2235 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2236 zipFileName);
2237 goto bail;
2238 }
2239
2240 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2241 const char* fileName = bundle->getFileSpecEntry(i);
2242 ZipEntry* entry;
2243
2244 entry = zip->getEntryByName(fileName);
2245 if (entry == NULL) {
2246 printf(" '%s' NOT FOUND\n", fileName);
2247 continue;
2248 }
2249
2250 result = zip->remove(entry);
2251
2252 if (result != NO_ERROR) {
2253 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2254 bundle->getFileSpecEntry(i), zipFileName);
2255 goto bail;
2256 }
2257 }
2258
2259 /* update the archive */
2260 zip->flush();
2261
2262bail:
2263 delete zip;
2264 return (result != NO_ERROR);
2265}
2266
Adam Lesinski3921e872014-05-13 10:56:25 -07002267static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002268 const size_t numDirs = dir->getDirs().size();
2269 for (size_t i = 0; i < numDirs; i++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002270 bool ignore = ignoreConfig;
2271 const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2272 const char* dirStr = subDir->getLeaf().string();
2273 if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2274 ignore = true;
2275 }
2276 status_t err = addResourcesToBuilder(subDir, builder, ignore);
Adam Lesinskifab50872014-04-16 14:40:42 -07002277 if (err != NO_ERROR) {
2278 return err;
2279 }
2280 }
2281
2282 const size_t numFiles = dir->getFiles().size();
2283 for (size_t i = 0; i < numFiles; i++) {
2284 sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2285 const size_t numConfigs = gp->getFiles().size();
2286 for (size_t j = 0; j < numConfigs; j++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002287 status_t err = NO_ERROR;
2288 if (ignoreConfig) {
2289 err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2290 } else {
2291 err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2292 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002293 if (err != NO_ERROR) {
2294 fprintf(stderr, "Failed to add %s (%s) to builder.\n",
2295 gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
2296 return err;
2297 }
2298 }
2299 }
2300 return NO_ERROR;
2301}
2302
2303static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2304 if (split->isBase()) {
2305 return original;
2306 }
2307
2308 String8 ext(original.getPathExtension());
2309 if (ext == String8(".apk")) {
2310 return String8::format("%s_%s%s",
2311 original.getBasePath().string(),
2312 split->getDirectorySafeName().string(),
2313 ext.string());
2314 }
2315
2316 return String8::format("%s_%s", original.string(),
2317 split->getDirectorySafeName().string());
2318}
Adam Lesinski282e1812014-01-23 18:17:42 -08002319
2320/*
2321 * Package up an asset directory and associated application files.
2322 */
2323int doPackage(Bundle* bundle)
2324{
2325 const char* outputAPKFile;
2326 int retVal = 1;
2327 status_t err;
2328 sp<AaptAssets> assets;
2329 int N;
2330 FILE* fp;
2331 String8 dependencyFile;
Adam Lesinskifab50872014-04-16 14:40:42 -07002332 sp<ApkBuilder> builder;
Adam Lesinski282e1812014-01-23 18:17:42 -08002333
Anton Krumina2ef5c02014-03-12 14:46:44 -07002334 // -c en_XA or/and ar_XB means do pseudolocalization
Adam Lesinskifab50872014-04-16 14:40:42 -07002335 sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2336 err = configFilter->parse(bundle->getConfigurations());
Adam Lesinski282e1812014-01-23 18:17:42 -08002337 if (err != NO_ERROR) {
2338 goto bail;
2339 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002340 if (configFilter->containsPseudo()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002341 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2342 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002343 if (configFilter->containsPseudoBidi()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002344 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
Adam Lesinski282e1812014-01-23 18:17:42 -08002345 }
2346
2347 N = bundle->getFileSpecCount();
2348 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
Adam Lesinski09384302014-01-22 16:07:42 -08002349 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002350 fprintf(stderr, "ERROR: no input files\n");
2351 goto bail;
2352 }
2353
2354 outputAPKFile = bundle->getOutputAPKFile();
2355
2356 // Make sure the filenames provided exist and are of the appropriate type.
2357 if (outputAPKFile) {
2358 FileType type;
2359 type = getFileType(outputAPKFile);
2360 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2361 fprintf(stderr,
2362 "ERROR: output file '%s' exists but is not regular file\n",
2363 outputAPKFile);
2364 goto bail;
2365 }
2366 }
2367
2368 // Load the assets.
2369 assets = new AaptAssets();
2370
2371 // Set up the resource gathering in assets if we're going to generate
2372 // dependency files. Every time we encounter a resource while slurping
2373 // the tree, we'll add it to these stores so we have full resource paths
2374 // to write to a dependency file.
2375 if (bundle->getGenDependencies()) {
2376 sp<FilePathStore> resPathStore = new FilePathStore;
2377 assets->setFullResPaths(resPathStore);
2378 sp<FilePathStore> assetPathStore = new FilePathStore;
2379 assets->setFullAssetPaths(assetPathStore);
2380 }
2381
2382 err = assets->slurpFromArgs(bundle);
2383 if (err < 0) {
2384 goto bail;
2385 }
2386
2387 if (bundle->getVerbose()) {
2388 assets->print(String8());
2389 }
2390
Adam Lesinskifab50872014-04-16 14:40:42 -07002391 // Create the ApkBuilder, which will collect the compiled files
2392 // to write to the final APK (or sets of APKs if we are building
2393 // a Split APK.
2394 builder = new ApkBuilder(configFilter);
2395
2396 // If we are generating a Split APK, find out which configurations to split on.
2397 if (bundle->getSplitConfigurations().size() > 0) {
2398 const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2399 const size_t numSplits = splitStrs.size();
2400 for (size_t i = 0; i < numSplits; i++) {
2401 std::set<ConfigDescription> configs;
2402 if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2403 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2404 goto bail;
2405 }
2406
2407 err = builder->createSplitForConfigs(configs);
2408 if (err != NO_ERROR) {
2409 goto bail;
2410 }
2411 }
2412 }
2413
Adam Lesinski282e1812014-01-23 18:17:42 -08002414 // If they asked for any fileAs that need to be compiled, do so.
2415 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002416 err = buildResources(bundle, assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002417 if (err != 0) {
2418 goto bail;
2419 }
2420 }
2421
2422 // At this point we've read everything and processed everything. From here
2423 // on out it's just writing output files.
2424 if (SourcePos::hasErrors()) {
2425 goto bail;
2426 }
2427
2428 // Update symbols with information about which ones are needed as Java symbols.
2429 assets->applyJavaSymbols();
2430 if (SourcePos::hasErrors()) {
2431 goto bail;
2432 }
2433
2434 // If we've been asked to generate a dependency file, do that here
2435 if (bundle->getGenDependencies()) {
2436 // If this is the packaging step, generate the dependency file next to
2437 // the output apk (e.g. bin/resources.ap_.d)
2438 if (outputAPKFile) {
2439 dependencyFile = String8(outputAPKFile);
2440 // Add the .d extension to the dependency file.
2441 dependencyFile.append(".d");
2442 } else {
2443 // Else if this is the R.java dependency generation step,
2444 // generate the dependency file in the R.java package subdirectory
2445 // e.g. gen/com/foo/app/R.java.d
2446 dependencyFile = String8(bundle->getRClassDir());
2447 dependencyFile.appendPath("R.java.d");
2448 }
2449 // Make sure we have a clean dependency file to start with
2450 fp = fopen(dependencyFile, "w");
2451 fclose(fp);
2452 }
2453
2454 // Write out R.java constants
2455 if (!assets->havePrivateSymbols()) {
2456 if (bundle->getCustomPackage() == NULL) {
2457 // Write the R.java file into the appropriate class directory
2458 // e.g. gen/com/foo/app/R.java
2459 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
2460 } else {
2461 const String8 customPkg(bundle->getCustomPackage());
2462 err = writeResourceSymbols(bundle, assets, customPkg, true);
2463 }
2464 if (err < 0) {
2465 goto bail;
2466 }
2467 // If we have library files, we're going to write our R.java file into
2468 // the appropriate class directory for those libraries as well.
2469 // e.g. gen/com/foo/app/lib/R.java
2470 if (bundle->getExtraPackages() != NULL) {
2471 // Split on colon
2472 String8 libs(bundle->getExtraPackages());
2473 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2474 while (packageString != NULL) {
2475 // Write the R.java file out with the correct package name
2476 err = writeResourceSymbols(bundle, assets, String8(packageString), true);
2477 if (err < 0) {
2478 goto bail;
2479 }
2480 packageString = strtok(NULL, ":");
2481 }
2482 libs.unlockBuffer();
2483 }
2484 } else {
2485 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
2486 if (err < 0) {
2487 goto bail;
2488 }
2489 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
2490 if (err < 0) {
2491 goto bail;
2492 }
2493 }
2494
2495 // Write out the ProGuard file
2496 err = writeProguardFile(bundle, assets);
2497 if (err < 0) {
2498 goto bail;
2499 }
2500
2501 // Write the apk
2502 if (outputAPKFile) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002503 // Gather all resources and add them to the APK Builder. The builder will then
2504 // figure out which Split they belong in.
2505 err = addResourcesToBuilder(assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002506 if (err != NO_ERROR) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002507 goto bail;
2508 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002509
2510 const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2511 const size_t numSplits = splits.size();
2512 for (size_t i = 0; i < numSplits; i++) {
2513 const sp<ApkSplit>& split = splits[i];
2514 String8 outputPath = buildApkName(String8(outputAPKFile), split);
2515 err = writeAPK(bundle, outputPath, split);
2516 if (err != NO_ERROR) {
2517 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2518 goto bail;
2519 }
2520 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002521 }
2522
2523 // If we've been asked to generate a dependency file, we need to finish up here.
2524 // the writeResourceSymbols and writeAPK functions have already written the target
2525 // half of the dependency file, now we need to write the prerequisites. (files that
2526 // the R.java file or .ap_ file depend on)
2527 if (bundle->getGenDependencies()) {
2528 // Now that writeResourceSymbols or writeAPK has taken care of writing
2529 // the targets to our dependency file, we'll write the prereqs
2530 fp = fopen(dependencyFile, "a+");
2531 fprintf(fp, " : ");
2532 bool includeRaw = (outputAPKFile != NULL);
2533 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2534 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2535 // and therefore was not added to our pathstores during slurping
2536 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2537 fclose(fp);
2538 }
2539
2540 retVal = 0;
2541bail:
2542 if (SourcePos::hasErrors()) {
2543 SourcePos::printErrors(stderr);
2544 }
2545 return retVal;
2546}
2547
2548/*
2549 * Do PNG Crunching
2550 * PRECONDITIONS
2551 * -S flag points to a source directory containing drawable* folders
2552 * -C flag points to destination directory. The folder structure in the
2553 * source directory will be mirrored to the destination (cache) directory
2554 *
2555 * POSTCONDITIONS
2556 * Destination directory will be updated to match the PNG files in
Maurice Chu2675f762013-10-22 17:33:11 -07002557 * the source directory.
Adam Lesinski282e1812014-01-23 18:17:42 -08002558 */
2559int doCrunch(Bundle* bundle)
2560{
2561 fprintf(stdout, "Crunching PNG Files in ");
2562 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2563 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2564
2565 updatePreProcessedCache(bundle);
2566
2567 return NO_ERROR;
2568}
2569
2570/*
2571 * Do PNG Crunching on a single flag
2572 * -i points to a single png file
2573 * -o points to a single png output file
2574 */
2575int doSingleCrunch(Bundle* bundle)
2576{
2577 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2578 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2579
2580 String8 input(bundle->getSingleCrunchInputFile());
2581 String8 output(bundle->getSingleCrunchOutputFile());
2582
2583 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2584 // we can't return the status_t as it gets truncate to the lower 8 bits.
2585 return 42;
2586 }
2587
2588 return NO_ERROR;
2589}
2590
2591char CONSOLE_DATA[2925] = {
2592 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2593 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2594 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2595 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2596 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2597 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2598 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2599 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2600 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2601 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2602 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2603 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2604 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2605 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2606 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2607 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2608 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2609 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2610 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2611 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2612 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2613 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2614 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2615 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2616 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2617 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2618 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2619 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2620 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2621 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2622 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2623 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2624 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2625 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2626 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2627 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2628 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2629 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2630 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2631 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2632 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2633 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2634 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2635 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2636 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2637 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2638 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2639 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2640 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2641 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2642 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2643 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2644 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2645 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2646 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2647 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2648 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2649 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2650 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2651 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2652 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2653 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2654 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2655 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2656 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2657 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2658 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2659 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2660 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2661 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2662 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2663 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2664 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2665 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2666 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2667 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2668 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2669 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2670 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2671 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2672 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2673 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2674 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2675 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2676 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2677 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2678 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2679 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2680 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2681 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2682 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2683 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2684 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2685 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2686 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2687 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2688 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2689 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2690 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2691 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2692 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2693 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2694 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2695 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2696 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2697 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2698 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2699 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2700 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2701 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2702 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2703 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2704 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2705 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2706 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2707 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2708 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2709 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2710 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2711 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2712 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2713 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2714 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2715 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2716 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2717 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2718 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2719 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2720 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2721 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2722 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2723 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2724 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2725 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2726 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2727 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2728 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2729 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2730 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2731 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2732 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2733 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2734 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2735 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2736 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2737 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2738 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2739 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2740 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2741 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2742 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2743 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2744 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2745 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2746 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2747 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2748 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2749 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2750 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2751 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2752 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2753 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2754 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2755 };