blob: 1dd43f79471d74dbe52f291a52cf5d82fa3b651a [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 Lesinskiad2d07d2014-08-27 16:21:08 -07006#include "AaptXml.h"
Adam Lesinskifab50872014-04-16 14:40:42 -07007#include "ApkBuilder.h"
Adam Lesinski282e1812014-01-23 18:17:42 -08008#include "Bundle.h"
Adam Lesinski2c72b682014-06-24 09:56:01 -07009#include "Images.h"
10#include "Main.h"
Adam Lesinski282e1812014-01-23 18:17:42 -080011#include "ResourceFilter.h"
12#include "ResourceTable.h"
Adam Lesinski282e1812014-01-23 18:17:42 -080013#include "XMLNode.h"
14
Adam Lesinski282e1812014-01-23 18:17:42 -080015#include <utils/Errors.h>
Adam Lesinski2c72b682014-06-24 09:56:01 -070016#include <utils/KeyedVector.h>
17#include <utils/List.h>
18#include <utils/Log.h>
19#include <utils/SortedVector.h>
20#include <utils/threads.h>
21#include <utils/Vector.h>
Adam Lesinski282e1812014-01-23 18:17:42 -080022
Adam Lesinski282e1812014-01-23 18:17:42 -080023#include <errno.h>
Adam Lesinski2c72b682014-06-24 09:56:01 -070024#include <fcntl.h>
Adam Lesinski282e1812014-01-23 18:17:42 -080025
Jerome Dochez6f1280c2014-09-26 10:21:21 -070026#include <iostream>
27#include <string>
28#include <sstream>
29
Adam Lesinski282e1812014-01-23 18:17:42 -080030using namespace android;
31
Adam Lesinskiad751222014-08-18 14:06:38 -070032#ifndef AAPT_VERSION
33 #define AAPT_VERSION ""
34#endif
35
Adam Lesinski282e1812014-01-23 18:17:42 -080036/*
37 * Show version info. All the cool kids do it.
38 */
39int doVersion(Bundle* bundle)
40{
41 if (bundle->getFileSpecCount() != 0) {
42 printf("(ignoring extra arguments)\n");
43 }
Adam Lesinskiad751222014-08-18 14:06:38 -070044 printf("Android Asset Packaging Tool, v0.2-" AAPT_VERSION "\n");
Adam Lesinski282e1812014-01-23 18:17:42 -080045
46 return 0;
47}
48
49
50/*
51 * Open the file read only. The call fails if the file doesn't exist.
52 *
53 * Returns NULL on failure.
54 */
55ZipFile* openReadOnly(const char* fileName)
56{
57 ZipFile* zip;
58 status_t result;
59
60 zip = new ZipFile;
61 result = zip->open(fileName, ZipFile::kOpenReadOnly);
62 if (result != NO_ERROR) {
63 if (result == NAME_NOT_FOUND) {
64 fprintf(stderr, "ERROR: '%s' not found\n", fileName);
65 } else if (result == PERMISSION_DENIED) {
66 fprintf(stderr, "ERROR: '%s' access denied\n", fileName);
67 } else {
68 fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n",
69 fileName);
70 }
71 delete zip;
72 return NULL;
73 }
74
75 return zip;
76}
77
78/*
79 * Open the file read-write. The file will be created if it doesn't
80 * already exist and "okayToCreate" is set.
81 *
82 * Returns NULL on failure.
83 */
84ZipFile* openReadWrite(const char* fileName, bool okayToCreate)
85{
86 ZipFile* zip = NULL;
87 status_t result;
88 int flags;
89
90 flags = ZipFile::kOpenReadWrite;
91 if (okayToCreate) {
92 flags |= ZipFile::kOpenCreate;
93 }
94
95 zip = new ZipFile;
96 result = zip->open(fileName, flags);
97 if (result != NO_ERROR) {
98 delete zip;
99 zip = NULL;
100 goto bail;
101 }
102
103bail:
104 return zip;
105}
106
107
108/*
109 * Return a short string describing the compression method.
110 */
111const char* compressionName(int method)
112{
113 if (method == ZipEntry::kCompressStored) {
114 return "Stored";
115 } else if (method == ZipEntry::kCompressDeflated) {
116 return "Deflated";
117 } else {
118 return "Unknown";
119 }
120}
121
122/*
123 * Return the percent reduction in size (0% == no compression).
124 */
125int calcPercent(long uncompressedLen, long compressedLen)
126{
127 if (!uncompressedLen) {
128 return 0;
129 } else {
130 return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5);
131 }
132}
133
134/*
135 * Handle the "list" command, which can be a simple file dump or
136 * a verbose listing.
137 *
138 * The verbose listing closely matches the output of the Info-ZIP "unzip"
139 * command.
140 */
141int doList(Bundle* bundle)
142{
143 int result = 1;
144 ZipFile* zip = NULL;
145 const ZipEntry* entry;
146 long totalUncLen, totalCompLen;
147 const char* zipFileName;
148
149 if (bundle->getFileSpecCount() != 1) {
150 fprintf(stderr, "ERROR: specify zip file name (only)\n");
151 goto bail;
152 }
153 zipFileName = bundle->getFileSpecEntry(0);
154
155 zip = openReadOnly(zipFileName);
156 if (zip == NULL) {
157 goto bail;
158 }
159
160 int count, i;
161
162 if (bundle->getVerbose()) {
163 printf("Archive: %s\n", zipFileName);
164 printf(
165 " Length Method Size Ratio Offset Date Time CRC-32 Name\n");
166 printf(
167 "-------- ------ ------- ----- ------- ---- ---- ------ ----\n");
168 }
169
170 totalUncLen = totalCompLen = 0;
171
172 count = zip->getNumEntries();
173 for (i = 0; i < count; i++) {
174 entry = zip->getEntryByIndex(i);
175 if (bundle->getVerbose()) {
176 char dateBuf[32];
177 time_t when;
178
179 when = entry->getModWhen();
180 strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M",
181 localtime(&when));
182
183 printf("%8ld %-7.7s %7ld %3d%% %8zd %s %08lx %s\n",
184 (long) entry->getUncompressedLen(),
185 compressionName(entry->getCompressionMethod()),
186 (long) entry->getCompressedLen(),
187 calcPercent(entry->getUncompressedLen(),
188 entry->getCompressedLen()),
189 (size_t) entry->getLFHOffset(),
190 dateBuf,
191 entry->getCRC32(),
192 entry->getFileName());
193 } else {
194 printf("%s\n", entry->getFileName());
195 }
196
197 totalUncLen += entry->getUncompressedLen();
198 totalCompLen += entry->getCompressedLen();
199 }
200
201 if (bundle->getVerbose()) {
202 printf(
203 "-------- ------- --- -------\n");
204 printf("%8ld %7ld %2d%% %d files\n",
205 totalUncLen,
206 totalCompLen,
207 calcPercent(totalUncLen, totalCompLen),
208 zip->getNumEntries());
209 }
210
211 if (bundle->getAndroidList()) {
212 AssetManager assets;
213 if (!assets.addAssetPath(String8(zipFileName), NULL)) {
214 fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n");
215 goto bail;
216 }
217
Elliott Hughesba3fe562015-08-12 14:49:53 -0700218#ifdef __ANDROID__
Andreas Gampeb8dc7bc2014-10-01 19:07:51 -0700219 static const bool kHaveAndroidOs = true;
220#else
221 static const bool kHaveAndroidOs = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800222#endif
Andreas Gampeb8dc7bc2014-10-01 19:07:51 -0700223 const ResTable& res = assets.getResources(false);
224 if (!kHaveAndroidOs) {
225 printf("\nResource table:\n");
226 res.print(false);
227 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800228
229 Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
230 Asset::ACCESS_BUFFER);
231 if (manifestAsset == NULL) {
232 printf("\nNo AndroidManifest.xml found.\n");
233 } else {
234 printf("\nAndroid manifest:\n");
235 ResXMLTree tree;
236 tree.setTo(manifestAsset->getBuffer(true),
237 manifestAsset->getLength());
238 printXMLBlock(&tree);
239 }
240 delete manifestAsset;
241 }
242
243 result = 0;
244
245bail:
246 delete zip;
247 return result;
248}
249
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700250static void printResolvedResourceAttribute(const ResTable& resTable, const ResXMLTree& tree,
Chih-Hung Hsieh8bd37ba2016-08-10 14:15:30 -0700251 uint32_t attrRes, const String8& attrLabel, String8* outError)
Maurice Chu76327312013-10-16 18:28:46 -0700252{
253 Res_value value;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700254 AaptXml::getResolvedResourceAttribute(resTable, tree, attrRes, &value, outError);
Maurice Chu76327312013-10-16 18:28:46 -0700255 if (*outError != "") {
256 *outError = "error print resolved resource attribute";
257 return;
258 }
259 if (value.dataType == Res_value::TYPE_STRING) {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700260 String8 result = AaptXml::getResolvedAttribute(resTable, tree, attrRes, outError);
Maurice Chu2675f762013-10-22 17:33:11 -0700261 printf("%s='%s'", attrLabel.string(),
262 ResTable::normalizeForOutput(result.string()).string());
Maurice Chu76327312013-10-16 18:28:46 -0700263 } else if (Res_value::TYPE_FIRST_INT <= value.dataType &&
264 value.dataType <= Res_value::TYPE_LAST_INT) {
265 printf("%s='%d'", attrLabel.string(), value.data);
266 } else {
267 printf("%s='0x%x'", attrLabel.string(), (int)value.data);
268 }
269}
270
Adam Lesinski282e1812014-01-23 18:17:42 -0800271// These are attribute resource constants for the platform, as found
272// in android.R.attr
273enum {
274 LABEL_ATTR = 0x01010001,
275 ICON_ATTR = 0x01010002,
276 NAME_ATTR = 0x01010003,
Adam Lesinskia5018c92013-09-30 16:23:15 -0700277 PERMISSION_ATTR = 0x01010006,
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700278 EXPORTED_ATTR = 0x01010010,
279 GRANT_URI_PERMISSIONS_ATTR = 0x0101001b,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700280 RESOURCE_ATTR = 0x01010025,
Adam Lesinski282e1812014-01-23 18:17:42 -0800281 DEBUGGABLE_ATTR = 0x0101000f,
282 VALUE_ATTR = 0x01010024,
283 VERSION_CODE_ATTR = 0x0101021b,
284 VERSION_NAME_ATTR = 0x0101021c,
285 SCREEN_ORIENTATION_ATTR = 0x0101001e,
286 MIN_SDK_VERSION_ATTR = 0x0101020c,
287 MAX_SDK_VERSION_ATTR = 0x01010271,
288 REQ_TOUCH_SCREEN_ATTR = 0x01010227,
289 REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
290 REQ_HARD_KEYBOARD_ATTR = 0x01010229,
291 REQ_NAVIGATION_ATTR = 0x0101022a,
292 REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
293 TARGET_SDK_VERSION_ATTR = 0x01010270,
294 TEST_ONLY_ATTR = 0x01010272,
295 ANY_DENSITY_ATTR = 0x0101026c,
296 GL_ES_VERSION_ATTR = 0x01010281,
297 SMALL_SCREEN_ATTR = 0x01010284,
298 NORMAL_SCREEN_ATTR = 0x01010285,
299 LARGE_SCREEN_ATTR = 0x01010286,
300 XLARGE_SCREEN_ATTR = 0x010102bf,
301 REQUIRED_ATTR = 0x0101028e,
Adam Lesinskicaf797c2014-08-22 12:56:26 -0700302 INSTALL_LOCATION_ATTR = 0x010102b7,
Adam Lesinski282e1812014-01-23 18:17:42 -0800303 SCREEN_SIZE_ATTR = 0x010102ca,
304 SCREEN_DENSITY_ATTR = 0x010102cb,
305 REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
306 COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
307 LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
308 PUBLIC_KEY_ATTR = 0x010103a6,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700309 CATEGORY_ATTR = 0x010103e8,
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800310 BANNER_ATTR = 0x10103f2,
Tim Kilbournd9b1cad2014-10-24 12:43:41 -0700311 ISGAME_ATTR = 0x10103f4,
Adam Lesinski282e1812014-01-23 18:17:42 -0800312};
313
Maurice Chu2675f762013-10-22 17:33:11 -0700314String8 getComponentName(String8 &pkgName, String8 &componentName) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800315 ssize_t idx = componentName.find(".");
316 String8 retStr(pkgName);
317 if (idx == 0) {
318 retStr += componentName;
319 } else if (idx < 0) {
320 retStr += ".";
321 retStr += componentName;
322 } else {
Maurice Chu2675f762013-10-22 17:33:11 -0700323 return componentName;
Adam Lesinski282e1812014-01-23 18:17:42 -0800324 }
Maurice Chu2675f762013-10-22 17:33:11 -0700325 return retStr;
Adam Lesinski282e1812014-01-23 18:17:42 -0800326}
327
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700328static void printCompatibleScreens(ResXMLTree& tree, String8* outError) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800329 size_t len;
330 ResXMLTree::event_code_t code;
331 int depth = 0;
332 bool first = true;
333 printf("compatible-screens:");
334 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
335 if (code == ResXMLTree::END_TAG) {
336 depth--;
337 if (depth < 0) {
338 break;
339 }
340 continue;
341 }
342 if (code != ResXMLTree::START_TAG) {
343 continue;
344 }
345 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700346 const char16_t* ctag16 = tree.getElementName(&len);
347 if (ctag16 == NULL) {
348 *outError = "failed to get XML element name (bad string pool)";
349 return;
350 }
351 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800352 if (tag == "screen") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700353 int32_t screenSize = AaptXml::getIntegerAttribute(tree,
354 SCREEN_SIZE_ATTR);
355 int32_t screenDensity = AaptXml::getIntegerAttribute(tree,
356 SCREEN_DENSITY_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -0800357 if (screenSize > 0 && screenDensity > 0) {
358 if (!first) {
359 printf(",");
360 }
361 first = false;
362 printf("'%d/%d'", screenSize, screenDensity);
363 }
364 }
365 }
366 printf("\n");
367}
368
Adam Lesinski58f1f362013-11-12 12:59:08 -0800369static void printUsesPermission(const String8& name, bool optional=false, int maxSdkVersion=-1) {
370 printf("uses-permission: name='%s'", ResTable::normalizeForOutput(name.string()).string());
371 if (maxSdkVersion != -1) {
372 printf(" maxSdkVersion='%d'", maxSdkVersion);
373 }
374 printf("\n");
375
376 if (optional) {
377 printf("optional-permission: name='%s'",
378 ResTable::normalizeForOutput(name.string()).string());
379 if (maxSdkVersion != -1) {
380 printf(" maxSdkVersion='%d'", maxSdkVersion);
381 }
382 printf("\n");
383 }
384}
385
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800386static void printUsesPermissionSdk23(const String8& name, int maxSdkVersion=-1) {
387 printf("uses-permission-sdk-23: ");
388
389 printf("name='%s'", ResTable::normalizeForOutput(name.string()).string());
390 if (maxSdkVersion != -1) {
391 printf(" maxSdkVersion='%d'", maxSdkVersion);
392 }
393 printf("\n");
394}
395
Adam Lesinski58f1f362013-11-12 12:59:08 -0800396static void printUsesImpliedPermission(const String8& name, const String8& reason) {
397 printf("uses-implied-permission: name='%s' reason='%s'\n",
398 ResTable::normalizeForOutput(name.string()).string(),
399 ResTable::normalizeForOutput(reason.string()).string());
400}
401
Chih-Hung Hsieh8bd37ba2016-08-10 14:15:30 -0700402Vector<String8> getNfcAidCategories(AssetManager& assets, const String8& xmlPath, bool offHost,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700403 String8 *outError = NULL)
404{
405 Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER);
406 if (aidAsset == NULL) {
407 if (outError != NULL) *outError = "xml resource does not exist";
408 return Vector<String8>();
409 }
410
411 const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service");
412
413 bool withinApduService = false;
414 Vector<String8> categories;
415
416 String8 error;
417 ResXMLTree tree;
418 tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength());
419
420 size_t len;
421 int depth = 0;
422 ResXMLTree::event_code_t code;
423 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
424 if (code == ResXMLTree::END_TAG) {
425 depth--;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700426 const char16_t* ctag16 = tree.getElementName(&len);
427 if (ctag16 == NULL) {
428 *outError = "failed to get XML element name (bad string pool)";
429 return Vector<String8>();
430 }
431 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700432
433 if (depth == 0 && tag == serviceTagName) {
434 withinApduService = false;
435 }
436
437 } else if (code == ResXMLTree::START_TAG) {
438 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700439 const char16_t* ctag16 = tree.getElementName(&len);
440 if (ctag16 == NULL) {
441 *outError = "failed to get XML element name (bad string pool)";
442 return Vector<String8>();
443 }
444 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700445
446 if (depth == 1) {
447 if (tag == serviceTagName) {
448 withinApduService = true;
449 }
450 } else if (depth == 2 && withinApduService) {
451 if (tag == "aid-group") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700452 String8 category = AaptXml::getAttribute(tree, CATEGORY_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700453 if (error != "") {
454 if (outError != NULL) *outError = error;
455 return Vector<String8>();
456 }
457
458 categories.add(category);
459 }
460 }
461 }
462 }
463 aidAsset->close();
464 return categories;
465}
466
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700467static void printComponentPresence(const char* componentName) {
468 printf("provides-component:'%s'\n", componentName);
469}
470
Adam Lesinski2c72b682014-06-24 09:56:01 -0700471/**
472 * Represents a feature that has been automatically added due to
473 * a pre-requisite or some other reason.
474 */
475struct ImpliedFeature {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800476 ImpliedFeature() : impliedBySdk23(false) {}
477 ImpliedFeature(const String8& n, bool sdk23) : name(n), impliedBySdk23(sdk23) {}
478
Adam Lesinski2c72b682014-06-24 09:56:01 -0700479 /**
480 * Name of the implied feature.
481 */
482 String8 name;
483
484 /**
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800485 * Was this implied by a permission from SDK 23 (<uses-permission-sdk-23 />)?
486 */
487 bool impliedBySdk23;
488
489 /**
Adam Lesinski2c72b682014-06-24 09:56:01 -0700490 * List of human-readable reasons for why this feature was implied.
491 */
492 SortedVector<String8> reasons;
493};
494
Adam Lesinski694d0a72016-04-06 16:12:04 -0700495struct Feature {
496 Feature() : required(false), version(-1) {}
497 Feature(bool required, int32_t version = -1) : required(required), version(version) {}
498
499 /**
500 * Whether the feature is required.
501 */
502 bool required;
503
504 /**
505 * What version of the feature is requested.
506 */
507 int32_t version;
508};
509
Adam Lesinski2c72b682014-06-24 09:56:01 -0700510/**
511 * Represents a <feature-group> tag in the AndroidManifest.xml
512 */
513struct FeatureGroup {
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700514 FeatureGroup() : openGLESVersion(-1) {}
515
Adam Lesinski2c72b682014-06-24 09:56:01 -0700516 /**
517 * Human readable label
518 */
519 String8 label;
520
521 /**
522 * Explicit features defined in the group
523 */
Adam Lesinski694d0a72016-04-06 16:12:04 -0700524 KeyedVector<String8, Feature> features;
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700525
526 /**
527 * OpenGL ES version required
528 */
529 int openGLESVersion;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700530};
531
Adam Lesinskica955a42016-08-01 16:44:29 -0700532static bool hasFeature(const char* name, const FeatureGroup& grp,
533 const KeyedVector<String8, ImpliedFeature>& implied) {
534 String8 name8(name);
535 ssize_t idx = grp.features.indexOfKey(name8);
536 if (idx < 0) {
537 idx = implied.indexOfKey(name8);
538 }
539 return idx >= 0;
540}
541
Adam Lesinski2c72b682014-06-24 09:56:01 -0700542static void addImpliedFeature(KeyedVector<String8, ImpliedFeature>* impliedFeatures,
Adam Lesinski43158772015-11-11 15:13:55 -0800543 const char* name, const String8& reason, bool sdk23) {
Adam Lesinski2c72b682014-06-24 09:56:01 -0700544 String8 name8(name);
545 ssize_t idx = impliedFeatures->indexOfKey(name8);
546 if (idx < 0) {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800547 idx = impliedFeatures->add(name8, ImpliedFeature(name8, sdk23));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700548 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800549
550 ImpliedFeature* feature = &impliedFeatures->editValueAt(idx);
551
552 // A non-sdk 23 implied feature takes precedence.
553 if (feature->impliedBySdk23 && !sdk23) {
554 feature->impliedBySdk23 = false;
555 }
Adam Lesinski43158772015-11-11 15:13:55 -0800556 feature->reasons.add(reason);
Adam Lesinski2c72b682014-06-24 09:56:01 -0700557}
558
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800559static void printFeatureGroupImpl(const FeatureGroup& grp,
560 const KeyedVector<String8, ImpliedFeature>* impliedFeatures) {
Adam Lesinski2c72b682014-06-24 09:56:01 -0700561 printf("feature-group: label='%s'\n", grp.label.string());
562
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700563 if (grp.openGLESVersion > 0) {
564 printf(" uses-gl-es: '0x%x'\n", grp.openGLESVersion);
565 }
566
Adam Lesinski2c72b682014-06-24 09:56:01 -0700567 const size_t numFeatures = grp.features.size();
568 for (size_t i = 0; i < numFeatures; i++) {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700569 const Feature& feature = grp.features[i];
570 const bool required = feature.required;
571 const int32_t version = feature.version;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700572
573 const String8& featureName = grp.features.keyAt(i);
Adam Lesinski694d0a72016-04-06 16:12:04 -0700574 printf(" uses-feature%s: name='%s'", (required ? "" : "-not-required"),
Adam Lesinski2c72b682014-06-24 09:56:01 -0700575 ResTable::normalizeForOutput(featureName.string()).string());
Adam Lesinski694d0a72016-04-06 16:12:04 -0700576
577 if (version > 0) {
578 printf(" version='%d'", version);
579 }
580 printf("\n");
Adam Lesinski2c72b682014-06-24 09:56:01 -0700581 }
582
583 const size_t numImpliedFeatures =
584 (impliedFeatures != NULL) ? impliedFeatures->size() : 0;
585 for (size_t i = 0; i < numImpliedFeatures; i++) {
586 const ImpliedFeature& impliedFeature = impliedFeatures->valueAt(i);
587 if (grp.features.indexOfKey(impliedFeature.name) >= 0) {
588 // The feature is explicitly set, no need to use implied
589 // definition.
590 continue;
591 }
592
593 String8 printableFeatureName(ResTable::normalizeForOutput(
594 impliedFeature.name.string()));
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800595 const char* sdk23Suffix = impliedFeature.impliedBySdk23 ? "-sdk-23" : "";
596
597 printf(" uses-feature%s: name='%s'\n", sdk23Suffix, printableFeatureName.string());
598 printf(" uses-implied-feature%s: name='%s' reason='", sdk23Suffix,
599 printableFeatureName.string());
Adam Lesinski2c72b682014-06-24 09:56:01 -0700600 const size_t numReasons = impliedFeature.reasons.size();
601 for (size_t j = 0; j < numReasons; j++) {
602 printf("%s", impliedFeature.reasons[j].string());
603 if (j + 2 < numReasons) {
604 printf(", ");
605 } else if (j + 1 < numReasons) {
606 printf(", and ");
607 }
608 }
609 printf("'\n");
610 }
611}
612
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800613static void printFeatureGroup(const FeatureGroup& grp) {
614 printFeatureGroupImpl(grp, NULL);
615}
616
617static void printDefaultFeatureGroup(const FeatureGroup& grp,
618 const KeyedVector<String8, ImpliedFeature>& impliedFeatures) {
619 printFeatureGroupImpl(grp, &impliedFeatures);
620}
621
Adam Lesinski2c72b682014-06-24 09:56:01 -0700622static void addParentFeatures(FeatureGroup* grp, const String8& name) {
623 if (name == "android.hardware.camera.autofocus" ||
624 name == "android.hardware.camera.flash") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700625 grp->features.add(String8("android.hardware.camera"), Feature(true));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700626 } else if (name == "android.hardware.location.gps" ||
627 name == "android.hardware.location.network") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700628 grp->features.add(String8("android.hardware.location"), Feature(true));
Adam Lesinskica955a42016-08-01 16:44:29 -0700629 } else if (name == "android.hardware.faketouch.multitouch") {
630 grp->features.add(String8("android.hardware.faketouch"), Feature(true));
631 } else if (name == "android.hardware.faketouch.multitouch.distinct" ||
632 name == "android.hardware.faketouch.multitouch.jazzhands") {
633 grp->features.add(String8("android.hardware.faketouch.multitouch"), Feature(true));
634 grp->features.add(String8("android.hardware.faketouch"), Feature(true));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700635 } else if (name == "android.hardware.touchscreen.multitouch") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700636 grp->features.add(String8("android.hardware.touchscreen"), Feature(true));
Adam Lesinskica955a42016-08-01 16:44:29 -0700637 } else if (name == "android.hardware.touchscreen.multitouch.distinct" ||
638 name == "android.hardware.touchscreen.multitouch.jazzhands") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700639 grp->features.add(String8("android.hardware.touchscreen.multitouch"), Feature(true));
640 grp->features.add(String8("android.hardware.touchscreen"), Feature(true));
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700641 } else if (name == "android.hardware.opengles.aep") {
642 const int openGLESVersion31 = 0x00030001;
643 if (openGLESVersion31 > grp->openGLESVersion) {
644 grp->openGLESVersion = openGLESVersion31;
645 }
Adam Lesinski2c72b682014-06-24 09:56:01 -0700646 }
647}
648
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800649static void addImpliedFeaturesForPermission(const int targetSdk, const String8& name,
650 KeyedVector<String8, ImpliedFeature>* impliedFeatures,
651 bool impliedBySdk23Permission) {
652 if (name == "android.permission.CAMERA") {
653 addImpliedFeature(impliedFeatures, "android.hardware.camera",
Adam Lesinski43158772015-11-11 15:13:55 -0800654 String8::format("requested %s permission", name.string()),
655 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800656 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
Adam Lesinski43158772015-11-11 15:13:55 -0800657 if (targetSdk < SDK_LOLLIPOP) {
658 addImpliedFeature(impliedFeatures, "android.hardware.location.gps",
659 String8::format("requested %s permission", name.string()),
660 impliedBySdk23Permission);
661 addImpliedFeature(impliedFeatures, "android.hardware.location.gps",
662 String8::format("targetSdkVersion < %d", SDK_LOLLIPOP),
663 impliedBySdk23Permission);
664 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800665 addImpliedFeature(impliedFeatures, "android.hardware.location",
Adam Lesinski43158772015-11-11 15:13:55 -0800666 String8::format("requested %s permission", name.string()),
667 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800668 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
Adam Lesinski43158772015-11-11 15:13:55 -0800669 if (targetSdk < SDK_LOLLIPOP) {
670 addImpliedFeature(impliedFeatures, "android.hardware.location.network",
671 String8::format("requested %s permission", name.string()),
672 impliedBySdk23Permission);
673 addImpliedFeature(impliedFeatures, "android.hardware.location.network",
674 String8::format("targetSdkVersion < %d", SDK_LOLLIPOP),
675 impliedBySdk23Permission);
676 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800677 addImpliedFeature(impliedFeatures, "android.hardware.location",
Adam Lesinski43158772015-11-11 15:13:55 -0800678 String8::format("requested %s permission", name.string()),
679 impliedBySdk23Permission);
680 } else if (name == "android.permission.ACCESS_MOCK_LOCATION" ||
681 name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800682 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
683 addImpliedFeature(impliedFeatures, "android.hardware.location",
Adam Lesinski43158772015-11-11 15:13:55 -0800684 String8::format("requested %s permission", name.string()),
685 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800686 } else if (name == "android.permission.BLUETOOTH" ||
687 name == "android.permission.BLUETOOTH_ADMIN") {
Adam Lesinski43158772015-11-11 15:13:55 -0800688 if (targetSdk > SDK_DONUT) {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800689 addImpliedFeature(impliedFeatures, "android.hardware.bluetooth",
Adam Lesinski43158772015-11-11 15:13:55 -0800690 String8::format("requested %s permission", name.string()),
691 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800692 addImpliedFeature(impliedFeatures, "android.hardware.bluetooth",
Adam Lesinski43158772015-11-11 15:13:55 -0800693 String8::format("targetSdkVersion > %d", SDK_DONUT),
694 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800695 }
696 } else if (name == "android.permission.RECORD_AUDIO") {
697 addImpliedFeature(impliedFeatures, "android.hardware.microphone",
Adam Lesinski43158772015-11-11 15:13:55 -0800698 String8::format("requested %s permission", name.string()),
699 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800700 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
701 name == "android.permission.CHANGE_WIFI_STATE" ||
702 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
703 addImpliedFeature(impliedFeatures, "android.hardware.wifi",
Adam Lesinski43158772015-11-11 15:13:55 -0800704 String8::format("requested %s permission", name.string()),
705 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800706 } else if (name == "android.permission.CALL_PHONE" ||
707 name == "android.permission.CALL_PRIVILEGED" ||
708 name == "android.permission.MODIFY_PHONE_STATE" ||
709 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
710 name == "android.permission.READ_SMS" ||
711 name == "android.permission.RECEIVE_SMS" ||
712 name == "android.permission.RECEIVE_MMS" ||
713 name == "android.permission.RECEIVE_WAP_PUSH" ||
714 name == "android.permission.SEND_SMS" ||
715 name == "android.permission.WRITE_APN_SETTINGS" ||
716 name == "android.permission.WRITE_SMS") {
717 addImpliedFeature(impliedFeatures, "android.hardware.telephony",
Adam Lesinski43158772015-11-11 15:13:55 -0800718 String8("requested a telephony permission"),
719 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800720 }
721}
722
Adam Lesinski282e1812014-01-23 18:17:42 -0800723/*
724 * Handle the "dump" command, to extract select data from an archive.
725 */
726extern char CONSOLE_DATA[2925]; // see EOF
727int doDump(Bundle* bundle)
728{
729 status_t result = UNKNOWN_ERROR;
Adam Lesinski282e1812014-01-23 18:17:42 -0800730
731 if (bundle->getFileSpecCount() < 1) {
732 fprintf(stderr, "ERROR: no dump option specified\n");
733 return 1;
734 }
735
736 if (bundle->getFileSpecCount() < 2) {
737 fprintf(stderr, "ERROR: no dump file specified\n");
738 return 1;
739 }
740
741 const char* option = bundle->getFileSpecEntry(0);
742 const char* filename = bundle->getFileSpecEntry(1);
743
744 AssetManager assets;
Narayan Kamathf85e41f2014-01-24 14:14:30 +0000745 int32_t assetsCookie;
Adam Lesinski282e1812014-01-23 18:17:42 -0800746 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
747 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
748 return 1;
749 }
750
751 // Make a dummy config for retrieving resources... we need to supply
752 // non-default values for some configs so that we can retrieve resources
753 // in the app that don't have a default. The most important of these is
754 // the API version because key resources like icons will have an implicit
755 // version if they are using newer config types like density.
756 ResTable_config config;
Narayan Kamath91447d82014-01-21 15:32:36 +0000757 memset(&config, 0, sizeof(ResTable_config));
Adam Lesinski282e1812014-01-23 18:17:42 -0800758 config.language[0] = 'e';
759 config.language[1] = 'n';
760 config.country[0] = 'U';
761 config.country[1] = 'S';
762 config.orientation = ResTable_config::ORIENTATION_PORT;
763 config.density = ResTable_config::DENSITY_MEDIUM;
764 config.sdkVersion = 10000; // Very high.
765 config.screenWidthDp = 320;
766 config.screenHeightDp = 480;
767 config.smallestScreenWidthDp = 320;
Adam Lesinskic2dea8d2014-08-04 16:40:41 -0700768 config.screenLayout |= ResTable_config::SCREENSIZE_NORMAL;
Adam Lesinski282e1812014-01-23 18:17:42 -0800769 assets.setConfiguration(config);
770
771 const ResTable& res = assets.getResources(false);
Dan Albert68001652014-09-09 09:51:01 -0700772 if (res.getError() != NO_ERROR) {
Adam Lesinski25e9d552014-05-19 15:01:43 -0700773 fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
Adam Lesinski63e646e2014-07-30 11:40:39 -0700774 return 1;
Adam Lesinski282e1812014-01-23 18:17:42 -0800775 }
776
Adam Lesinski694d0a72016-04-06 16:12:04 -0700777 // Source for AndroidManifest.xml
778 const String8 manifestFile = String8::format("%s@AndroidManifest.xml", filename);
779
Adam Lesinski2cb761e2014-08-15 13:59:02 -0700780 // The dynamicRefTable can be null if there are no resources for this asset cookie.
781 // This fine.
Adam Lesinski63e646e2014-07-30 11:40:39 -0700782 const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700783
784 Asset* asset = NULL;
785
Adam Lesinski282e1812014-01-23 18:17:42 -0800786 if (strcmp("resources", option) == 0) {
Elliott Hughesba3fe562015-08-12 14:49:53 -0700787#ifndef __ANDROID__
Adam Lesinski282e1812014-01-23 18:17:42 -0800788 res.print(bundle->getValues());
789#endif
790
791 } else if (strcmp("strings", option) == 0) {
792 const ResStringPool* pool = res.getTableStringBlock(0);
793 printStringPool(pool);
794
795 } else if (strcmp("xmltree", option) == 0) {
796 if (bundle->getFileSpecCount() < 3) {
797 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
798 goto bail;
799 }
800
801 for (int i=2; i<bundle->getFileSpecCount(); i++) {
802 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700803 ResXMLTree tree(dynamicRefTable);
804 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800805 if (asset == NULL) {
806 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
807 goto bail;
808 }
809
810 if (tree.setTo(asset->getBuffer(true),
811 asset->getLength()) != NO_ERROR) {
812 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
813 goto bail;
814 }
815 tree.restart();
816 printXMLBlock(&tree);
817 tree.uninit();
818 delete asset;
819 asset = NULL;
820 }
821
822 } else if (strcmp("xmlstrings", option) == 0) {
823 if (bundle->getFileSpecCount() < 3) {
824 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
825 goto bail;
826 }
827
828 for (int i=2; i<bundle->getFileSpecCount(); i++) {
829 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700830 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800831 if (asset == NULL) {
832 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
833 goto bail;
834 }
835
Adam Lesinski63e646e2014-07-30 11:40:39 -0700836 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800837 if (tree.setTo(asset->getBuffer(true),
838 asset->getLength()) != NO_ERROR) {
839 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
840 goto bail;
841 }
842 printStringPool(&tree.getStrings());
843 delete asset;
844 asset = NULL;
845 }
846
847 } else {
Adam Lesinski63e646e2014-07-30 11:40:39 -0700848 asset = assets.openNonAsset(assetsCookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800849 if (asset == NULL) {
850 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
851 goto bail;
852 }
853
Adam Lesinski63e646e2014-07-30 11:40:39 -0700854 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800855 if (tree.setTo(asset->getBuffer(true),
856 asset->getLength()) != NO_ERROR) {
857 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
858 goto bail;
859 }
860 tree.restart();
861
862 if (strcmp("permissions", option) == 0) {
863 size_t len;
864 ResXMLTree::event_code_t code;
865 int depth = 0;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800866 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
867 code != ResXMLTree::BAD_DOCUMENT) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800868 if (code == ResXMLTree::END_TAG) {
869 depth--;
870 continue;
871 }
872 if (code != ResXMLTree::START_TAG) {
873 continue;
874 }
875 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700876 const char16_t* ctag16 = tree.getElementName(&len);
877 if (ctag16 == NULL) {
878 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
879 goto bail;
880 }
881 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800882 //printf("Depth %d tag %s\n", depth, tag.string());
883 if (depth == 1) {
884 if (tag != "manifest") {
885 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
886 goto bail;
887 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700888 String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -0700889 printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800890 } else if (depth == 2) {
891 if (tag == "permission") {
892 String8 error;
893 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
894 if (error != "") {
895 fprintf(stderr, "ERROR: %s\n", error.string());
896 goto bail;
897 }
898
899 if (name == "") {
900 fprintf(stderr, "ERROR: missing 'android:name' for permission\n");
901 goto bail;
902 }
903 printf("permission: %s\n",
904 ResTable::normalizeForOutput(name.string()).string());
905 } else if (tag == "uses-permission") {
906 String8 error;
907 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
908 if (error != "") {
909 fprintf(stderr, "ERROR: %s\n", error.string());
910 goto bail;
911 }
912
913 if (name == "") {
914 fprintf(stderr, "ERROR: missing 'android:name' for uses-permission\n");
915 goto bail;
916 }
917 printUsesPermission(name,
918 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
919 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
920 } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
921 String8 error;
922 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
923 if (error != "") {
924 fprintf(stderr, "ERROR: %s\n", error.string());
925 goto bail;
926 }
927
928 if (name == "") {
929 fprintf(stderr, "ERROR: missing 'android:name' for "
930 "uses-permission-sdk-23\n");
931 goto bail;
932 }
933 printUsesPermissionSdk23(
934 name,
935 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
Adam Lesinski282e1812014-01-23 18:17:42 -0800936 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800937 }
938 }
939 } else if (strcmp("badging", option) == 0) {
940 Vector<String8> locales;
941 res.getLocales(&locales);
942
943 Vector<ResTable_config> configs;
944 res.getConfigurations(&configs);
945 SortedVector<int> densities;
946 const size_t NC = configs.size();
947 for (size_t i=0; i<NC; i++) {
948 int dens = configs[i].density;
949 if (dens == 0) {
950 dens = 160;
951 }
952 densities.add(dens);
953 }
954
955 size_t len;
956 ResXMLTree::event_code_t code;
957 int depth = 0;
958 String8 error;
959 bool withinActivity = false;
960 bool isMainActivity = false;
961 bool isLauncherActivity = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800962 bool isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800963 bool isSearchable = false;
964 bool withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700965 bool withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700966 bool withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800967 bool withinReceiver = false;
968 bool withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700969 bool withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800970 bool withinIntentFilter = false;
971 bool hasMainActivity = false;
972 bool hasOtherActivities = false;
973 bool hasOtherReceivers = false;
974 bool hasOtherServices = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700975 bool hasIntentFilter = false;
976
Adam Lesinski282e1812014-01-23 18:17:42 -0800977 bool hasWallpaperService = false;
978 bool hasImeService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700979 bool hasAccessibilityService = false;
980 bool hasPrintService = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800981 bool hasWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700982 bool hasDeviceAdminReceiver = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700983 bool hasPaymentService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700984 bool hasDocumentsProvider = false;
985 bool hasCameraActivity = false;
986 bool hasCameraSecureActivity = false;
987 bool hasLauncher = false;
988 bool hasNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400989 bool hasDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700990
Adam Lesinski282e1812014-01-23 18:17:42 -0800991 bool actMainActivity = false;
992 bool actWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700993 bool actDeviceAdminEnabled = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800994 bool actImeService = false;
995 bool actWallpaperService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700996 bool actAccessibilityService = false;
997 bool actPrintService = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700998 bool actHostApduService = false;
999 bool actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001000 bool actDocumentsProvider = false;
1001 bool actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001002 bool actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001003 bool actCamera = false;
1004 bool actCameraSecure = false;
1005 bool catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001006 bool hasMetaHostPaymentCategory = false;
1007 bool hasMetaOffHostPaymentCategory = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001008
1009 // These permissions are required by services implementing services
1010 // the system binds to (IME, Accessibility, PrintServices, etc.)
1011 bool hasBindDeviceAdminPermission = false;
1012 bool hasBindInputMethodPermission = false;
1013 bool hasBindAccessibilityServicePermission = false;
1014 bool hasBindPrintServicePermission = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001015 bool hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001016 bool hasRequiredSafAttributes = false;
1017 bool hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001018 bool hasBindDreamServicePermission = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001019
1020 // These two implement the implicit permissions that are granted
1021 // to pre-1.6 applications.
1022 bool hasWriteExternalStoragePermission = false;
1023 bool hasReadPhoneStatePermission = false;
1024
1025 // If an app requests write storage, they will also get read storage.
1026 bool hasReadExternalStoragePermission = false;
1027
1028 // Implement transition to read and write call log.
1029 bool hasReadContactsPermission = false;
1030 bool hasWriteContactsPermission = false;
1031 bool hasReadCallLogPermission = false;
1032 bool hasWriteCallLogPermission = false;
1033
Adam Lesinskie47fd122014-08-15 22:25:36 -07001034 // If an app declares itself as multiArch, we report the
1035 // native libraries differently.
1036 bool hasMultiArch = false;
1037
Adam Lesinski282e1812014-01-23 18:17:42 -08001038 // This next group of variables is used to implement a group of
1039 // backward-compatibility heuristics necessitated by the addition of
1040 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
1041 // heuristic is "if an app requests a permission but doesn't explicitly
1042 // request the corresponding <uses-feature>, presume it's there anyway".
Adam Lesinski2c72b682014-06-24 09:56:01 -07001043
Adam Lesinski282e1812014-01-23 18:17:42 -08001044 // 2.2 also added some other features that apps can request, but that
1045 // have no corresponding permission, so we cannot implement any
1046 // back-compatibility heuristic for them. The below are thus unnecessary
1047 // (but are retained here for documentary purposes.)
1048 //bool specCompassFeature = false;
1049 //bool specAccelerometerFeature = false;
1050 //bool specProximityFeature = false;
1051 //bool specAmbientLightFeature = false;
1052 //bool specLiveWallpaperFeature = false;
1053
1054 int targetSdk = 0;
1055 int smallScreen = 1;
1056 int normalScreen = 1;
1057 int largeScreen = 1;
1058 int xlargeScreen = 1;
1059 int anyDensity = 1;
1060 int requiresSmallestWidthDp = 0;
1061 int compatibleWidthLimitDp = 0;
1062 int largestWidthLimitDp = 0;
1063 String8 pkg;
1064 String8 activityName;
1065 String8 activityLabel;
1066 String8 activityIcon;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001067 String8 activityBanner;
Adam Lesinski282e1812014-01-23 18:17:42 -08001068 String8 receiverName;
1069 String8 serviceName;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001070 Vector<String8> supportedInput;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001071
1072 FeatureGroup commonFeatures;
1073 Vector<FeatureGroup> featureGroups;
1074 KeyedVector<String8, ImpliedFeature> impliedFeatures;
1075
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001076 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
1077 code != ResXMLTree::BAD_DOCUMENT) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001078 if (code == ResXMLTree::END_TAG) {
1079 depth--;
1080 if (depth < 2) {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001081 if (withinSupportsInput && !supportedInput.isEmpty()) {
1082 printf("supports-input: '");
1083 const size_t N = supportedInput.size();
1084 for (size_t i=0; i<N; i++) {
Maurice Chu2675f762013-10-22 17:33:11 -07001085 printf("%s", ResTable::normalizeForOutput(
1086 supportedInput[i].string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001087 if (i != N - 1) {
1088 printf("' '");
1089 } else {
1090 printf("'\n");
1091 }
1092 }
1093 supportedInput.clear();
1094 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001095 withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001096 withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001097 withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001098 } else if (depth < 3) {
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001099 if (withinActivity && isMainActivity) {
Maurice Chu2675f762013-10-22 17:33:11 -07001100 String8 aName(getComponentName(pkg, activityName));
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001101 if (isLauncherActivity) {
1102 printf("launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001103 if (aName.length() > 0) {
1104 printf(" name='%s' ",
1105 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001106 }
1107 printf(" label='%s' icon='%s'\n",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001108 ResTable::normalizeForOutput(activityLabel.string())
1109 .string(),
1110 ResTable::normalizeForOutput(activityIcon.string())
1111 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001112 }
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001113 if (isLeanbackLauncherActivity) {
1114 printf("leanback-launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001115 if (aName.length() > 0) {
1116 printf(" name='%s' ",
1117 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001118 }
1119 printf(" label='%s' icon='%s' banner='%s'\n",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001120 ResTable::normalizeForOutput(activityLabel.string())
1121 .string(),
1122 ResTable::normalizeForOutput(activityIcon.string())
1123 .string(),
1124 ResTable::normalizeForOutput(activityBanner.string())
1125 .string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001126 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001127 }
1128 if (!hasIntentFilter) {
1129 hasOtherActivities |= withinActivity;
1130 hasOtherReceivers |= withinReceiver;
1131 hasOtherServices |= withinService;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001132 } else {
1133 if (withinService) {
1134 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
1135 hasBindNfcServicePermission);
1136 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
1137 hasBindNfcServicePermission);
1138 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001139 }
1140 withinActivity = false;
1141 withinService = false;
1142 withinReceiver = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001143 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001144 hasIntentFilter = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001145 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001146 } else if (depth < 4) {
1147 if (withinIntentFilter) {
1148 if (withinActivity) {
1149 hasMainActivity |= actMainActivity;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001150 hasLauncher |= catLauncher;
1151 hasCameraActivity |= actCamera;
1152 hasCameraSecureActivity |= actCameraSecure;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001153 hasOtherActivities |=
1154 !actMainActivity && !actCamera && !actCameraSecure;
Adam Lesinski282e1812014-01-23 18:17:42 -08001155 } else if (withinReceiver) {
1156 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001157 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
1158 hasBindDeviceAdminPermission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001159 hasOtherReceivers |=
1160 (!actWidgetReceivers && !actDeviceAdminEnabled);
Adam Lesinski282e1812014-01-23 18:17:42 -08001161 } else if (withinService) {
1162 hasImeService |= actImeService;
1163 hasWallpaperService |= actWallpaperService;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001164 hasAccessibilityService |= (actAccessibilityService &&
1165 hasBindAccessibilityServicePermission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001166 hasPrintService |=
1167 (actPrintService && hasBindPrintServicePermission);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001168 hasNotificationListenerService |= actNotificationListenerService &&
1169 hasBindNotificationListenerServicePermission;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001170 hasDreamService |= actDreamService && hasBindDreamServicePermission;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001171 hasOtherServices |= (!actImeService && !actWallpaperService &&
Adam Lesinski94fc9122013-09-30 17:16:09 -07001172 !actAccessibilityService && !actPrintService &&
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001173 !actHostApduService && !actOffHostApduService &&
1174 !actNotificationListenerService);
1175 } else if (withinProvider) {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001176 hasDocumentsProvider |=
1177 actDocumentsProvider && hasRequiredSafAttributes;
Adam Lesinski282e1812014-01-23 18:17:42 -08001178 }
1179 }
1180 withinIntentFilter = false;
1181 }
1182 continue;
1183 }
1184 if (code != ResXMLTree::START_TAG) {
1185 continue;
1186 }
1187 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001188
1189 const char16_t* ctag16 = tree.getElementName(&len);
1190 if (ctag16 == NULL) {
1191 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
1192 goto bail;
1193 }
1194 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -08001195 //printf("Depth %d, %s\n", depth, tag.string());
1196 if (depth == 1) {
1197 if (tag != "manifest") {
1198 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
1199 goto bail;
1200 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001201 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -07001202 printf("package: name='%s' ",
1203 ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001204 int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR,
1205 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001206 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001207 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n",
1208 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001209 goto bail;
1210 }
1211 if (versionCode > 0) {
1212 printf("versionCode='%d' ", versionCode);
1213 } else {
1214 printf("versionCode='' ");
1215 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001216 String8 versionName = AaptXml::getResolvedAttribute(res, tree,
1217 VERSION_NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001218 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001219 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n",
1220 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001221 goto bail;
1222 }
Adam Lesinski25d35a92014-08-11 09:41:56 -07001223 printf("versionName='%s'",
Maurice Chu2675f762013-10-22 17:33:11 -07001224 ResTable::normalizeForOutput(versionName.string()).string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001225
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001226 String8 splitName = AaptXml::getAttribute(tree, NULL, "split");
Adam Lesinski25d35a92014-08-11 09:41:56 -07001227 if (!splitName.isEmpty()) {
1228 printf(" split='%s'", ResTable::normalizeForOutput(
1229 splitName.string()).string());
1230 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001231
Adam Lesinski5283fab2014-08-29 11:23:55 -07001232 String8 platformVersionName = AaptXml::getAttribute(tree, NULL,
1233 "platformBuildVersionName");
1234 printf(" platformBuildVersionName='%s'", platformVersionName.string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001235 printf("\n");
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001236
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001237 int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
1238 INSTALL_LOCATION_ATTR, &error);
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001239 if (error != "") {
1240 fprintf(stderr, "ERROR getting 'android:installLocation' attribute: %s\n",
1241 error.string());
1242 goto bail;
1243 }
1244
1245 if (installLocation >= 0) {
1246 printf("install-location:'");
1247 switch (installLocation) {
1248 case 0:
1249 printf("auto");
1250 break;
1251 case 1:
1252 printf("internalOnly");
1253 break;
1254 case 2:
1255 printf("preferExternal");
1256 break;
1257 default:
1258 fprintf(stderr, "Invalid installLocation %d\n", installLocation);
1259 goto bail;
1260 }
1261 printf("'\n");
1262 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001263 } else if (depth == 2) {
1264 withinApplication = false;
1265 if (tag == "application") {
1266 withinApplication = true;
1267
1268 String8 label;
1269 const size_t NL = locales.size();
1270 for (size_t i=0; i<NL; i++) {
1271 const char* localeStr = locales[i].string();
1272 assets.setLocale(localeStr != NULL ? localeStr : "");
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001273 String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1274 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001275 if (llabel != "") {
1276 if (localeStr == NULL || strlen(localeStr) == 0) {
1277 label = llabel;
Maurice Chu2675f762013-10-22 17:33:11 -07001278 printf("application-label:'%s'\n",
1279 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001280 } else {
1281 if (label == "") {
1282 label = llabel;
1283 }
1284 printf("application-label-%s:'%s'\n", localeStr,
Maurice Chu2675f762013-10-22 17:33:11 -07001285 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001286 }
1287 }
1288 }
1289
1290 ResTable_config tmpConfig = config;
1291 const size_t ND = densities.size();
1292 for (size_t i=0; i<ND; i++) {
1293 tmpConfig.density = densities[i];
1294 assets.setConfiguration(tmpConfig);
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001295 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1296 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001297 if (icon != "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001298 printf("application-icon-%d:'%s'\n", densities[i],
1299 ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001300 }
1301 }
1302 assets.setConfiguration(config);
1303
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001304 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001305 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001306 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1307 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001308 goto bail;
1309 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001310 int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0,
1311 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001312 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001313 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n",
1314 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001315 goto bail;
1316 }
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001317
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001318 String8 banner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1319 &error);
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001320 if (error != "") {
1321 fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
1322 error.string());
1323 goto bail;
1324 }
Maurice Chu2675f762013-10-22 17:33:11 -07001325 printf("application: label='%s' ",
1326 ResTable::normalizeForOutput(label.string()).string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001327 printf("icon='%s'", ResTable::normalizeForOutput(icon.string()).string());
1328 if (banner != "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001329 printf(" banner='%s'",
1330 ResTable::normalizeForOutput(banner.string()).string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001331 }
1332 printf("\n");
Adam Lesinski282e1812014-01-23 18:17:42 -08001333 if (testOnly != 0) {
1334 printf("testOnly='%d'\n", testOnly);
1335 }
1336
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001337 int32_t isGame = AaptXml::getResolvedIntegerAttribute(res, tree,
1338 ISGAME_ATTR, 0, &error);
1339 if (error != "") {
1340 fprintf(stderr, "ERROR getting 'android:isGame' attribute: %s\n",
1341 error.string());
1342 goto bail;
1343 }
1344 if (isGame != 0) {
1345 printf("application-isGame\n");
1346 }
1347
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001348 int32_t debuggable = AaptXml::getResolvedIntegerAttribute(res, tree,
1349 DEBUGGABLE_ATTR, 0, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001350 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001351 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n",
1352 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001353 goto bail;
1354 }
1355 if (debuggable != 0) {
1356 printf("application-debuggable\n");
1357 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07001358
1359 // We must search by name because the multiArch flag hasn't been API
1360 // frozen yet.
1361 int32_t multiArchIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
1362 "multiArch");
1363 if (multiArchIndex >= 0) {
1364 Res_value value;
1365 if (tree.getAttributeValue(multiArchIndex, &value) != NO_ERROR) {
1366 if (value.dataType >= Res_value::TYPE_FIRST_INT &&
1367 value.dataType <= Res_value::TYPE_LAST_INT) {
1368 hasMultiArch = value.data;
1369 }
1370 }
1371 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001372 } else if (tag == "uses-sdk") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001373 int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR,
1374 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001375 if (error != "") {
1376 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001377 String8 name = AaptXml::getResolvedAttribute(res, tree,
1378 MIN_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001379 if (error != "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001380 fprintf(stderr,
1381 "ERROR getting 'android:minSdkVersion' attribute: %s\n",
Adam Lesinski282e1812014-01-23 18:17:42 -08001382 error.string());
1383 goto bail;
1384 }
1385 if (name == "Donut") targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001386 printf("sdkVersion:'%s'\n",
1387 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001388 } else if (code != -1) {
1389 targetSdk = code;
1390 printf("sdkVersion:'%d'\n", code);
1391 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001392 code = AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -08001393 if (code != -1) {
1394 printf("maxSdkVersion:'%d'\n", code);
1395 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001396 code = AaptXml::getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001397 if (error != "") {
1398 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001399 String8 name = AaptXml::getResolvedAttribute(res, tree,
1400 TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001401 if (error != "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001402 fprintf(stderr,
1403 "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
Adam Lesinski282e1812014-01-23 18:17:42 -08001404 error.string());
1405 goto bail;
1406 }
1407 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001408 printf("targetSdkVersion:'%s'\n",
1409 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001410 } else if (code != -1) {
1411 if (targetSdk < code) {
1412 targetSdk = code;
1413 }
1414 printf("targetSdkVersion:'%d'\n", code);
1415 }
1416 } else if (tag == "uses-configuration") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001417 int32_t reqTouchScreen = AaptXml::getIntegerAttribute(tree,
1418 REQ_TOUCH_SCREEN_ATTR, 0);
1419 int32_t reqKeyboardType = AaptXml::getIntegerAttribute(tree,
1420 REQ_KEYBOARD_TYPE_ATTR, 0);
1421 int32_t reqHardKeyboard = AaptXml::getIntegerAttribute(tree,
1422 REQ_HARD_KEYBOARD_ATTR, 0);
1423 int32_t reqNavigation = AaptXml::getIntegerAttribute(tree,
1424 REQ_NAVIGATION_ATTR, 0);
1425 int32_t reqFiveWayNav = AaptXml::getIntegerAttribute(tree,
1426 REQ_FIVE_WAY_NAV_ATTR, 0);
Adam Lesinski282e1812014-01-23 18:17:42 -08001427 printf("uses-configuration:");
1428 if (reqTouchScreen != 0) {
1429 printf(" reqTouchScreen='%d'", reqTouchScreen);
1430 }
1431 if (reqKeyboardType != 0) {
1432 printf(" reqKeyboardType='%d'", reqKeyboardType);
1433 }
1434 if (reqHardKeyboard != 0) {
1435 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1436 }
1437 if (reqNavigation != 0) {
1438 printf(" reqNavigation='%d'", reqNavigation);
1439 }
1440 if (reqFiveWayNav != 0) {
1441 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1442 }
1443 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001444 } else if (tag == "supports-input") {
1445 withinSupportsInput = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001446 } else if (tag == "supports-screens") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001447 smallScreen = AaptXml::getIntegerAttribute(tree,
1448 SMALL_SCREEN_ATTR, 1);
1449 normalScreen = AaptXml::getIntegerAttribute(tree,
1450 NORMAL_SCREEN_ATTR, 1);
1451 largeScreen = AaptXml::getIntegerAttribute(tree,
1452 LARGE_SCREEN_ATTR, 1);
1453 xlargeScreen = AaptXml::getIntegerAttribute(tree,
1454 XLARGE_SCREEN_ATTR, 1);
1455 anyDensity = AaptXml::getIntegerAttribute(tree,
1456 ANY_DENSITY_ATTR, 1);
1457 requiresSmallestWidthDp = AaptXml::getIntegerAttribute(tree,
1458 REQUIRES_SMALLEST_WIDTH_DP_ATTR, 0);
1459 compatibleWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1460 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, 0);
1461 largestWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1462 LARGEST_WIDTH_LIMIT_DP_ATTR, 0);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001463 } else if (tag == "feature-group") {
1464 withinFeatureGroup = true;
1465 FeatureGroup group;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001466 group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001467 if (error != "") {
1468 fprintf(stderr, "ERROR getting 'android:label' attribute:"
1469 " %s\n", error.string());
1470 goto bail;
1471 }
1472 featureGroups.add(group);
1473
Adam Lesinski282e1812014-01-23 18:17:42 -08001474 } else if (tag == "uses-feature") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001475 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001476 if (name != "" && error == "") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001477 const char* androidSchema =
1478 "http://schemas.android.com/apk/res/android";
Adam Lesinski282e1812014-01-23 18:17:42 -08001479
Adam Lesinski694d0a72016-04-06 16:12:04 -07001480 int32_t req = AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1,
1481 &error);
1482 if (error != "") {
1483 SourcePos(manifestFile, tree.getLineNumber()).error(
1484 "failed to read attribute 'android:required': %s",
1485 error.string());
1486 goto bail;
1487 }
1488
1489 int32_t version = AaptXml::getIntegerAttribute(tree, androidSchema,
1490 "version", 0, &error);
1491 if (error != "") {
1492 SourcePos(manifestFile, tree.getLineNumber()).error(
1493 "failed to read attribute 'android:version': %s",
1494 error.string());
1495 goto bail;
1496 }
1497
1498 commonFeatures.features.add(name, Feature(req != 0, version));
Adam Lesinski2c72b682014-06-24 09:56:01 -07001499 if (req) {
1500 addParentFeatures(&commonFeatures, name);
Adam Lesinski282e1812014-01-23 18:17:42 -08001501 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001502 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001503 int vers = AaptXml::getIntegerAttribute(tree,
Adam Lesinski282e1812014-01-23 18:17:42 -08001504 GL_ES_VERSION_ATTR, &error);
1505 if (error == "") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001506 if (vers > commonFeatures.openGLESVersion) {
1507 commonFeatures.openGLESVersion = vers;
1508 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001509 }
1510 }
1511 } else if (tag == "uses-permission") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001512 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001513 if (error != "") {
Adam Lesinski282e1812014-01-23 18:17:42 -08001514 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1515 error.string());
1516 goto bail;
1517 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001518
1519 if (name == "") {
1520 fprintf(stderr, "ERROR: missing 'android:name' for uses-permission\n");
1521 goto bail;
1522 }
1523
1524 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, false);
1525
1526 if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1527 hasWriteExternalStoragePermission = true;
1528 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1529 hasReadExternalStoragePermission = true;
1530 } else if (name == "android.permission.READ_PHONE_STATE") {
1531 hasReadPhoneStatePermission = true;
1532 } else if (name == "android.permission.READ_CONTACTS") {
1533 hasReadContactsPermission = true;
1534 } else if (name == "android.permission.WRITE_CONTACTS") {
1535 hasWriteContactsPermission = true;
1536 } else if (name == "android.permission.READ_CALL_LOG") {
1537 hasReadCallLogPermission = true;
1538 } else if (name == "android.permission.WRITE_CALL_LOG") {
1539 hasWriteCallLogPermission = true;
1540 }
1541
1542 printUsesPermission(name,
1543 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
1544 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
1545
1546 } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
1547 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1548 if (error != "") {
1549 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1550 error.string());
1551 goto bail;
1552 }
1553
1554 if (name == "") {
1555 fprintf(stderr, "ERROR: missing 'android:name' for "
1556 "uses-permission-sdk-23\n");
1557 goto bail;
1558 }
1559
1560 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, true);
1561
1562 printUsesPermissionSdk23(
1563 name, AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
1564
Adam Lesinski282e1812014-01-23 18:17:42 -08001565 } else if (tag == "uses-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001566 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001567 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001568 printf("uses-package:'%s'\n",
1569 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001570 } else {
1571 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1572 error.string());
1573 goto bail;
1574 }
1575 } else if (tag == "original-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001576 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001577 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001578 printf("original-package:'%s'\n",
1579 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001580 } else {
1581 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1582 error.string());
1583 goto bail;
1584 }
1585 } else if (tag == "supports-gl-texture") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001586 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001587 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001588 printf("supports-gl-texture:'%s'\n",
1589 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001590 } else {
1591 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1592 error.string());
1593 goto bail;
1594 }
1595 } else if (tag == "compatible-screens") {
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001596 printCompatibleScreens(tree, &error);
1597 if (error != "") {
1598 fprintf(stderr, "ERROR getting compatible screens: %s\n",
1599 error.string());
1600 goto bail;
1601 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001602 depth--;
1603 } else if (tag == "package-verifier") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001604 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001605 if (name != "" && error == "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001606 String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR,
1607 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001608 if (publicKey != "" && error == "") {
1609 printf("package-verifier: name='%s' publicKey='%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001610 ResTable::normalizeForOutput(name.string()).string(),
1611 ResTable::normalizeForOutput(publicKey.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001612 }
1613 }
1614 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001615 } else if (depth == 3) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001616 withinActivity = false;
1617 withinReceiver = false;
1618 withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001619 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001620 hasIntentFilter = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001621 hasMetaHostPaymentCategory = false;
1622 hasMetaOffHostPaymentCategory = false;
1623 hasBindDeviceAdminPermission = false;
1624 hasBindInputMethodPermission = false;
1625 hasBindAccessibilityServicePermission = false;
1626 hasBindPrintServicePermission = false;
1627 hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001628 hasRequiredSafAttributes = false;
1629 hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001630 hasBindDreamServicePermission = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001631 if (withinApplication) {
1632 if(tag == "activity") {
1633 withinActivity = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001634 activityName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001635 if (error != "") {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001636 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1637 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001638 goto bail;
1639 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001640
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001641 activityLabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1642 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001643 if (error != "") {
1644 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
1645 error.string());
1646 goto bail;
1647 }
1648
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001649 activityIcon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1650 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001651 if (error != "") {
1652 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1653 error.string());
1654 goto bail;
1655 }
1656
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001657 activityBanner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1658 &error);
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001659 if (error != "") {
1660 fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
1661 error.string());
1662 goto bail;
1663 }
1664
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001665 int32_t orien = AaptXml::getResolvedIntegerAttribute(res, tree,
Michael Wrightec4fdec2013-09-06 16:50:52 -07001666 SCREEN_ORIENTATION_ATTR, &error);
1667 if (error == "") {
1668 if (orien == 0 || orien == 6 || orien == 8) {
1669 // Requests landscape, sensorLandscape, or reverseLandscape.
Adam Lesinski43158772015-11-11 15:13:55 -08001670 addImpliedFeature(
1671 &impliedFeatures, "android.hardware.screen.landscape",
1672 String8("one or more activities have specified a "
1673 "landscape orientation"),
1674 false);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001675 } else if (orien == 1 || orien == 7 || orien == 9) {
1676 // Requests portrait, sensorPortrait, or reversePortrait.
Adam Lesinski43158772015-11-11 15:13:55 -08001677 addImpliedFeature(
1678 &impliedFeatures, "android.hardware.screen.portrait",
1679 String8("one or more activities have specified a "
1680 "portrait orientation"),
1681 false);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001682 }
1683 }
1684 } else if (tag == "uses-library") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001685 String8 libraryName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001686 if (error != "") {
1687 fprintf(stderr,
1688 "ERROR getting 'android:name' attribute for uses-library"
1689 " %s\n", error.string());
1690 goto bail;
1691 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001692 int req = AaptXml::getIntegerAttribute(tree,
1693 REQUIRED_ATTR, 1);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001694 printf("uses-library%s:'%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001695 req ? "" : "-not-required", ResTable::normalizeForOutput(
1696 libraryName.string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001697 } else if (tag == "receiver") {
1698 withinReceiver = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001699 receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001700
1701 if (error != "") {
1702 fprintf(stderr,
1703 "ERROR getting 'android:name' attribute for receiver:"
1704 " %s\n", error.string());
1705 goto bail;
1706 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001707
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001708 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1709 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001710 if (error == "") {
1711 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1712 hasBindDeviceAdminPermission = true;
1713 }
1714 } else {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001715 fprintf(stderr,
1716 "ERROR getting 'android:permission' attribute for"
1717 " receiver '%s': %s\n",
1718 receiverName.string(), error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001719 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001720 } else if (tag == "service") {
1721 withinService = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001722 serviceName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001723
1724 if (error != "") {
1725 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1726 "service:%s\n", error.string());
1727 goto bail;
1728 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001729
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001730 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1731 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001732 if (error == "") {
1733 if (permission == "android.permission.BIND_INPUT_METHOD") {
1734 hasBindInputMethodPermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001735 } else if (permission ==
1736 "android.permission.BIND_ACCESSIBILITY_SERVICE") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001737 hasBindAccessibilityServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001738 } else if (permission ==
1739 "android.permission.BIND_PRINT_SERVICE") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001740 hasBindPrintServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001741 } else if (permission ==
1742 "android.permission.BIND_NFC_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07001743 hasBindNfcServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001744 } else if (permission ==
1745 "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001746 hasBindNotificationListenerServicePermission = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001747 } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
1748 hasBindDreamServicePermission = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001749 }
1750 } else {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001751 fprintf(stderr, "ERROR getting 'android:permission' attribute for "
1752 "service '%s': %s\n", serviceName.string(), error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001753 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001754 } else if (tag == "provider") {
1755 withinProvider = true;
1756
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001757 bool exported = AaptXml::getResolvedIntegerAttribute(res, tree,
1758 EXPORTED_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001759 if (error != "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001760 fprintf(stderr,
1761 "ERROR getting 'android:exported' attribute for provider:"
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001762 " %s\n", error.string());
1763 goto bail;
1764 }
1765
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001766 bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute(
1767 res, tree, GRANT_URI_PERMISSIONS_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001768 if (error != "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001769 fprintf(stderr,
1770 "ERROR getting 'android:grantUriPermissions' attribute for "
1771 "provider: %s\n", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001772 goto bail;
1773 }
1774
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001775 String8 permission = AaptXml::getResolvedAttribute(res, tree,
1776 PERMISSION_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001777 if (error != "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001778 fprintf(stderr, "ERROR getting 'android:permission' attribute for "
1779 "provider: %s\n", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001780 goto bail;
1781 }
1782
1783 hasRequiredSafAttributes |= exported && grantUriPermissions &&
1784 permission == "android.permission.MANAGE_DOCUMENTS";
1785
Michael Wrightec4fdec2013-09-06 16:50:52 -07001786 } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001787 String8 metaDataName = AaptXml::getResolvedAttribute(res, tree,
1788 NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001789 if (error != "") {
1790 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1791 "meta-data:%s\n", error.string());
1792 goto bail;
1793 }
Maurice Chu2675f762013-10-22 17:33:11 -07001794 printf("meta-data: name='%s' ",
1795 ResTable::normalizeForOutput(metaDataName.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001796 printResolvedResourceAttribute(res, tree, VALUE_ATTR, String8("value"),
Maurice Chu76327312013-10-16 18:28:46 -07001797 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001798 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001799 // Try looking for a RESOURCE_ATTR
1800 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001801 printResolvedResourceAttribute(res, tree, RESOURCE_ATTR,
Maurice Chu76327312013-10-16 18:28:46 -07001802 String8("resource"), &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001803 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001804 fprintf(stderr, "ERROR getting 'android:value' or "
1805 "'android:resource' attribute for "
1806 "meta-data:%s\n", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001807 goto bail;
1808 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001809 }
Maurice Chu76327312013-10-16 18:28:46 -07001810 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001811 } else if (withinSupportsInput && tag == "input-type") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001812 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001813 if (name != "" && error == "") {
1814 supportedInput.add(name);
1815 } else {
1816 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1817 error.string());
1818 goto bail;
1819 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001820 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001821 } else if (withinFeatureGroup && tag == "uses-feature") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001822 const String8 androidSchema("http://schemas.android.com/apk/res/android");
Adam Lesinski2c72b682014-06-24 09:56:01 -07001823 FeatureGroup& top = featureGroups.editTop();
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001824
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001825 String8 name = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001826 if (name != "" && error == "") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001827 Feature feature(true);
1828
1829 int32_t featureVers = AaptXml::getIntegerAttribute(
1830 tree, androidSchema.string(), "version", 0, &error);
1831 if (error == "") {
1832 feature.version = featureVers;
1833 } else {
1834 SourcePos(manifestFile, tree.getLineNumber()).error(
1835 "failed to read attribute 'android:version': %s",
1836 error.string());
1837 goto bail;
1838 }
1839
1840 top.features.add(name, feature);
Adam Lesinskid3edfde2014-08-08 17:32:44 -07001841 addParentFeatures(&top, name);
Adam Lesinski694d0a72016-04-06 16:12:04 -07001842
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001843 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001844 int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR,
1845 &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001846 if (error == "") {
1847 if (vers > top.openGLESVersion) {
1848 top.openGLESVersion = vers;
1849 }
1850 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001851 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001852 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001853 } else if (depth == 4) {
1854 if (tag == "intent-filter") {
1855 hasIntentFilter = true;
1856 withinIntentFilter = true;
1857 actMainActivity = false;
1858 actWidgetReceivers = false;
1859 actImeService = false;
1860 actWallpaperService = false;
1861 actAccessibilityService = false;
1862 actPrintService = false;
1863 actDeviceAdminEnabled = false;
1864 actHostApduService = false;
1865 actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001866 actDocumentsProvider = false;
1867 actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001868 actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001869 actCamera = false;
1870 actCameraSecure = false;
1871 catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001872 } else if (withinService && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001873 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001874 if (error != "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001875 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1876 "meta-data tag in service '%s': %s\n", serviceName.string(),
1877 error.string());
Adam Lesinski94fc9122013-09-30 17:16:09 -07001878 goto bail;
1879 }
1880
1881 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1882 name == "android.nfc.cardemulation.off_host_apdu_service") {
1883 bool offHost = true;
1884 if (name == "android.nfc.cardemulation.host_apdu_service") {
1885 offHost = false;
1886 }
1887
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001888 String8 xmlPath = AaptXml::getResolvedAttribute(res, tree,
1889 RESOURCE_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001890 if (error != "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001891 fprintf(stderr, "ERROR getting 'android:resource' attribute for "
1892 "meta-data tag in service '%s': %s\n",
1893 serviceName.string(), error.string());
Adam Lesinski94fc9122013-09-30 17:16:09 -07001894 goto bail;
1895 }
1896
1897 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1898 offHost, &error);
1899 if (error != "") {
1900 fprintf(stderr, "ERROR getting AID category for service '%s'\n",
1901 serviceName.string());
1902 goto bail;
1903 }
1904
1905 const size_t catLen = categories.size();
1906 for (size_t i = 0; i < catLen; i++) {
1907 bool paymentCategory = (categories[i] == "payment");
1908 if (offHost) {
1909 hasMetaOffHostPaymentCategory |= paymentCategory;
1910 } else {
1911 hasMetaHostPaymentCategory |= paymentCategory;
1912 }
1913 }
1914 }
1915 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001916 } else if ((depth == 5) && withinIntentFilter) {
1917 String8 action;
1918 if (tag == "action") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001919 action = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001920 if (error != "") {
1921 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1922 error.string());
1923 goto bail;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001924 }
1925
Adam Lesinskia5018c92013-09-30 16:23:15 -07001926 if (withinActivity) {
1927 if (action == "android.intent.action.MAIN") {
1928 isMainActivity = true;
1929 actMainActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001930 } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
1931 action == "android.media.action.VIDEO_CAMERA") {
1932 actCamera = true;
1933 } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
1934 actCameraSecure = true;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001935 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001936 } else if (withinReceiver) {
1937 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1938 actWidgetReceivers = true;
1939 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1940 actDeviceAdminEnabled = true;
1941 }
1942 } else if (withinService) {
1943 if (action == "android.view.InputMethod") {
1944 actImeService = true;
1945 } else if (action == "android.service.wallpaper.WallpaperService") {
1946 actWallpaperService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001947 } else if (action ==
1948 "android.accessibilityservice.AccessibilityService") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001949 actAccessibilityService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001950 } else if (action =="android.printservice.PrintService") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001951 actPrintService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001952 } else if (action ==
1953 "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07001954 actHostApduService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001955 } else if (action ==
1956 "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07001957 actOffHostApduService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001958 } else if (action ==
1959 "android.service.notification.NotificationListenerService") {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001960 actNotificationListenerService = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001961 } else if (action == "android.service.dreams.DreamService") {
1962 actDreamService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001963 }
1964 } else if (withinProvider) {
1965 if (action == "android.content.action.DOCUMENTS_PROVIDER") {
1966 actDocumentsProvider = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001967 }
1968 }
1969 if (action == "android.intent.action.SEARCH") {
1970 isSearchable = true;
1971 }
1972 }
1973
1974 if (tag == "category") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001975 String8 category = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001976 if (error != "") {
1977 fprintf(stderr, "ERROR getting 'name' attribute: %s\n",
1978 error.string());
1979 goto bail;
1980 }
1981 if (withinActivity) {
1982 if (category == "android.intent.category.LAUNCHER") {
1983 isLauncherActivity = true;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001984 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
1985 isLeanbackLauncherActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001986 } else if (category == "android.intent.category.HOME") {
1987 catLauncher = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001988 }
1989 }
1990 }
1991 }
1992 }
1993
1994 // Pre-1.6 implicitly granted permission compatibility logic
1995 if (targetSdk < 4) {
1996 if (!hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001997 printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
1998 printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
1999 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002000 hasWriteExternalStoragePermission = true;
2001 }
2002 if (!hasReadPhoneStatePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002003 printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
2004 printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
2005 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002006 }
2007 }
2008
2009 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
2010 // force them to always take READ_EXTERNAL_STORAGE as well. We always
2011 // do this (regardless of target API version) because we can't have
2012 // an app with write permission but not read permission.
2013 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002014 printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"));
2015 printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
2016 String8("requested WRITE_EXTERNAL_STORAGE"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002017 }
2018
2019 // Pre-JellyBean call log permission compatibility.
2020 if (targetSdk < 16) {
2021 if (!hasReadCallLogPermission && hasReadContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002022 printUsesPermission(String8("android.permission.READ_CALL_LOG"));
2023 printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
2024 String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002025 }
2026 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002027 printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
2028 printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
2029 String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002030 }
2031 }
2032
Adam Lesinskica955a42016-08-01 16:44:29 -07002033 // If the app hasn't declared the touchscreen as a feature requirement (either
2034 // directly or implied, required or not), then the faketouch feature is implied.
2035 if (!hasFeature("android.hardware.touchscreen", commonFeatures, impliedFeatures)) {
2036 addImpliedFeature(&impliedFeatures, "android.hardware.faketouch",
Adam Lesinski43158772015-11-11 15:13:55 -08002037 String8("default feature for all apps"), false);
Adam Lesinskica955a42016-08-01 16:44:29 -07002038 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07002039
2040 const size_t numFeatureGroups = featureGroups.size();
2041 if (numFeatureGroups == 0) {
2042 // If no <feature-group> tags were defined, apply auto-implied features.
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002043 printDefaultFeatureGroup(commonFeatures, impliedFeatures);
Adam Lesinski2c72b682014-06-24 09:56:01 -07002044
2045 } else {
2046 // <feature-group> tags are defined, so we ignore implied features and
2047 for (size_t i = 0; i < numFeatureGroups; i++) {
2048 FeatureGroup& grp = featureGroups.editItemAt(i);
2049
Adam Lesinskid7a94da2014-07-25 14:38:54 -07002050 if (commonFeatures.openGLESVersion > grp.openGLESVersion) {
2051 grp.openGLESVersion = commonFeatures.openGLESVersion;
2052 }
2053
Adam Lesinski2c72b682014-06-24 09:56:01 -07002054 // Merge the features defined in the top level (not inside a <feature-group>)
2055 // with this feature group.
2056 const size_t numCommonFeatures = commonFeatures.features.size();
2057 for (size_t j = 0; j < numCommonFeatures; j++) {
2058 if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07002059 grp.features.add(commonFeatures.features.keyAt(j),
2060 commonFeatures.features[j]);
Adam Lesinski2c72b682014-06-24 09:56:01 -07002061 }
2062 }
2063
Adam Lesinski73a05112014-12-08 12:53:17 -08002064 if (!grp.features.isEmpty()) {
Adam Lesinski2c72b682014-06-24 09:56:01 -07002065 printFeatureGroup(grp);
Adam Lesinski282e1812014-01-23 18:17:42 -08002066 }
2067 }
2068 }
2069
Adam Lesinski282e1812014-01-23 18:17:42 -08002070
Adam Lesinski282e1812014-01-23 18:17:42 -08002071 if (hasWidgetReceivers) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002072 printComponentPresence("app-widget");
Adam Lesinski282e1812014-01-23 18:17:42 -08002073 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002074 if (hasDeviceAdminReceiver) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002075 printComponentPresence("device-admin");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002076 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002077 if (hasImeService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002078 printComponentPresence("ime");
Adam Lesinski282e1812014-01-23 18:17:42 -08002079 }
2080 if (hasWallpaperService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002081 printComponentPresence("wallpaper");
Adam Lesinski282e1812014-01-23 18:17:42 -08002082 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002083 if (hasAccessibilityService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002084 printComponentPresence("accessibility");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002085 }
2086 if (hasPrintService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002087 printComponentPresence("print-service");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002088 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07002089 if (hasPaymentService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002090 printComponentPresence("payment");
2091 }
2092 if (isSearchable) {
2093 printComponentPresence("search");
2094 }
2095 if (hasDocumentsProvider) {
2096 printComponentPresence("document-provider");
2097 }
2098 if (hasLauncher) {
2099 printComponentPresence("launcher");
2100 }
2101 if (hasNotificationListenerService) {
2102 printComponentPresence("notification-listener");
2103 }
John Spurlockeb8d1be2014-06-25 17:46:15 -04002104 if (hasDreamService) {
2105 printComponentPresence("dream");
2106 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002107 if (hasCameraActivity) {
2108 printComponentPresence("camera");
2109 }
2110 if (hasCameraSecureActivity) {
2111 printComponentPresence("camera-secure");
2112 }
2113
2114 if (hasMainActivity) {
2115 printf("main\n");
Adam Lesinski94fc9122013-09-30 17:16:09 -07002116 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002117 if (hasOtherActivities) {
2118 printf("other-activities\n");
2119 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002120 if (hasOtherReceivers) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002121 printf("other-receivers\n");
2122 }
2123 if (hasOtherServices) {
2124 printf("other-services\n");
2125 }
2126
2127 // For modern apps, if screen size buckets haven't been specified
2128 // but the new width ranges have, then infer the buckets from them.
2129 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
2130 && requiresSmallestWidthDp > 0) {
2131 int compatWidth = compatibleWidthLimitDp;
2132 if (compatWidth <= 0) {
2133 compatWidth = requiresSmallestWidthDp;
2134 }
2135 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
2136 smallScreen = -1;
2137 } else {
2138 smallScreen = 0;
2139 }
2140 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
2141 normalScreen = -1;
2142 } else {
2143 normalScreen = 0;
2144 }
2145 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
2146 largeScreen = -1;
2147 } else {
2148 largeScreen = 0;
2149 }
2150 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
2151 xlargeScreen = -1;
2152 } else {
2153 xlargeScreen = 0;
2154 }
2155 }
2156
2157 // Determine default values for any unspecified screen sizes,
2158 // based on the target SDK of the package. As of 4 (donut)
2159 // the screen size support was introduced, so all default to
2160 // enabled.
2161 if (smallScreen > 0) {
2162 smallScreen = targetSdk >= 4 ? -1 : 0;
2163 }
2164 if (normalScreen > 0) {
2165 normalScreen = -1;
2166 }
2167 if (largeScreen > 0) {
2168 largeScreen = targetSdk >= 4 ? -1 : 0;
2169 }
2170 if (xlargeScreen > 0) {
2171 // Introduced in Gingerbread.
2172 xlargeScreen = targetSdk >= 9 ? -1 : 0;
2173 }
2174 if (anyDensity > 0) {
2175 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
2176 || compatibleWidthLimitDp > 0) ? -1 : 0;
2177 }
2178 printf("supports-screens:");
2179 if (smallScreen != 0) {
2180 printf(" 'small'");
2181 }
2182 if (normalScreen != 0) {
2183 printf(" 'normal'");
2184 }
2185 if (largeScreen != 0) {
2186 printf(" 'large'");
2187 }
2188 if (xlargeScreen != 0) {
2189 printf(" 'xlarge'");
2190 }
2191 printf("\n");
2192 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
2193 if (requiresSmallestWidthDp > 0) {
2194 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
2195 }
2196 if (compatibleWidthLimitDp > 0) {
2197 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
2198 }
2199 if (largestWidthLimitDp > 0) {
2200 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
2201 }
2202
2203 printf("locales:");
2204 const size_t NL = locales.size();
2205 for (size_t i=0; i<NL; i++) {
2206 const char* localeStr = locales[i].string();
2207 if (localeStr == NULL || strlen(localeStr) == 0) {
2208 localeStr = "--_--";
2209 }
2210 printf(" '%s'", localeStr);
2211 }
2212 printf("\n");
2213
2214 printf("densities:");
2215 const size_t ND = densities.size();
2216 for (size_t i=0; i<ND; i++) {
2217 printf(" '%d'", densities[i]);
2218 }
2219 printf("\n");
2220
2221 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
2222 if (dir != NULL) {
2223 if (dir->getFileCount() > 0) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002224 SortedVector<String8> architectures;
Adam Lesinski282e1812014-01-23 18:17:42 -08002225 for (size_t i=0; i<dir->getFileCount(); i++) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002226 architectures.add(ResTable::normalizeForOutput(
2227 dir->getFileName(i).string()));
Adam Lesinski282e1812014-01-23 18:17:42 -08002228 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07002229
2230 bool outputAltNativeCode = false;
2231 // A multiArch package is one that contains 64-bit and
2232 // 32-bit versions of native code and expects 3rd-party
2233 // apps to load these native code libraries. Since most
2234 // 64-bit systems also support 32-bit apps, the apps
2235 // loading this multiArch package's code may be either
2236 // 32-bit or 64-bit.
2237 if (hasMultiArch) {
2238 // If this is a multiArch package, report the 64-bit
2239 // version only. Then as a separate entry, report the
2240 // rest.
2241 //
2242 // If we report the 32-bit architecture, this APK will
2243 // be installed on a 32-bit device, causing a large waste
2244 // of bandwidth and disk space. This assumes that
2245 // the developer of the multiArch package has also
2246 // made a version that is 32-bit only.
2247 String8 intel64("x86_64");
2248 String8 arm64("arm64-v8a");
2249 ssize_t index = architectures.indexOf(intel64);
2250 if (index < 0) {
2251 index = architectures.indexOf(arm64);
2252 }
2253
2254 if (index >= 0) {
2255 printf("native-code: '%s'\n", architectures[index].string());
2256 architectures.removeAt(index);
2257 outputAltNativeCode = true;
2258 }
2259 }
2260
2261 const size_t archCount = architectures.size();
2262 if (archCount > 0) {
2263 if (outputAltNativeCode) {
2264 printf("alt-");
2265 }
2266 printf("native-code:");
2267 for (size_t i = 0; i < archCount; i++) {
2268 printf(" '%s'", architectures[i].string());
2269 }
2270 printf("\n");
2271 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002272 }
2273 delete dir;
2274 }
2275 } else if (strcmp("badger", option) == 0) {
2276 printf("%s", CONSOLE_DATA);
2277 } else if (strcmp("configurations", option) == 0) {
2278 Vector<ResTable_config> configs;
2279 res.getConfigurations(&configs);
2280 const size_t N = configs.size();
2281 for (size_t i=0; i<N; i++) {
2282 printf("%s\n", configs[i].toString().string());
2283 }
2284 } else {
2285 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2286 goto bail;
2287 }
2288 }
2289
2290 result = NO_ERROR;
2291
2292bail:
2293 if (asset) {
2294 delete asset;
2295 }
2296 return (result != NO_ERROR);
2297}
2298
2299
2300/*
2301 * Handle the "add" command, which wants to add files to a new or
2302 * pre-existing archive.
2303 */
2304int doAdd(Bundle* bundle)
2305{
2306 ZipFile* zip = NULL;
2307 status_t result = UNKNOWN_ERROR;
2308 const char* zipFileName;
2309
2310 if (bundle->getUpdate()) {
2311 /* avoid confusion */
2312 fprintf(stderr, "ERROR: can't use '-u' with add\n");
2313 goto bail;
2314 }
2315
2316 if (bundle->getFileSpecCount() < 1) {
2317 fprintf(stderr, "ERROR: must specify zip file name\n");
2318 goto bail;
2319 }
2320 zipFileName = bundle->getFileSpecEntry(0);
2321
2322 if (bundle->getFileSpecCount() < 2) {
2323 fprintf(stderr, "NOTE: nothing to do\n");
2324 goto bail;
2325 }
2326
2327 zip = openReadWrite(zipFileName, true);
2328 if (zip == NULL) {
2329 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2330 goto bail;
2331 }
2332
2333 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2334 const char* fileName = bundle->getFileSpecEntry(i);
2335
2336 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
2337 printf(" '%s'... (from gzip)\n", fileName);
2338 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
2339 } else {
2340 if (bundle->getJunkPath()) {
2341 String8 storageName = String8(fileName).getPathLeaf();
Maurice Chu2675f762013-10-22 17:33:11 -07002342 printf(" '%s' as '%s'...\n", fileName,
2343 ResTable::normalizeForOutput(storageName.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08002344 result = zip->add(fileName, storageName.string(),
2345 bundle->getCompressionMethod(), NULL);
2346 } else {
2347 printf(" '%s'...\n", fileName);
2348 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2349 }
2350 }
2351 if (result != NO_ERROR) {
2352 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2353 if (result == NAME_NOT_FOUND) {
2354 fprintf(stderr, ": file not found\n");
2355 } else if (result == ALREADY_EXISTS) {
2356 fprintf(stderr, ": already exists in archive\n");
2357 } else {
2358 fprintf(stderr, "\n");
2359 }
2360 goto bail;
2361 }
2362 }
2363
2364 result = NO_ERROR;
2365
2366bail:
2367 delete zip;
2368 return (result != NO_ERROR);
2369}
2370
2371
2372/*
2373 * Delete files from an existing archive.
2374 */
2375int doRemove(Bundle* bundle)
2376{
2377 ZipFile* zip = NULL;
2378 status_t result = UNKNOWN_ERROR;
2379 const char* zipFileName;
2380
2381 if (bundle->getFileSpecCount() < 1) {
2382 fprintf(stderr, "ERROR: must specify zip file name\n");
2383 goto bail;
2384 }
2385 zipFileName = bundle->getFileSpecEntry(0);
2386
2387 if (bundle->getFileSpecCount() < 2) {
2388 fprintf(stderr, "NOTE: nothing to do\n");
2389 goto bail;
2390 }
2391
2392 zip = openReadWrite(zipFileName, false);
2393 if (zip == NULL) {
2394 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2395 zipFileName);
2396 goto bail;
2397 }
2398
2399 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2400 const char* fileName = bundle->getFileSpecEntry(i);
2401 ZipEntry* entry;
2402
2403 entry = zip->getEntryByName(fileName);
2404 if (entry == NULL) {
2405 printf(" '%s' NOT FOUND\n", fileName);
2406 continue;
2407 }
2408
2409 result = zip->remove(entry);
2410
2411 if (result != NO_ERROR) {
2412 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2413 bundle->getFileSpecEntry(i), zipFileName);
2414 goto bail;
2415 }
2416 }
2417
2418 /* update the archive */
2419 zip->flush();
2420
2421bail:
2422 delete zip;
2423 return (result != NO_ERROR);
2424}
2425
Adam Lesinski3921e872014-05-13 10:56:25 -07002426static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002427 const size_t numDirs = dir->getDirs().size();
2428 for (size_t i = 0; i < numDirs; i++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002429 bool ignore = ignoreConfig;
2430 const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2431 const char* dirStr = subDir->getLeaf().string();
2432 if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2433 ignore = true;
2434 }
2435 status_t err = addResourcesToBuilder(subDir, builder, ignore);
Adam Lesinskifab50872014-04-16 14:40:42 -07002436 if (err != NO_ERROR) {
2437 return err;
2438 }
2439 }
2440
2441 const size_t numFiles = dir->getFiles().size();
2442 for (size_t i = 0; i < numFiles; i++) {
2443 sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2444 const size_t numConfigs = gp->getFiles().size();
2445 for (size_t j = 0; j < numConfigs; j++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002446 status_t err = NO_ERROR;
2447 if (ignoreConfig) {
2448 err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2449 } else {
2450 err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2451 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002452 if (err != NO_ERROR) {
2453 fprintf(stderr, "Failed to add %s (%s) to builder.\n",
2454 gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
2455 return err;
2456 }
2457 }
2458 }
2459 return NO_ERROR;
2460}
2461
2462static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2463 if (split->isBase()) {
2464 return original;
2465 }
2466
2467 String8 ext(original.getPathExtension());
2468 if (ext == String8(".apk")) {
2469 return String8::format("%s_%s%s",
2470 original.getBasePath().string(),
2471 split->getDirectorySafeName().string(),
2472 ext.string());
2473 }
2474
2475 return String8::format("%s_%s", original.string(),
2476 split->getDirectorySafeName().string());
2477}
Adam Lesinski282e1812014-01-23 18:17:42 -08002478
2479/*
2480 * Package up an asset directory and associated application files.
2481 */
2482int doPackage(Bundle* bundle)
2483{
2484 const char* outputAPKFile;
2485 int retVal = 1;
2486 status_t err;
2487 sp<AaptAssets> assets;
2488 int N;
2489 FILE* fp;
2490 String8 dependencyFile;
Adam Lesinskifab50872014-04-16 14:40:42 -07002491 sp<ApkBuilder> builder;
Adam Lesinski282e1812014-01-23 18:17:42 -08002492
Anton Krumina2ef5c02014-03-12 14:46:44 -07002493 // -c en_XA or/and ar_XB means do pseudolocalization
Adam Lesinskifab50872014-04-16 14:40:42 -07002494 sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2495 err = configFilter->parse(bundle->getConfigurations());
Adam Lesinski282e1812014-01-23 18:17:42 -08002496 if (err != NO_ERROR) {
2497 goto bail;
2498 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002499 if (configFilter->containsPseudo()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002500 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2501 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002502 if (configFilter->containsPseudoBidi()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002503 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
Adam Lesinski282e1812014-01-23 18:17:42 -08002504 }
2505
2506 N = bundle->getFileSpecCount();
2507 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
Adam Lesinski09384302014-01-22 16:07:42 -08002508 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002509 fprintf(stderr, "ERROR: no input files\n");
2510 goto bail;
2511 }
2512
2513 outputAPKFile = bundle->getOutputAPKFile();
2514
2515 // Make sure the filenames provided exist and are of the appropriate type.
2516 if (outputAPKFile) {
2517 FileType type;
2518 type = getFileType(outputAPKFile);
2519 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2520 fprintf(stderr,
2521 "ERROR: output file '%s' exists but is not regular file\n",
2522 outputAPKFile);
2523 goto bail;
2524 }
2525 }
2526
2527 // Load the assets.
2528 assets = new AaptAssets();
2529
2530 // Set up the resource gathering in assets if we're going to generate
2531 // dependency files. Every time we encounter a resource while slurping
2532 // the tree, we'll add it to these stores so we have full resource paths
2533 // to write to a dependency file.
2534 if (bundle->getGenDependencies()) {
2535 sp<FilePathStore> resPathStore = new FilePathStore;
2536 assets->setFullResPaths(resPathStore);
2537 sp<FilePathStore> assetPathStore = new FilePathStore;
2538 assets->setFullAssetPaths(assetPathStore);
2539 }
2540
2541 err = assets->slurpFromArgs(bundle);
2542 if (err < 0) {
2543 goto bail;
2544 }
2545
2546 if (bundle->getVerbose()) {
2547 assets->print(String8());
2548 }
2549
Adam Lesinskifab50872014-04-16 14:40:42 -07002550 // Create the ApkBuilder, which will collect the compiled files
2551 // to write to the final APK (or sets of APKs if we are building
2552 // a Split APK.
2553 builder = new ApkBuilder(configFilter);
2554
2555 // If we are generating a Split APK, find out which configurations to split on.
2556 if (bundle->getSplitConfigurations().size() > 0) {
2557 const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2558 const size_t numSplits = splitStrs.size();
2559 for (size_t i = 0; i < numSplits; i++) {
2560 std::set<ConfigDescription> configs;
2561 if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2562 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2563 goto bail;
2564 }
2565
2566 err = builder->createSplitForConfigs(configs);
2567 if (err != NO_ERROR) {
2568 goto bail;
2569 }
2570 }
2571 }
2572
Adam Lesinski282e1812014-01-23 18:17:42 -08002573 // If they asked for any fileAs that need to be compiled, do so.
2574 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002575 err = buildResources(bundle, assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002576 if (err != 0) {
2577 goto bail;
2578 }
2579 }
2580
2581 // At this point we've read everything and processed everything. From here
2582 // on out it's just writing output files.
2583 if (SourcePos::hasErrors()) {
2584 goto bail;
2585 }
2586
2587 // Update symbols with information about which ones are needed as Java symbols.
2588 assets->applyJavaSymbols();
2589 if (SourcePos::hasErrors()) {
2590 goto bail;
2591 }
2592
2593 // If we've been asked to generate a dependency file, do that here
2594 if (bundle->getGenDependencies()) {
2595 // If this is the packaging step, generate the dependency file next to
2596 // the output apk (e.g. bin/resources.ap_.d)
2597 if (outputAPKFile) {
2598 dependencyFile = String8(outputAPKFile);
2599 // Add the .d extension to the dependency file.
2600 dependencyFile.append(".d");
2601 } else {
2602 // Else if this is the R.java dependency generation step,
2603 // generate the dependency file in the R.java package subdirectory
2604 // e.g. gen/com/foo/app/R.java.d
2605 dependencyFile = String8(bundle->getRClassDir());
2606 dependencyFile.appendPath("R.java.d");
2607 }
2608 // Make sure we have a clean dependency file to start with
2609 fp = fopen(dependencyFile, "w");
2610 fclose(fp);
2611 }
2612
2613 // Write out R.java constants
2614 if (!assets->havePrivateSymbols()) {
2615 if (bundle->getCustomPackage() == NULL) {
2616 // Write the R.java file into the appropriate class directory
2617 // e.g. gen/com/foo/app/R.java
Adam Lesinski1e4663852014-08-15 14:47:28 -07002618 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002619 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002620 } else {
2621 const String8 customPkg(bundle->getCustomPackage());
Adam Lesinski1e4663852014-08-15 14:47:28 -07002622 err = writeResourceSymbols(bundle, assets, customPkg, true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002623 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002624 }
2625 if (err < 0) {
2626 goto bail;
2627 }
2628 // If we have library files, we're going to write our R.java file into
2629 // the appropriate class directory for those libraries as well.
2630 // e.g. gen/com/foo/app/lib/R.java
2631 if (bundle->getExtraPackages() != NULL) {
2632 // Split on colon
2633 String8 libs(bundle->getExtraPackages());
2634 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2635 while (packageString != NULL) {
2636 // Write the R.java file out with the correct package name
Marcin Kosiba0f3a5a62014-09-11 13:48:48 +01002637 err = writeResourceSymbols(bundle, assets, String8(packageString), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002638 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002639 if (err < 0) {
2640 goto bail;
2641 }
2642 packageString = strtok(NULL, ":");
2643 }
2644 libs.unlockBuffer();
2645 }
2646 } else {
Adam Lesinski1e4663852014-08-15 14:47:28 -07002647 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002648 if (err < 0) {
2649 goto bail;
2650 }
Adam Lesinski1e4663852014-08-15 14:47:28 -07002651 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002652 if (err < 0) {
2653 goto bail;
2654 }
2655 }
2656
2657 // Write out the ProGuard file
2658 err = writeProguardFile(bundle, assets);
2659 if (err < 0) {
2660 goto bail;
2661 }
2662
Rohit Agrawal682583c2016-04-21 16:29:58 -07002663 // Write out the Main Dex ProGuard file
2664 err = writeMainDexProguardFile(bundle, assets);
2665 if (err < 0) {
2666 goto bail;
2667 }
2668
Adam Lesinski282e1812014-01-23 18:17:42 -08002669 // Write the apk
2670 if (outputAPKFile) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002671 // Gather all resources and add them to the APK Builder. The builder will then
2672 // figure out which Split they belong in.
2673 err = addResourcesToBuilder(assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002674 if (err != NO_ERROR) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002675 goto bail;
2676 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002677
2678 const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2679 const size_t numSplits = splits.size();
2680 for (size_t i = 0; i < numSplits; i++) {
2681 const sp<ApkSplit>& split = splits[i];
2682 String8 outputPath = buildApkName(String8(outputAPKFile), split);
2683 err = writeAPK(bundle, outputPath, split);
2684 if (err != NO_ERROR) {
2685 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2686 goto bail;
2687 }
2688 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002689 }
2690
2691 // If we've been asked to generate a dependency file, we need to finish up here.
2692 // the writeResourceSymbols and writeAPK functions have already written the target
2693 // half of the dependency file, now we need to write the prerequisites. (files that
2694 // the R.java file or .ap_ file depend on)
2695 if (bundle->getGenDependencies()) {
2696 // Now that writeResourceSymbols or writeAPK has taken care of writing
2697 // the targets to our dependency file, we'll write the prereqs
2698 fp = fopen(dependencyFile, "a+");
2699 fprintf(fp, " : ");
2700 bool includeRaw = (outputAPKFile != NULL);
2701 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2702 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2703 // and therefore was not added to our pathstores during slurping
2704 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2705 fclose(fp);
2706 }
2707
2708 retVal = 0;
2709bail:
2710 if (SourcePos::hasErrors()) {
2711 SourcePos::printErrors(stderr);
2712 }
2713 return retVal;
2714}
2715
2716/*
2717 * Do PNG Crunching
2718 * PRECONDITIONS
2719 * -S flag points to a source directory containing drawable* folders
2720 * -C flag points to destination directory. The folder structure in the
2721 * source directory will be mirrored to the destination (cache) directory
2722 *
2723 * POSTCONDITIONS
2724 * Destination directory will be updated to match the PNG files in
Maurice Chu2675f762013-10-22 17:33:11 -07002725 * the source directory.
Adam Lesinski282e1812014-01-23 18:17:42 -08002726 */
2727int doCrunch(Bundle* bundle)
2728{
2729 fprintf(stdout, "Crunching PNG Files in ");
2730 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2731 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2732
2733 updatePreProcessedCache(bundle);
2734
2735 return NO_ERROR;
2736}
2737
2738/*
2739 * Do PNG Crunching on a single flag
2740 * -i points to a single png file
2741 * -o points to a single png output file
2742 */
2743int doSingleCrunch(Bundle* bundle)
2744{
2745 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2746 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2747
2748 String8 input(bundle->getSingleCrunchInputFile());
2749 String8 output(bundle->getSingleCrunchOutputFile());
2750
2751 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2752 // we can't return the status_t as it gets truncate to the lower 8 bits.
2753 return 42;
2754 }
2755
2756 return NO_ERROR;
2757}
2758
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002759int runInDaemonMode(Bundle* bundle) {
2760 std::cout << "Ready" << std::endl;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002761 for (std::string cmd; std::getline(std::cin, cmd);) {
2762 if (cmd == "quit") {
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002763 return NO_ERROR;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002764 } else if (cmd == "s") {
2765 // Two argument crunch
2766 std::string inputFile, outputFile;
2767 std::getline(std::cin, inputFile);
2768 std::getline(std::cin, outputFile);
2769 bundle->setSingleCrunchInputFile(inputFile.c_str());
2770 bundle->setSingleCrunchOutputFile(outputFile.c_str());
2771 std::cout << "Crunching " << inputFile << std::endl;
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002772 if (doSingleCrunch(bundle) != NO_ERROR) {
2773 std::cout << "Error" << std::endl;
2774 }
2775 std::cout << "Done" << std::endl;
2776 } else {
2777 // in case of invalid command, just bail out.
2778 std::cerr << "Unknown command" << std::endl;
2779 return -1;
2780 }
2781 }
2782 return -1;
2783}
2784
Adam Lesinski282e1812014-01-23 18:17:42 -08002785char CONSOLE_DATA[2925] = {
2786 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2787 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2788 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2789 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2790 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2791 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2792 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2793 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2794 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2795 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2796 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2797 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2798 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2799 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2800 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2801 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2802 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2803 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2804 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2805 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2806 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2807 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2808 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2809 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2810 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2811 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2812 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2813 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2814 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2815 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2816 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2817 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2818 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2819 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2820 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2821 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2822 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2823 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2824 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2825 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2826 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2827 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2828 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2829 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2830 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2831 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2832 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2833 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2834 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2835 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2836 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2837 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2838 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2839 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2840 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2841 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2842 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2843 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2844 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2845 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2846 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2847 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2848 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2849 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2850 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2851 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2852 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2853 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2854 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2855 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2856 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2857 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2858 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2859 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2860 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2861 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2862 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2863 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2864 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2865 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2866 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2867 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2868 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2869 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2870 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2871 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2872 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2873 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2874 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2875 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2876 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2877 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2878 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2879 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2880 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2881 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2882 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2883 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2884 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2885 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2886 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2887 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2888 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2889 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2890 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2891 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2892 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2893 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2894 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2895 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2896 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2897 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2898 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2899 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2900 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2901 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2902 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2903 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2904 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2905 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2906 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2907 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2908 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2909 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2910 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2911 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2912 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2913 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2914 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2915 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2916 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2917 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2918 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2919 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2920 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2921 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2922 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2923 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2924 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2925 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2926 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2927 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2928 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2929 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2930 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2931 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2932 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2933 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2934 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2935 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2936 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2937 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2938 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2939 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2940 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2941 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2942 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2943 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2944 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2945 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2946 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2947 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2948 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2949 };