blob: 447ace6e4e479c237533664c06b6543dce6fc407 [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,
Maurice Chu76327312013-10-16 18:28:46 -0700251 uint32_t attrRes, String8 attrLabel, String8* outError)
252{
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
Adam Lesinski94fc9122013-09-30 17:16:09 -0700402Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool offHost,
403 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) {}
Chih-Hung Hsiehd53e3be2016-05-03 10:02:51 -0700497 explicit Feature(bool required, int32_t version = -1) : required(required), version(version) {}
Adam Lesinski694d0a72016-04-06 16:12:04 -0700498
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
532static void addImpliedFeature(KeyedVector<String8, ImpliedFeature>* impliedFeatures,
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800533 const char* name, const char* reason, bool sdk23) {
Adam Lesinski2c72b682014-06-24 09:56:01 -0700534 String8 name8(name);
535 ssize_t idx = impliedFeatures->indexOfKey(name8);
536 if (idx < 0) {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800537 idx = impliedFeatures->add(name8, ImpliedFeature(name8, sdk23));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700538 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800539
540 ImpliedFeature* feature = &impliedFeatures->editValueAt(idx);
541
542 // A non-sdk 23 implied feature takes precedence.
543 if (feature->impliedBySdk23 && !sdk23) {
544 feature->impliedBySdk23 = false;
545 }
546 feature->reasons.add(String8(reason));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700547}
548
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800549static void printFeatureGroupImpl(const FeatureGroup& grp,
550 const KeyedVector<String8, ImpliedFeature>* impliedFeatures) {
Adam Lesinski2c72b682014-06-24 09:56:01 -0700551 printf("feature-group: label='%s'\n", grp.label.string());
552
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700553 if (grp.openGLESVersion > 0) {
554 printf(" uses-gl-es: '0x%x'\n", grp.openGLESVersion);
555 }
556
Adam Lesinski2c72b682014-06-24 09:56:01 -0700557 const size_t numFeatures = grp.features.size();
558 for (size_t i = 0; i < numFeatures; i++) {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700559 const Feature& feature = grp.features[i];
560 const bool required = feature.required;
561 const int32_t version = feature.version;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700562
563 const String8& featureName = grp.features.keyAt(i);
Adam Lesinski694d0a72016-04-06 16:12:04 -0700564 printf(" uses-feature%s: name='%s'", (required ? "" : "-not-required"),
Adam Lesinski2c72b682014-06-24 09:56:01 -0700565 ResTable::normalizeForOutput(featureName.string()).string());
Adam Lesinski694d0a72016-04-06 16:12:04 -0700566
567 if (version > 0) {
568 printf(" version='%d'", version);
569 }
570 printf("\n");
Adam Lesinski2c72b682014-06-24 09:56:01 -0700571 }
572
573 const size_t numImpliedFeatures =
574 (impliedFeatures != NULL) ? impliedFeatures->size() : 0;
575 for (size_t i = 0; i < numImpliedFeatures; i++) {
576 const ImpliedFeature& impliedFeature = impliedFeatures->valueAt(i);
577 if (grp.features.indexOfKey(impliedFeature.name) >= 0) {
578 // The feature is explicitly set, no need to use implied
579 // definition.
580 continue;
581 }
582
583 String8 printableFeatureName(ResTable::normalizeForOutput(
584 impliedFeature.name.string()));
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800585 const char* sdk23Suffix = impliedFeature.impliedBySdk23 ? "-sdk-23" : "";
586
587 printf(" uses-feature%s: name='%s'\n", sdk23Suffix, printableFeatureName.string());
588 printf(" uses-implied-feature%s: name='%s' reason='", sdk23Suffix,
589 printableFeatureName.string());
Adam Lesinski2c72b682014-06-24 09:56:01 -0700590 const size_t numReasons = impliedFeature.reasons.size();
591 for (size_t j = 0; j < numReasons; j++) {
592 printf("%s", impliedFeature.reasons[j].string());
593 if (j + 2 < numReasons) {
594 printf(", ");
595 } else if (j + 1 < numReasons) {
596 printf(", and ");
597 }
598 }
599 printf("'\n");
600 }
601}
602
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800603static void printFeatureGroup(const FeatureGroup& grp) {
604 printFeatureGroupImpl(grp, NULL);
605}
606
607static void printDefaultFeatureGroup(const FeatureGroup& grp,
608 const KeyedVector<String8, ImpliedFeature>& impliedFeatures) {
609 printFeatureGroupImpl(grp, &impliedFeatures);
610}
611
Adam Lesinski2c72b682014-06-24 09:56:01 -0700612static void addParentFeatures(FeatureGroup* grp, const String8& name) {
613 if (name == "android.hardware.camera.autofocus" ||
614 name == "android.hardware.camera.flash") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700615 grp->features.add(String8("android.hardware.camera"), Feature(true));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700616 } else if (name == "android.hardware.location.gps" ||
617 name == "android.hardware.location.network") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700618 grp->features.add(String8("android.hardware.location"), Feature(true));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700619 } else if (name == "android.hardware.touchscreen.multitouch") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700620 grp->features.add(String8("android.hardware.touchscreen"), Feature(true));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700621 } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700622 grp->features.add(String8("android.hardware.touchscreen.multitouch"), Feature(true));
623 grp->features.add(String8("android.hardware.touchscreen"), Feature(true));
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700624 } else if (name == "android.hardware.opengles.aep") {
625 const int openGLESVersion31 = 0x00030001;
626 if (openGLESVersion31 > grp->openGLESVersion) {
627 grp->openGLESVersion = openGLESVersion31;
628 }
Adam Lesinski2c72b682014-06-24 09:56:01 -0700629 }
630}
631
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800632static void addImpliedFeaturesForPermission(const int targetSdk, const String8& name,
633 KeyedVector<String8, ImpliedFeature>* impliedFeatures,
634 bool impliedBySdk23Permission) {
635 if (name == "android.permission.CAMERA") {
636 addImpliedFeature(impliedFeatures, "android.hardware.camera",
637 String8::format("requested %s permission", name.string())
638 .string(), impliedBySdk23Permission);
639 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
640 addImpliedFeature(impliedFeatures, "android.hardware.location.gps",
641 String8::format("requested %s permission", name.string())
642 .string(), impliedBySdk23Permission);
643 addImpliedFeature(impliedFeatures, "android.hardware.location",
644 String8::format("requested %s permission", name.string())
645 .string(), impliedBySdk23Permission);
646 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
647 addImpliedFeature(impliedFeatures, "android.hardware.location",
648 String8::format("requested %s permission", name.string())
649 .string(), impliedBySdk23Permission);
650 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
651 addImpliedFeature(impliedFeatures, "android.hardware.location.network",
652 String8::format("requested %s permission", name.string())
653 .string(), impliedBySdk23Permission);
654 addImpliedFeature(impliedFeatures, "android.hardware.location",
655 String8::format("requested %s permission", name.string())
656 .string(), impliedBySdk23Permission);
657 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
658 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
659 addImpliedFeature(impliedFeatures, "android.hardware.location",
660 String8::format("requested %s permission", name.string())
661 .string(), impliedBySdk23Permission);
662 } else if (name == "android.permission.BLUETOOTH" ||
663 name == "android.permission.BLUETOOTH_ADMIN") {
664 if (targetSdk > 4) {
665 addImpliedFeature(impliedFeatures, "android.hardware.bluetooth",
666 String8::format("requested %s permission", name.string())
667 .string(), impliedBySdk23Permission);
668 addImpliedFeature(impliedFeatures, "android.hardware.bluetooth",
669 "targetSdkVersion > 4", impliedBySdk23Permission);
670 }
671 } else if (name == "android.permission.RECORD_AUDIO") {
672 addImpliedFeature(impliedFeatures, "android.hardware.microphone",
673 String8::format("requested %s permission", name.string())
674 .string(), impliedBySdk23Permission);
675 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
676 name == "android.permission.CHANGE_WIFI_STATE" ||
677 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
678 addImpliedFeature(impliedFeatures, "android.hardware.wifi",
679 String8::format("requested %s permission", name.string())
680 .string(), impliedBySdk23Permission);
681 } else if (name == "android.permission.CALL_PHONE" ||
682 name == "android.permission.CALL_PRIVILEGED" ||
683 name == "android.permission.MODIFY_PHONE_STATE" ||
684 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
685 name == "android.permission.READ_SMS" ||
686 name == "android.permission.RECEIVE_SMS" ||
687 name == "android.permission.RECEIVE_MMS" ||
688 name == "android.permission.RECEIVE_WAP_PUSH" ||
689 name == "android.permission.SEND_SMS" ||
690 name == "android.permission.WRITE_APN_SETTINGS" ||
691 name == "android.permission.WRITE_SMS") {
692 addImpliedFeature(impliedFeatures, "android.hardware.telephony",
693 String8("requested a telephony permission").string(),
694 impliedBySdk23Permission);
695 }
696}
697
Adam Lesinski282e1812014-01-23 18:17:42 -0800698/*
699 * Handle the "dump" command, to extract select data from an archive.
700 */
701extern char CONSOLE_DATA[2925]; // see EOF
702int doDump(Bundle* bundle)
703{
704 status_t result = UNKNOWN_ERROR;
Adam Lesinski282e1812014-01-23 18:17:42 -0800705
706 if (bundle->getFileSpecCount() < 1) {
707 fprintf(stderr, "ERROR: no dump option specified\n");
708 return 1;
709 }
710
711 if (bundle->getFileSpecCount() < 2) {
712 fprintf(stderr, "ERROR: no dump file specified\n");
713 return 1;
714 }
715
716 const char* option = bundle->getFileSpecEntry(0);
717 const char* filename = bundle->getFileSpecEntry(1);
718
719 AssetManager assets;
Narayan Kamathf85e41f2014-01-24 14:14:30 +0000720 int32_t assetsCookie;
Adam Lesinski282e1812014-01-23 18:17:42 -0800721 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
722 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
723 return 1;
724 }
725
726 // Make a dummy config for retrieving resources... we need to supply
727 // non-default values for some configs so that we can retrieve resources
728 // in the app that don't have a default. The most important of these is
729 // the API version because key resources like icons will have an implicit
730 // version if they are using newer config types like density.
731 ResTable_config config;
Narayan Kamath91447d82014-01-21 15:32:36 +0000732 memset(&config, 0, sizeof(ResTable_config));
Adam Lesinski282e1812014-01-23 18:17:42 -0800733 config.language[0] = 'e';
734 config.language[1] = 'n';
735 config.country[0] = 'U';
736 config.country[1] = 'S';
737 config.orientation = ResTable_config::ORIENTATION_PORT;
738 config.density = ResTable_config::DENSITY_MEDIUM;
739 config.sdkVersion = 10000; // Very high.
740 config.screenWidthDp = 320;
741 config.screenHeightDp = 480;
742 config.smallestScreenWidthDp = 320;
Adam Lesinskic2dea8d2014-08-04 16:40:41 -0700743 config.screenLayout |= ResTable_config::SCREENSIZE_NORMAL;
Adam Lesinski282e1812014-01-23 18:17:42 -0800744 assets.setConfiguration(config);
745
746 const ResTable& res = assets.getResources(false);
Dan Albert68001652014-09-09 09:51:01 -0700747 if (res.getError() != NO_ERROR) {
Adam Lesinski25e9d552014-05-19 15:01:43 -0700748 fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
Adam Lesinski63e646e2014-07-30 11:40:39 -0700749 return 1;
Adam Lesinski282e1812014-01-23 18:17:42 -0800750 }
751
Adam Lesinski694d0a72016-04-06 16:12:04 -0700752 // Source for AndroidManifest.xml
753 const String8 manifestFile = String8::format("%s@AndroidManifest.xml", filename);
754
Adam Lesinski2cb761e2014-08-15 13:59:02 -0700755 // The dynamicRefTable can be null if there are no resources for this asset cookie.
756 // This fine.
Adam Lesinski63e646e2014-07-30 11:40:39 -0700757 const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700758
759 Asset* asset = NULL;
760
Adam Lesinski282e1812014-01-23 18:17:42 -0800761 if (strcmp("resources", option) == 0) {
Elliott Hughesba3fe562015-08-12 14:49:53 -0700762#ifndef __ANDROID__
Adam Lesinski282e1812014-01-23 18:17:42 -0800763 res.print(bundle->getValues());
764#endif
765
766 } else if (strcmp("strings", option) == 0) {
767 const ResStringPool* pool = res.getTableStringBlock(0);
768 printStringPool(pool);
769
770 } else if (strcmp("xmltree", option) == 0) {
771 if (bundle->getFileSpecCount() < 3) {
772 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
773 goto bail;
774 }
775
776 for (int i=2; i<bundle->getFileSpecCount(); i++) {
777 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700778 ResXMLTree tree(dynamicRefTable);
779 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800780 if (asset == NULL) {
781 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
782 goto bail;
783 }
784
785 if (tree.setTo(asset->getBuffer(true),
786 asset->getLength()) != NO_ERROR) {
787 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
788 goto bail;
789 }
790 tree.restart();
791 printXMLBlock(&tree);
792 tree.uninit();
793 delete asset;
794 asset = NULL;
795 }
796
797 } else if (strcmp("xmlstrings", option) == 0) {
798 if (bundle->getFileSpecCount() < 3) {
799 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
800 goto bail;
801 }
802
803 for (int i=2; i<bundle->getFileSpecCount(); i++) {
804 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700805 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800806 if (asset == NULL) {
807 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
808 goto bail;
809 }
810
Adam Lesinski63e646e2014-07-30 11:40:39 -0700811 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800812 if (tree.setTo(asset->getBuffer(true),
813 asset->getLength()) != NO_ERROR) {
814 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
815 goto bail;
816 }
817 printStringPool(&tree.getStrings());
818 delete asset;
819 asset = NULL;
820 }
821
822 } else {
Adam Lesinski63e646e2014-07-30 11:40:39 -0700823 asset = assets.openNonAsset(assetsCookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800824 if (asset == NULL) {
825 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
826 goto bail;
827 }
828
Adam Lesinski63e646e2014-07-30 11:40:39 -0700829 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800830 if (tree.setTo(asset->getBuffer(true),
831 asset->getLength()) != NO_ERROR) {
832 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
833 goto bail;
834 }
835 tree.restart();
836
837 if (strcmp("permissions", option) == 0) {
838 size_t len;
839 ResXMLTree::event_code_t code;
840 int depth = 0;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800841 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
842 code != ResXMLTree::BAD_DOCUMENT) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800843 if (code == ResXMLTree::END_TAG) {
844 depth--;
845 continue;
846 }
847 if (code != ResXMLTree::START_TAG) {
848 continue;
849 }
850 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700851 const char16_t* ctag16 = tree.getElementName(&len);
852 if (ctag16 == NULL) {
853 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
854 goto bail;
855 }
856 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800857 //printf("Depth %d tag %s\n", depth, tag.string());
858 if (depth == 1) {
859 if (tag != "manifest") {
860 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
861 goto bail;
862 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700863 String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -0700864 printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800865 } else if (depth == 2) {
866 if (tag == "permission") {
867 String8 error;
868 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
869 if (error != "") {
870 fprintf(stderr, "ERROR: %s\n", error.string());
871 goto bail;
872 }
873
874 if (name == "") {
875 fprintf(stderr, "ERROR: missing 'android:name' for permission\n");
876 goto bail;
877 }
878 printf("permission: %s\n",
879 ResTable::normalizeForOutput(name.string()).string());
880 } else if (tag == "uses-permission") {
881 String8 error;
882 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
883 if (error != "") {
884 fprintf(stderr, "ERROR: %s\n", error.string());
885 goto bail;
886 }
887
888 if (name == "") {
889 fprintf(stderr, "ERROR: missing 'android:name' for uses-permission\n");
890 goto bail;
891 }
892 printUsesPermission(name,
893 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
894 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
895 } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
896 String8 error;
897 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
898 if (error != "") {
899 fprintf(stderr, "ERROR: %s\n", error.string());
900 goto bail;
901 }
902
903 if (name == "") {
904 fprintf(stderr, "ERROR: missing 'android:name' for "
905 "uses-permission-sdk-23\n");
906 goto bail;
907 }
908 printUsesPermissionSdk23(
909 name,
910 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
Adam Lesinski282e1812014-01-23 18:17:42 -0800911 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800912 }
913 }
914 } else if (strcmp("badging", option) == 0) {
915 Vector<String8> locales;
916 res.getLocales(&locales);
917
918 Vector<ResTable_config> configs;
919 res.getConfigurations(&configs);
920 SortedVector<int> densities;
921 const size_t NC = configs.size();
922 for (size_t i=0; i<NC; i++) {
923 int dens = configs[i].density;
924 if (dens == 0) {
925 dens = 160;
926 }
927 densities.add(dens);
928 }
929
930 size_t len;
931 ResXMLTree::event_code_t code;
932 int depth = 0;
933 String8 error;
934 bool withinActivity = false;
935 bool isMainActivity = false;
936 bool isLauncherActivity = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800937 bool isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800938 bool isSearchable = false;
939 bool withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700940 bool withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700941 bool withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800942 bool withinReceiver = false;
943 bool withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700944 bool withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800945 bool withinIntentFilter = false;
946 bool hasMainActivity = false;
947 bool hasOtherActivities = false;
948 bool hasOtherReceivers = false;
949 bool hasOtherServices = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700950 bool hasIntentFilter = false;
951
Adam Lesinski282e1812014-01-23 18:17:42 -0800952 bool hasWallpaperService = false;
953 bool hasImeService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700954 bool hasAccessibilityService = false;
955 bool hasPrintService = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800956 bool hasWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700957 bool hasDeviceAdminReceiver = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700958 bool hasPaymentService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700959 bool hasDocumentsProvider = false;
960 bool hasCameraActivity = false;
961 bool hasCameraSecureActivity = false;
962 bool hasLauncher = false;
963 bool hasNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400964 bool hasDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700965
Adam Lesinski282e1812014-01-23 18:17:42 -0800966 bool actMainActivity = false;
967 bool actWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700968 bool actDeviceAdminEnabled = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800969 bool actImeService = false;
970 bool actWallpaperService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700971 bool actAccessibilityService = false;
972 bool actPrintService = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700973 bool actHostApduService = false;
974 bool actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700975 bool actDocumentsProvider = false;
976 bool actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400977 bool actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700978 bool actCamera = false;
979 bool actCameraSecure = false;
980 bool catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700981 bool hasMetaHostPaymentCategory = false;
982 bool hasMetaOffHostPaymentCategory = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700983
984 // These permissions are required by services implementing services
985 // the system binds to (IME, Accessibility, PrintServices, etc.)
986 bool hasBindDeviceAdminPermission = false;
987 bool hasBindInputMethodPermission = false;
988 bool hasBindAccessibilityServicePermission = false;
989 bool hasBindPrintServicePermission = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700990 bool hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700991 bool hasRequiredSafAttributes = false;
992 bool hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400993 bool hasBindDreamServicePermission = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800994
995 // These two implement the implicit permissions that are granted
996 // to pre-1.6 applications.
997 bool hasWriteExternalStoragePermission = false;
998 bool hasReadPhoneStatePermission = false;
999
1000 // If an app requests write storage, they will also get read storage.
1001 bool hasReadExternalStoragePermission = false;
1002
1003 // Implement transition to read and write call log.
1004 bool hasReadContactsPermission = false;
1005 bool hasWriteContactsPermission = false;
1006 bool hasReadCallLogPermission = false;
1007 bool hasWriteCallLogPermission = false;
1008
Adam Lesinskie47fd122014-08-15 22:25:36 -07001009 // If an app declares itself as multiArch, we report the
1010 // native libraries differently.
1011 bool hasMultiArch = false;
1012
Adam Lesinski282e1812014-01-23 18:17:42 -08001013 // This next group of variables is used to implement a group of
1014 // backward-compatibility heuristics necessitated by the addition of
1015 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
1016 // heuristic is "if an app requests a permission but doesn't explicitly
1017 // request the corresponding <uses-feature>, presume it's there anyway".
Adam Lesinski2c72b682014-06-24 09:56:01 -07001018
Adam Lesinski282e1812014-01-23 18:17:42 -08001019 // 2.2 also added some other features that apps can request, but that
1020 // have no corresponding permission, so we cannot implement any
1021 // back-compatibility heuristic for them. The below are thus unnecessary
1022 // (but are retained here for documentary purposes.)
1023 //bool specCompassFeature = false;
1024 //bool specAccelerometerFeature = false;
1025 //bool specProximityFeature = false;
1026 //bool specAmbientLightFeature = false;
1027 //bool specLiveWallpaperFeature = false;
1028
1029 int targetSdk = 0;
1030 int smallScreen = 1;
1031 int normalScreen = 1;
1032 int largeScreen = 1;
1033 int xlargeScreen = 1;
1034 int anyDensity = 1;
1035 int requiresSmallestWidthDp = 0;
1036 int compatibleWidthLimitDp = 0;
1037 int largestWidthLimitDp = 0;
1038 String8 pkg;
1039 String8 activityName;
1040 String8 activityLabel;
1041 String8 activityIcon;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001042 String8 activityBanner;
Adam Lesinski282e1812014-01-23 18:17:42 -08001043 String8 receiverName;
1044 String8 serviceName;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001045 Vector<String8> supportedInput;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001046
1047 FeatureGroup commonFeatures;
1048 Vector<FeatureGroup> featureGroups;
1049 KeyedVector<String8, ImpliedFeature> impliedFeatures;
1050
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001051 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
1052 code != ResXMLTree::BAD_DOCUMENT) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001053 if (code == ResXMLTree::END_TAG) {
1054 depth--;
1055 if (depth < 2) {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001056 if (withinSupportsInput && !supportedInput.isEmpty()) {
1057 printf("supports-input: '");
1058 const size_t N = supportedInput.size();
1059 for (size_t i=0; i<N; i++) {
Maurice Chu2675f762013-10-22 17:33:11 -07001060 printf("%s", ResTable::normalizeForOutput(
1061 supportedInput[i].string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001062 if (i != N - 1) {
1063 printf("' '");
1064 } else {
1065 printf("'\n");
1066 }
1067 }
1068 supportedInput.clear();
1069 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001070 withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001071 withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001072 withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001073 } else if (depth < 3) {
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001074 if (withinActivity && isMainActivity) {
Maurice Chu2675f762013-10-22 17:33:11 -07001075 String8 aName(getComponentName(pkg, activityName));
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001076 if (isLauncherActivity) {
1077 printf("launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001078 if (aName.length() > 0) {
1079 printf(" name='%s' ",
1080 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001081 }
1082 printf(" label='%s' icon='%s'\n",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001083 ResTable::normalizeForOutput(activityLabel.string())
1084 .string(),
1085 ResTable::normalizeForOutput(activityIcon.string())
1086 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001087 }
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001088 if (isLeanbackLauncherActivity) {
1089 printf("leanback-launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001090 if (aName.length() > 0) {
1091 printf(" name='%s' ",
1092 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001093 }
1094 printf(" label='%s' icon='%s' banner='%s'\n",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001095 ResTable::normalizeForOutput(activityLabel.string())
1096 .string(),
1097 ResTable::normalizeForOutput(activityIcon.string())
1098 .string(),
1099 ResTable::normalizeForOutput(activityBanner.string())
1100 .string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001101 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001102 }
1103 if (!hasIntentFilter) {
1104 hasOtherActivities |= withinActivity;
1105 hasOtherReceivers |= withinReceiver;
1106 hasOtherServices |= withinService;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001107 } else {
1108 if (withinService) {
1109 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
1110 hasBindNfcServicePermission);
1111 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
1112 hasBindNfcServicePermission);
1113 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001114 }
1115 withinActivity = false;
1116 withinService = false;
1117 withinReceiver = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001118 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001119 hasIntentFilter = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001120 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001121 } else if (depth < 4) {
1122 if (withinIntentFilter) {
1123 if (withinActivity) {
1124 hasMainActivity |= actMainActivity;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001125 hasLauncher |= catLauncher;
1126 hasCameraActivity |= actCamera;
1127 hasCameraSecureActivity |= actCameraSecure;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001128 hasOtherActivities |=
1129 !actMainActivity && !actCamera && !actCameraSecure;
Adam Lesinski282e1812014-01-23 18:17:42 -08001130 } else if (withinReceiver) {
1131 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001132 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
1133 hasBindDeviceAdminPermission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001134 hasOtherReceivers |=
1135 (!actWidgetReceivers && !actDeviceAdminEnabled);
Adam Lesinski282e1812014-01-23 18:17:42 -08001136 } else if (withinService) {
1137 hasImeService |= actImeService;
1138 hasWallpaperService |= actWallpaperService;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001139 hasAccessibilityService |= (actAccessibilityService &&
1140 hasBindAccessibilityServicePermission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001141 hasPrintService |=
1142 (actPrintService && hasBindPrintServicePermission);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001143 hasNotificationListenerService |= actNotificationListenerService &&
1144 hasBindNotificationListenerServicePermission;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001145 hasDreamService |= actDreamService && hasBindDreamServicePermission;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001146 hasOtherServices |= (!actImeService && !actWallpaperService &&
Adam Lesinski94fc9122013-09-30 17:16:09 -07001147 !actAccessibilityService && !actPrintService &&
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001148 !actHostApduService && !actOffHostApduService &&
1149 !actNotificationListenerService);
1150 } else if (withinProvider) {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001151 hasDocumentsProvider |=
1152 actDocumentsProvider && hasRequiredSafAttributes;
Adam Lesinski282e1812014-01-23 18:17:42 -08001153 }
1154 }
1155 withinIntentFilter = false;
1156 }
1157 continue;
1158 }
1159 if (code != ResXMLTree::START_TAG) {
1160 continue;
1161 }
1162 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001163
1164 const char16_t* ctag16 = tree.getElementName(&len);
1165 if (ctag16 == NULL) {
1166 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
1167 goto bail;
1168 }
1169 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -08001170 //printf("Depth %d, %s\n", depth, tag.string());
1171 if (depth == 1) {
1172 if (tag != "manifest") {
1173 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
1174 goto bail;
1175 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001176 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -07001177 printf("package: name='%s' ",
1178 ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001179 int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR,
1180 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001181 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001182 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n",
1183 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001184 goto bail;
1185 }
1186 if (versionCode > 0) {
1187 printf("versionCode='%d' ", versionCode);
1188 } else {
1189 printf("versionCode='' ");
1190 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001191 String8 versionName = AaptXml::getResolvedAttribute(res, tree,
1192 VERSION_NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001193 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001194 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n",
1195 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001196 goto bail;
1197 }
Adam Lesinski25d35a92014-08-11 09:41:56 -07001198 printf("versionName='%s'",
Maurice Chu2675f762013-10-22 17:33:11 -07001199 ResTable::normalizeForOutput(versionName.string()).string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001200
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001201 String8 splitName = AaptXml::getAttribute(tree, NULL, "split");
Adam Lesinski25d35a92014-08-11 09:41:56 -07001202 if (!splitName.isEmpty()) {
1203 printf(" split='%s'", ResTable::normalizeForOutput(
1204 splitName.string()).string());
1205 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001206
Adam Lesinski5283fab2014-08-29 11:23:55 -07001207 String8 platformVersionName = AaptXml::getAttribute(tree, NULL,
1208 "platformBuildVersionName");
1209 printf(" platformBuildVersionName='%s'", platformVersionName.string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001210 printf("\n");
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001211
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001212 int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
1213 INSTALL_LOCATION_ATTR, &error);
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001214 if (error != "") {
1215 fprintf(stderr, "ERROR getting 'android:installLocation' attribute: %s\n",
1216 error.string());
1217 goto bail;
1218 }
1219
1220 if (installLocation >= 0) {
1221 printf("install-location:'");
1222 switch (installLocation) {
1223 case 0:
1224 printf("auto");
1225 break;
1226 case 1:
1227 printf("internalOnly");
1228 break;
1229 case 2:
1230 printf("preferExternal");
1231 break;
1232 default:
1233 fprintf(stderr, "Invalid installLocation %d\n", installLocation);
1234 goto bail;
1235 }
1236 printf("'\n");
1237 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001238 } else if (depth == 2) {
1239 withinApplication = false;
1240 if (tag == "application") {
1241 withinApplication = true;
1242
1243 String8 label;
1244 const size_t NL = locales.size();
1245 for (size_t i=0; i<NL; i++) {
1246 const char* localeStr = locales[i].string();
1247 assets.setLocale(localeStr != NULL ? localeStr : "");
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001248 String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1249 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001250 if (llabel != "") {
1251 if (localeStr == NULL || strlen(localeStr) == 0) {
1252 label = llabel;
Maurice Chu2675f762013-10-22 17:33:11 -07001253 printf("application-label:'%s'\n",
1254 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001255 } else {
1256 if (label == "") {
1257 label = llabel;
1258 }
1259 printf("application-label-%s:'%s'\n", localeStr,
Maurice Chu2675f762013-10-22 17:33:11 -07001260 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001261 }
1262 }
1263 }
1264
1265 ResTable_config tmpConfig = config;
1266 const size_t ND = densities.size();
1267 for (size_t i=0; i<ND; i++) {
1268 tmpConfig.density = densities[i];
1269 assets.setConfiguration(tmpConfig);
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001270 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1271 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001272 if (icon != "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001273 printf("application-icon-%d:'%s'\n", densities[i],
1274 ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001275 }
1276 }
1277 assets.setConfiguration(config);
1278
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001279 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001280 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001281 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1282 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001283 goto bail;
1284 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001285 int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0,
1286 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001287 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001288 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n",
1289 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001290 goto bail;
1291 }
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001292
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001293 String8 banner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1294 &error);
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001295 if (error != "") {
1296 fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
1297 error.string());
1298 goto bail;
1299 }
Maurice Chu2675f762013-10-22 17:33:11 -07001300 printf("application: label='%s' ",
1301 ResTable::normalizeForOutput(label.string()).string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001302 printf("icon='%s'", ResTable::normalizeForOutput(icon.string()).string());
1303 if (banner != "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001304 printf(" banner='%s'",
1305 ResTable::normalizeForOutput(banner.string()).string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001306 }
1307 printf("\n");
Adam Lesinski282e1812014-01-23 18:17:42 -08001308 if (testOnly != 0) {
1309 printf("testOnly='%d'\n", testOnly);
1310 }
1311
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001312 int32_t isGame = AaptXml::getResolvedIntegerAttribute(res, tree,
1313 ISGAME_ATTR, 0, &error);
1314 if (error != "") {
1315 fprintf(stderr, "ERROR getting 'android:isGame' attribute: %s\n",
1316 error.string());
1317 goto bail;
1318 }
1319 if (isGame != 0) {
1320 printf("application-isGame\n");
1321 }
1322
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001323 int32_t debuggable = AaptXml::getResolvedIntegerAttribute(res, tree,
1324 DEBUGGABLE_ATTR, 0, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001325 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001326 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n",
1327 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001328 goto bail;
1329 }
1330 if (debuggable != 0) {
1331 printf("application-debuggable\n");
1332 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07001333
1334 // We must search by name because the multiArch flag hasn't been API
1335 // frozen yet.
1336 int32_t multiArchIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
1337 "multiArch");
1338 if (multiArchIndex >= 0) {
1339 Res_value value;
1340 if (tree.getAttributeValue(multiArchIndex, &value) != NO_ERROR) {
1341 if (value.dataType >= Res_value::TYPE_FIRST_INT &&
1342 value.dataType <= Res_value::TYPE_LAST_INT) {
1343 hasMultiArch = value.data;
1344 }
1345 }
1346 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001347 } else if (tag == "uses-sdk") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001348 int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR,
1349 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001350 if (error != "") {
1351 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001352 String8 name = AaptXml::getResolvedAttribute(res, tree,
1353 MIN_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001354 if (error != "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001355 fprintf(stderr,
1356 "ERROR getting 'android:minSdkVersion' attribute: %s\n",
Adam Lesinski282e1812014-01-23 18:17:42 -08001357 error.string());
1358 goto bail;
1359 }
1360 if (name == "Donut") targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001361 printf("sdkVersion:'%s'\n",
1362 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001363 } else if (code != -1) {
1364 targetSdk = code;
1365 printf("sdkVersion:'%d'\n", code);
1366 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001367 code = AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -08001368 if (code != -1) {
1369 printf("maxSdkVersion:'%d'\n", code);
1370 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001371 code = AaptXml::getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001372 if (error != "") {
1373 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001374 String8 name = AaptXml::getResolvedAttribute(res, tree,
1375 TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001376 if (error != "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001377 fprintf(stderr,
1378 "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
Adam Lesinski282e1812014-01-23 18:17:42 -08001379 error.string());
1380 goto bail;
1381 }
1382 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001383 printf("targetSdkVersion:'%s'\n",
1384 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001385 } else if (code != -1) {
1386 if (targetSdk < code) {
1387 targetSdk = code;
1388 }
1389 printf("targetSdkVersion:'%d'\n", code);
1390 }
1391 } else if (tag == "uses-configuration") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001392 int32_t reqTouchScreen = AaptXml::getIntegerAttribute(tree,
1393 REQ_TOUCH_SCREEN_ATTR, 0);
1394 int32_t reqKeyboardType = AaptXml::getIntegerAttribute(tree,
1395 REQ_KEYBOARD_TYPE_ATTR, 0);
1396 int32_t reqHardKeyboard = AaptXml::getIntegerAttribute(tree,
1397 REQ_HARD_KEYBOARD_ATTR, 0);
1398 int32_t reqNavigation = AaptXml::getIntegerAttribute(tree,
1399 REQ_NAVIGATION_ATTR, 0);
1400 int32_t reqFiveWayNav = AaptXml::getIntegerAttribute(tree,
1401 REQ_FIVE_WAY_NAV_ATTR, 0);
Adam Lesinski282e1812014-01-23 18:17:42 -08001402 printf("uses-configuration:");
1403 if (reqTouchScreen != 0) {
1404 printf(" reqTouchScreen='%d'", reqTouchScreen);
1405 }
1406 if (reqKeyboardType != 0) {
1407 printf(" reqKeyboardType='%d'", reqKeyboardType);
1408 }
1409 if (reqHardKeyboard != 0) {
1410 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1411 }
1412 if (reqNavigation != 0) {
1413 printf(" reqNavigation='%d'", reqNavigation);
1414 }
1415 if (reqFiveWayNav != 0) {
1416 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1417 }
1418 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001419 } else if (tag == "supports-input") {
1420 withinSupportsInput = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001421 } else if (tag == "supports-screens") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001422 smallScreen = AaptXml::getIntegerAttribute(tree,
1423 SMALL_SCREEN_ATTR, 1);
1424 normalScreen = AaptXml::getIntegerAttribute(tree,
1425 NORMAL_SCREEN_ATTR, 1);
1426 largeScreen = AaptXml::getIntegerAttribute(tree,
1427 LARGE_SCREEN_ATTR, 1);
1428 xlargeScreen = AaptXml::getIntegerAttribute(tree,
1429 XLARGE_SCREEN_ATTR, 1);
1430 anyDensity = AaptXml::getIntegerAttribute(tree,
1431 ANY_DENSITY_ATTR, 1);
1432 requiresSmallestWidthDp = AaptXml::getIntegerAttribute(tree,
1433 REQUIRES_SMALLEST_WIDTH_DP_ATTR, 0);
1434 compatibleWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1435 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, 0);
1436 largestWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1437 LARGEST_WIDTH_LIMIT_DP_ATTR, 0);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001438 } else if (tag == "feature-group") {
1439 withinFeatureGroup = true;
1440 FeatureGroup group;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001441 group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001442 if (error != "") {
1443 fprintf(stderr, "ERROR getting 'android:label' attribute:"
1444 " %s\n", error.string());
1445 goto bail;
1446 }
1447 featureGroups.add(group);
1448
Adam Lesinski282e1812014-01-23 18:17:42 -08001449 } else if (tag == "uses-feature") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001450 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001451 if (name != "" && error == "") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001452 const char* androidSchema =
1453 "http://schemas.android.com/apk/res/android";
Adam Lesinski282e1812014-01-23 18:17:42 -08001454
Adam Lesinski694d0a72016-04-06 16:12:04 -07001455 int32_t req = AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1,
1456 &error);
1457 if (error != "") {
1458 SourcePos(manifestFile, tree.getLineNumber()).error(
1459 "failed to read attribute 'android:required': %s",
1460 error.string());
1461 goto bail;
1462 }
1463
1464 int32_t version = AaptXml::getIntegerAttribute(tree, androidSchema,
1465 "version", 0, &error);
1466 if (error != "") {
1467 SourcePos(manifestFile, tree.getLineNumber()).error(
1468 "failed to read attribute 'android:version': %s",
1469 error.string());
1470 goto bail;
1471 }
1472
1473 commonFeatures.features.add(name, Feature(req != 0, version));
Adam Lesinski2c72b682014-06-24 09:56:01 -07001474 if (req) {
1475 addParentFeatures(&commonFeatures, name);
Adam Lesinski282e1812014-01-23 18:17:42 -08001476 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001477 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001478 int vers = AaptXml::getIntegerAttribute(tree,
Adam Lesinski282e1812014-01-23 18:17:42 -08001479 GL_ES_VERSION_ATTR, &error);
1480 if (error == "") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001481 if (vers > commonFeatures.openGLESVersion) {
1482 commonFeatures.openGLESVersion = vers;
1483 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001484 }
1485 }
1486 } else if (tag == "uses-permission") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001487 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001488 if (error != "") {
Adam Lesinski282e1812014-01-23 18:17:42 -08001489 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1490 error.string());
1491 goto bail;
1492 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001493
1494 if (name == "") {
1495 fprintf(stderr, "ERROR: missing 'android:name' for uses-permission\n");
1496 goto bail;
1497 }
1498
1499 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, false);
1500
1501 if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1502 hasWriteExternalStoragePermission = true;
1503 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1504 hasReadExternalStoragePermission = true;
1505 } else if (name == "android.permission.READ_PHONE_STATE") {
1506 hasReadPhoneStatePermission = true;
1507 } else if (name == "android.permission.READ_CONTACTS") {
1508 hasReadContactsPermission = true;
1509 } else if (name == "android.permission.WRITE_CONTACTS") {
1510 hasWriteContactsPermission = true;
1511 } else if (name == "android.permission.READ_CALL_LOG") {
1512 hasReadCallLogPermission = true;
1513 } else if (name == "android.permission.WRITE_CALL_LOG") {
1514 hasWriteCallLogPermission = true;
1515 }
1516
1517 printUsesPermission(name,
1518 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
1519 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
1520
1521 } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
1522 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1523 if (error != "") {
1524 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1525 error.string());
1526 goto bail;
1527 }
1528
1529 if (name == "") {
1530 fprintf(stderr, "ERROR: missing 'android:name' for "
1531 "uses-permission-sdk-23\n");
1532 goto bail;
1533 }
1534
1535 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, true);
1536
1537 printUsesPermissionSdk23(
1538 name, AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
1539
Adam Lesinski282e1812014-01-23 18:17:42 -08001540 } else if (tag == "uses-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001541 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001542 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001543 printf("uses-package:'%s'\n",
1544 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001545 } else {
1546 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1547 error.string());
1548 goto bail;
1549 }
1550 } else if (tag == "original-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001551 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001552 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001553 printf("original-package:'%s'\n",
1554 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001555 } else {
1556 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1557 error.string());
1558 goto bail;
1559 }
1560 } else if (tag == "supports-gl-texture") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001561 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001562 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001563 printf("supports-gl-texture:'%s'\n",
1564 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001565 } else {
1566 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1567 error.string());
1568 goto bail;
1569 }
1570 } else if (tag == "compatible-screens") {
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001571 printCompatibleScreens(tree, &error);
1572 if (error != "") {
1573 fprintf(stderr, "ERROR getting compatible screens: %s\n",
1574 error.string());
1575 goto bail;
1576 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001577 depth--;
1578 } else if (tag == "package-verifier") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001579 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001580 if (name != "" && error == "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001581 String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR,
1582 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001583 if (publicKey != "" && error == "") {
1584 printf("package-verifier: name='%s' publicKey='%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001585 ResTable::normalizeForOutput(name.string()).string(),
1586 ResTable::normalizeForOutput(publicKey.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001587 }
1588 }
1589 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001590 } else if (depth == 3) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001591 withinActivity = false;
1592 withinReceiver = false;
1593 withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001594 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001595 hasIntentFilter = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001596 hasMetaHostPaymentCategory = false;
1597 hasMetaOffHostPaymentCategory = false;
1598 hasBindDeviceAdminPermission = false;
1599 hasBindInputMethodPermission = false;
1600 hasBindAccessibilityServicePermission = false;
1601 hasBindPrintServicePermission = false;
1602 hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001603 hasRequiredSafAttributes = false;
1604 hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001605 hasBindDreamServicePermission = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001606 if (withinApplication) {
1607 if(tag == "activity") {
1608 withinActivity = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001609 activityName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001610 if (error != "") {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001611 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1612 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001613 goto bail;
1614 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001615
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001616 activityLabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1617 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001618 if (error != "") {
1619 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
1620 error.string());
1621 goto bail;
1622 }
1623
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001624 activityIcon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1625 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001626 if (error != "") {
1627 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1628 error.string());
1629 goto bail;
1630 }
1631
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001632 activityBanner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1633 &error);
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001634 if (error != "") {
1635 fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
1636 error.string());
1637 goto bail;
1638 }
1639
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001640 int32_t orien = AaptXml::getResolvedIntegerAttribute(res, tree,
Michael Wrightec4fdec2013-09-06 16:50:52 -07001641 SCREEN_ORIENTATION_ATTR, &error);
1642 if (error == "") {
1643 if (orien == 0 || orien == 6 || orien == 8) {
1644 // Requests landscape, sensorLandscape, or reverseLandscape.
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001645 addImpliedFeature(&impliedFeatures,
1646 "android.hardware.screen.landscape",
1647 "one or more activities have specified a "
1648 "landscape orientation",
1649 false);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001650 } else if (orien == 1 || orien == 7 || orien == 9) {
1651 // Requests portrait, sensorPortrait, or reversePortrait.
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001652 addImpliedFeature(&impliedFeatures,
1653 "android.hardware.screen.portrait",
1654 "one or more activities have specified a "
1655 "portrait orientation",
1656 false);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001657 }
1658 }
1659 } else if (tag == "uses-library") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001660 String8 libraryName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001661 if (error != "") {
1662 fprintf(stderr,
1663 "ERROR getting 'android:name' attribute for uses-library"
1664 " %s\n", error.string());
1665 goto bail;
1666 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001667 int req = AaptXml::getIntegerAttribute(tree,
1668 REQUIRED_ATTR, 1);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001669 printf("uses-library%s:'%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001670 req ? "" : "-not-required", ResTable::normalizeForOutput(
1671 libraryName.string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001672 } else if (tag == "receiver") {
1673 withinReceiver = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001674 receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001675
1676 if (error != "") {
1677 fprintf(stderr,
1678 "ERROR getting 'android:name' attribute for receiver:"
1679 " %s\n", error.string());
1680 goto bail;
1681 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001682
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001683 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1684 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001685 if (error == "") {
1686 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1687 hasBindDeviceAdminPermission = true;
1688 }
1689 } else {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001690 fprintf(stderr,
1691 "ERROR getting 'android:permission' attribute for"
1692 " receiver '%s': %s\n",
1693 receiverName.string(), error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001694 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001695 } else if (tag == "service") {
1696 withinService = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001697 serviceName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001698
1699 if (error != "") {
1700 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1701 "service:%s\n", error.string());
1702 goto bail;
1703 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001704
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001705 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1706 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001707 if (error == "") {
1708 if (permission == "android.permission.BIND_INPUT_METHOD") {
1709 hasBindInputMethodPermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001710 } else if (permission ==
1711 "android.permission.BIND_ACCESSIBILITY_SERVICE") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001712 hasBindAccessibilityServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001713 } else if (permission ==
1714 "android.permission.BIND_PRINT_SERVICE") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001715 hasBindPrintServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001716 } else if (permission ==
1717 "android.permission.BIND_NFC_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07001718 hasBindNfcServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001719 } else if (permission ==
1720 "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001721 hasBindNotificationListenerServicePermission = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001722 } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
1723 hasBindDreamServicePermission = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001724 }
1725 } else {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001726 fprintf(stderr, "ERROR getting 'android:permission' attribute for "
1727 "service '%s': %s\n", serviceName.string(), error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001728 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001729 } else if (tag == "provider") {
1730 withinProvider = true;
1731
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001732 bool exported = AaptXml::getResolvedIntegerAttribute(res, tree,
1733 EXPORTED_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001734 if (error != "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001735 fprintf(stderr,
1736 "ERROR getting 'android:exported' attribute for provider:"
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001737 " %s\n", error.string());
1738 goto bail;
1739 }
1740
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001741 bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute(
1742 res, tree, GRANT_URI_PERMISSIONS_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001743 if (error != "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001744 fprintf(stderr,
1745 "ERROR getting 'android:grantUriPermissions' attribute for "
1746 "provider: %s\n", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001747 goto bail;
1748 }
1749
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001750 String8 permission = AaptXml::getResolvedAttribute(res, tree,
1751 PERMISSION_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001752 if (error != "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001753 fprintf(stderr, "ERROR getting 'android:permission' attribute for "
1754 "provider: %s\n", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001755 goto bail;
1756 }
1757
1758 hasRequiredSafAttributes |= exported && grantUriPermissions &&
1759 permission == "android.permission.MANAGE_DOCUMENTS";
1760
Michael Wrightec4fdec2013-09-06 16:50:52 -07001761 } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001762 String8 metaDataName = AaptXml::getResolvedAttribute(res, tree,
1763 NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001764 if (error != "") {
1765 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1766 "meta-data:%s\n", error.string());
1767 goto bail;
1768 }
Maurice Chu2675f762013-10-22 17:33:11 -07001769 printf("meta-data: name='%s' ",
1770 ResTable::normalizeForOutput(metaDataName.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001771 printResolvedResourceAttribute(res, tree, VALUE_ATTR, String8("value"),
Maurice Chu76327312013-10-16 18:28:46 -07001772 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001773 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001774 // Try looking for a RESOURCE_ATTR
1775 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001776 printResolvedResourceAttribute(res, tree, RESOURCE_ATTR,
Maurice Chu76327312013-10-16 18:28:46 -07001777 String8("resource"), &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001778 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001779 fprintf(stderr, "ERROR getting 'android:value' or "
1780 "'android:resource' attribute for "
1781 "meta-data:%s\n", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001782 goto bail;
1783 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001784 }
Maurice Chu76327312013-10-16 18:28:46 -07001785 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001786 } else if (withinSupportsInput && tag == "input-type") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001787 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001788 if (name != "" && error == "") {
1789 supportedInput.add(name);
1790 } else {
1791 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1792 error.string());
1793 goto bail;
1794 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001795 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001796 } else if (withinFeatureGroup && tag == "uses-feature") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001797 const String8 androidSchema("http://schemas.android.com/apk/res/android");
Adam Lesinski2c72b682014-06-24 09:56:01 -07001798 FeatureGroup& top = featureGroups.editTop();
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001799
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001800 String8 name = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001801 if (name != "" && error == "") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001802 Feature feature(true);
1803
1804 int32_t featureVers = AaptXml::getIntegerAttribute(
1805 tree, androidSchema.string(), "version", 0, &error);
1806 if (error == "") {
1807 feature.version = featureVers;
1808 } else {
1809 SourcePos(manifestFile, tree.getLineNumber()).error(
1810 "failed to read attribute 'android:version': %s",
1811 error.string());
1812 goto bail;
1813 }
1814
1815 top.features.add(name, feature);
Adam Lesinskid3edfde2014-08-08 17:32:44 -07001816 addParentFeatures(&top, name);
Adam Lesinski694d0a72016-04-06 16:12:04 -07001817
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001818 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001819 int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR,
1820 &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001821 if (error == "") {
1822 if (vers > top.openGLESVersion) {
1823 top.openGLESVersion = vers;
1824 }
1825 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001826 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001827 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001828 } else if (depth == 4) {
1829 if (tag == "intent-filter") {
1830 hasIntentFilter = true;
1831 withinIntentFilter = true;
1832 actMainActivity = false;
1833 actWidgetReceivers = false;
1834 actImeService = false;
1835 actWallpaperService = false;
1836 actAccessibilityService = false;
1837 actPrintService = false;
1838 actDeviceAdminEnabled = false;
1839 actHostApduService = false;
1840 actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001841 actDocumentsProvider = false;
1842 actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001843 actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001844 actCamera = false;
1845 actCameraSecure = false;
1846 catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001847 } else if (withinService && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001848 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001849 if (error != "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001850 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1851 "meta-data tag in service '%s': %s\n", serviceName.string(),
1852 error.string());
Adam Lesinski94fc9122013-09-30 17:16:09 -07001853 goto bail;
1854 }
1855
1856 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1857 name == "android.nfc.cardemulation.off_host_apdu_service") {
1858 bool offHost = true;
1859 if (name == "android.nfc.cardemulation.host_apdu_service") {
1860 offHost = false;
1861 }
1862
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001863 String8 xmlPath = AaptXml::getResolvedAttribute(res, tree,
1864 RESOURCE_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001865 if (error != "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001866 fprintf(stderr, "ERROR getting 'android:resource' attribute for "
1867 "meta-data tag in service '%s': %s\n",
1868 serviceName.string(), error.string());
Adam Lesinski94fc9122013-09-30 17:16:09 -07001869 goto bail;
1870 }
1871
1872 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1873 offHost, &error);
1874 if (error != "") {
1875 fprintf(stderr, "ERROR getting AID category for service '%s'\n",
1876 serviceName.string());
1877 goto bail;
1878 }
1879
1880 const size_t catLen = categories.size();
1881 for (size_t i = 0; i < catLen; i++) {
1882 bool paymentCategory = (categories[i] == "payment");
1883 if (offHost) {
1884 hasMetaOffHostPaymentCategory |= paymentCategory;
1885 } else {
1886 hasMetaHostPaymentCategory |= paymentCategory;
1887 }
1888 }
1889 }
1890 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001891 } else if ((depth == 5) && withinIntentFilter) {
1892 String8 action;
1893 if (tag == "action") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001894 action = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001895 if (error != "") {
1896 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1897 error.string());
1898 goto bail;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001899 }
1900
Adam Lesinskia5018c92013-09-30 16:23:15 -07001901 if (withinActivity) {
1902 if (action == "android.intent.action.MAIN") {
1903 isMainActivity = true;
1904 actMainActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001905 } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
1906 action == "android.media.action.VIDEO_CAMERA") {
1907 actCamera = true;
1908 } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
1909 actCameraSecure = true;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001910 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001911 } else if (withinReceiver) {
1912 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1913 actWidgetReceivers = true;
1914 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1915 actDeviceAdminEnabled = true;
1916 }
1917 } else if (withinService) {
1918 if (action == "android.view.InputMethod") {
1919 actImeService = true;
1920 } else if (action == "android.service.wallpaper.WallpaperService") {
1921 actWallpaperService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001922 } else if (action ==
1923 "android.accessibilityservice.AccessibilityService") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001924 actAccessibilityService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001925 } else if (action =="android.printservice.PrintService") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001926 actPrintService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001927 } else if (action ==
1928 "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07001929 actHostApduService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001930 } else if (action ==
1931 "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07001932 actOffHostApduService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001933 } else if (action ==
1934 "android.service.notification.NotificationListenerService") {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001935 actNotificationListenerService = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001936 } else if (action == "android.service.dreams.DreamService") {
1937 actDreamService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001938 }
1939 } else if (withinProvider) {
1940 if (action == "android.content.action.DOCUMENTS_PROVIDER") {
1941 actDocumentsProvider = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001942 }
1943 }
1944 if (action == "android.intent.action.SEARCH") {
1945 isSearchable = true;
1946 }
1947 }
1948
1949 if (tag == "category") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001950 String8 category = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001951 if (error != "") {
1952 fprintf(stderr, "ERROR getting 'name' attribute: %s\n",
1953 error.string());
1954 goto bail;
1955 }
1956 if (withinActivity) {
1957 if (category == "android.intent.category.LAUNCHER") {
1958 isLauncherActivity = true;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001959 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
1960 isLeanbackLauncherActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001961 } else if (category == "android.intent.category.HOME") {
1962 catLauncher = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001963 }
1964 }
1965 }
1966 }
1967 }
1968
1969 // Pre-1.6 implicitly granted permission compatibility logic
1970 if (targetSdk < 4) {
1971 if (!hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001972 printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
1973 printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
1974 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001975 hasWriteExternalStoragePermission = true;
1976 }
1977 if (!hasReadPhoneStatePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001978 printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
1979 printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
1980 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001981 }
1982 }
1983
1984 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1985 // force them to always take READ_EXTERNAL_STORAGE as well. We always
1986 // do this (regardless of target API version) because we can't have
1987 // an app with write permission but not read permission.
1988 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001989 printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"));
1990 printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
1991 String8("requested WRITE_EXTERNAL_STORAGE"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001992 }
1993
1994 // Pre-JellyBean call log permission compatibility.
1995 if (targetSdk < 16) {
1996 if (!hasReadCallLogPermission && hasReadContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001997 printUsesPermission(String8("android.permission.READ_CALL_LOG"));
1998 printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
1999 String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002000 }
2001 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002002 printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
2003 printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
2004 String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002005 }
2006 }
2007
Adam Lesinski2c72b682014-06-24 09:56:01 -07002008 addImpliedFeature(&impliedFeatures, "android.hardware.touchscreen",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002009 "default feature for all apps", false);
Adam Lesinski2c72b682014-06-24 09:56:01 -07002010
2011 const size_t numFeatureGroups = featureGroups.size();
2012 if (numFeatureGroups == 0) {
2013 // If no <feature-group> tags were defined, apply auto-implied features.
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002014 printDefaultFeatureGroup(commonFeatures, impliedFeatures);
Adam Lesinski2c72b682014-06-24 09:56:01 -07002015
2016 } else {
2017 // <feature-group> tags are defined, so we ignore implied features and
2018 for (size_t i = 0; i < numFeatureGroups; i++) {
2019 FeatureGroup& grp = featureGroups.editItemAt(i);
2020
Adam Lesinskid7a94da2014-07-25 14:38:54 -07002021 if (commonFeatures.openGLESVersion > grp.openGLESVersion) {
2022 grp.openGLESVersion = commonFeatures.openGLESVersion;
2023 }
2024
Adam Lesinski2c72b682014-06-24 09:56:01 -07002025 // Merge the features defined in the top level (not inside a <feature-group>)
2026 // with this feature group.
2027 const size_t numCommonFeatures = commonFeatures.features.size();
2028 for (size_t j = 0; j < numCommonFeatures; j++) {
2029 if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07002030 grp.features.add(commonFeatures.features.keyAt(j),
2031 commonFeatures.features[j]);
Adam Lesinski2c72b682014-06-24 09:56:01 -07002032 }
2033 }
2034
Adam Lesinski73a05112014-12-08 12:53:17 -08002035 if (!grp.features.isEmpty()) {
Adam Lesinski2c72b682014-06-24 09:56:01 -07002036 printFeatureGroup(grp);
Adam Lesinski282e1812014-01-23 18:17:42 -08002037 }
2038 }
2039 }
2040
Adam Lesinski282e1812014-01-23 18:17:42 -08002041
Adam Lesinski282e1812014-01-23 18:17:42 -08002042 if (hasWidgetReceivers) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002043 printComponentPresence("app-widget");
Adam Lesinski282e1812014-01-23 18:17:42 -08002044 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002045 if (hasDeviceAdminReceiver) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002046 printComponentPresence("device-admin");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002047 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002048 if (hasImeService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002049 printComponentPresence("ime");
Adam Lesinski282e1812014-01-23 18:17:42 -08002050 }
2051 if (hasWallpaperService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002052 printComponentPresence("wallpaper");
Adam Lesinski282e1812014-01-23 18:17:42 -08002053 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002054 if (hasAccessibilityService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002055 printComponentPresence("accessibility");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002056 }
2057 if (hasPrintService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002058 printComponentPresence("print-service");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002059 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07002060 if (hasPaymentService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002061 printComponentPresence("payment");
2062 }
2063 if (isSearchable) {
2064 printComponentPresence("search");
2065 }
2066 if (hasDocumentsProvider) {
2067 printComponentPresence("document-provider");
2068 }
2069 if (hasLauncher) {
2070 printComponentPresence("launcher");
2071 }
2072 if (hasNotificationListenerService) {
2073 printComponentPresence("notification-listener");
2074 }
John Spurlockeb8d1be2014-06-25 17:46:15 -04002075 if (hasDreamService) {
2076 printComponentPresence("dream");
2077 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002078 if (hasCameraActivity) {
2079 printComponentPresence("camera");
2080 }
2081 if (hasCameraSecureActivity) {
2082 printComponentPresence("camera-secure");
2083 }
2084
2085 if (hasMainActivity) {
2086 printf("main\n");
Adam Lesinski94fc9122013-09-30 17:16:09 -07002087 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002088 if (hasOtherActivities) {
2089 printf("other-activities\n");
2090 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002091 if (hasOtherReceivers) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002092 printf("other-receivers\n");
2093 }
2094 if (hasOtherServices) {
2095 printf("other-services\n");
2096 }
2097
2098 // For modern apps, if screen size buckets haven't been specified
2099 // but the new width ranges have, then infer the buckets from them.
2100 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
2101 && requiresSmallestWidthDp > 0) {
2102 int compatWidth = compatibleWidthLimitDp;
2103 if (compatWidth <= 0) {
2104 compatWidth = requiresSmallestWidthDp;
2105 }
2106 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
2107 smallScreen = -1;
2108 } else {
2109 smallScreen = 0;
2110 }
2111 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
2112 normalScreen = -1;
2113 } else {
2114 normalScreen = 0;
2115 }
2116 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
2117 largeScreen = -1;
2118 } else {
2119 largeScreen = 0;
2120 }
2121 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
2122 xlargeScreen = -1;
2123 } else {
2124 xlargeScreen = 0;
2125 }
2126 }
2127
2128 // Determine default values for any unspecified screen sizes,
2129 // based on the target SDK of the package. As of 4 (donut)
2130 // the screen size support was introduced, so all default to
2131 // enabled.
2132 if (smallScreen > 0) {
2133 smallScreen = targetSdk >= 4 ? -1 : 0;
2134 }
2135 if (normalScreen > 0) {
2136 normalScreen = -1;
2137 }
2138 if (largeScreen > 0) {
2139 largeScreen = targetSdk >= 4 ? -1 : 0;
2140 }
2141 if (xlargeScreen > 0) {
2142 // Introduced in Gingerbread.
2143 xlargeScreen = targetSdk >= 9 ? -1 : 0;
2144 }
2145 if (anyDensity > 0) {
2146 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
2147 || compatibleWidthLimitDp > 0) ? -1 : 0;
2148 }
2149 printf("supports-screens:");
2150 if (smallScreen != 0) {
2151 printf(" 'small'");
2152 }
2153 if (normalScreen != 0) {
2154 printf(" 'normal'");
2155 }
2156 if (largeScreen != 0) {
2157 printf(" 'large'");
2158 }
2159 if (xlargeScreen != 0) {
2160 printf(" 'xlarge'");
2161 }
2162 printf("\n");
2163 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
2164 if (requiresSmallestWidthDp > 0) {
2165 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
2166 }
2167 if (compatibleWidthLimitDp > 0) {
2168 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
2169 }
2170 if (largestWidthLimitDp > 0) {
2171 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
2172 }
2173
2174 printf("locales:");
2175 const size_t NL = locales.size();
2176 for (size_t i=0; i<NL; i++) {
2177 const char* localeStr = locales[i].string();
2178 if (localeStr == NULL || strlen(localeStr) == 0) {
2179 localeStr = "--_--";
2180 }
2181 printf(" '%s'", localeStr);
2182 }
2183 printf("\n");
2184
2185 printf("densities:");
2186 const size_t ND = densities.size();
2187 for (size_t i=0; i<ND; i++) {
2188 printf(" '%d'", densities[i]);
2189 }
2190 printf("\n");
2191
2192 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
2193 if (dir != NULL) {
2194 if (dir->getFileCount() > 0) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002195 SortedVector<String8> architectures;
Adam Lesinski282e1812014-01-23 18:17:42 -08002196 for (size_t i=0; i<dir->getFileCount(); i++) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002197 architectures.add(ResTable::normalizeForOutput(
2198 dir->getFileName(i).string()));
Adam Lesinski282e1812014-01-23 18:17:42 -08002199 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07002200
2201 bool outputAltNativeCode = false;
2202 // A multiArch package is one that contains 64-bit and
2203 // 32-bit versions of native code and expects 3rd-party
2204 // apps to load these native code libraries. Since most
2205 // 64-bit systems also support 32-bit apps, the apps
2206 // loading this multiArch package's code may be either
2207 // 32-bit or 64-bit.
2208 if (hasMultiArch) {
2209 // If this is a multiArch package, report the 64-bit
2210 // version only. Then as a separate entry, report the
2211 // rest.
2212 //
2213 // If we report the 32-bit architecture, this APK will
2214 // be installed on a 32-bit device, causing a large waste
2215 // of bandwidth and disk space. This assumes that
2216 // the developer of the multiArch package has also
2217 // made a version that is 32-bit only.
2218 String8 intel64("x86_64");
2219 String8 arm64("arm64-v8a");
2220 ssize_t index = architectures.indexOf(intel64);
2221 if (index < 0) {
2222 index = architectures.indexOf(arm64);
2223 }
2224
2225 if (index >= 0) {
2226 printf("native-code: '%s'\n", architectures[index].string());
2227 architectures.removeAt(index);
2228 outputAltNativeCode = true;
2229 }
2230 }
2231
2232 const size_t archCount = architectures.size();
2233 if (archCount > 0) {
2234 if (outputAltNativeCode) {
2235 printf("alt-");
2236 }
2237 printf("native-code:");
2238 for (size_t i = 0; i < archCount; i++) {
2239 printf(" '%s'", architectures[i].string());
2240 }
2241 printf("\n");
2242 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002243 }
2244 delete dir;
2245 }
2246 } else if (strcmp("badger", option) == 0) {
2247 printf("%s", CONSOLE_DATA);
2248 } else if (strcmp("configurations", option) == 0) {
2249 Vector<ResTable_config> configs;
2250 res.getConfigurations(&configs);
2251 const size_t N = configs.size();
2252 for (size_t i=0; i<N; i++) {
2253 printf("%s\n", configs[i].toString().string());
2254 }
2255 } else {
2256 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2257 goto bail;
2258 }
2259 }
2260
2261 result = NO_ERROR;
2262
2263bail:
2264 if (asset) {
2265 delete asset;
2266 }
2267 return (result != NO_ERROR);
2268}
2269
2270
2271/*
2272 * Handle the "add" command, which wants to add files to a new or
2273 * pre-existing archive.
2274 */
2275int doAdd(Bundle* bundle)
2276{
2277 ZipFile* zip = NULL;
2278 status_t result = UNKNOWN_ERROR;
2279 const char* zipFileName;
2280
2281 if (bundle->getUpdate()) {
2282 /* avoid confusion */
2283 fprintf(stderr, "ERROR: can't use '-u' with add\n");
2284 goto bail;
2285 }
2286
2287 if (bundle->getFileSpecCount() < 1) {
2288 fprintf(stderr, "ERROR: must specify zip file name\n");
2289 goto bail;
2290 }
2291 zipFileName = bundle->getFileSpecEntry(0);
2292
2293 if (bundle->getFileSpecCount() < 2) {
2294 fprintf(stderr, "NOTE: nothing to do\n");
2295 goto bail;
2296 }
2297
2298 zip = openReadWrite(zipFileName, true);
2299 if (zip == NULL) {
2300 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2301 goto bail;
2302 }
2303
2304 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2305 const char* fileName = bundle->getFileSpecEntry(i);
2306
2307 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
2308 printf(" '%s'... (from gzip)\n", fileName);
2309 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
2310 } else {
2311 if (bundle->getJunkPath()) {
2312 String8 storageName = String8(fileName).getPathLeaf();
Maurice Chu2675f762013-10-22 17:33:11 -07002313 printf(" '%s' as '%s'...\n", fileName,
2314 ResTable::normalizeForOutput(storageName.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08002315 result = zip->add(fileName, storageName.string(),
2316 bundle->getCompressionMethod(), NULL);
2317 } else {
2318 printf(" '%s'...\n", fileName);
2319 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2320 }
2321 }
2322 if (result != NO_ERROR) {
2323 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2324 if (result == NAME_NOT_FOUND) {
2325 fprintf(stderr, ": file not found\n");
2326 } else if (result == ALREADY_EXISTS) {
2327 fprintf(stderr, ": already exists in archive\n");
2328 } else {
2329 fprintf(stderr, "\n");
2330 }
2331 goto bail;
2332 }
2333 }
2334
2335 result = NO_ERROR;
2336
2337bail:
2338 delete zip;
2339 return (result != NO_ERROR);
2340}
2341
2342
2343/*
2344 * Delete files from an existing archive.
2345 */
2346int doRemove(Bundle* bundle)
2347{
2348 ZipFile* zip = NULL;
2349 status_t result = UNKNOWN_ERROR;
2350 const char* zipFileName;
2351
2352 if (bundle->getFileSpecCount() < 1) {
2353 fprintf(stderr, "ERROR: must specify zip file name\n");
2354 goto bail;
2355 }
2356 zipFileName = bundle->getFileSpecEntry(0);
2357
2358 if (bundle->getFileSpecCount() < 2) {
2359 fprintf(stderr, "NOTE: nothing to do\n");
2360 goto bail;
2361 }
2362
2363 zip = openReadWrite(zipFileName, false);
2364 if (zip == NULL) {
2365 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2366 zipFileName);
2367 goto bail;
2368 }
2369
2370 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2371 const char* fileName = bundle->getFileSpecEntry(i);
2372 ZipEntry* entry;
2373
2374 entry = zip->getEntryByName(fileName);
2375 if (entry == NULL) {
2376 printf(" '%s' NOT FOUND\n", fileName);
2377 continue;
2378 }
2379
2380 result = zip->remove(entry);
2381
2382 if (result != NO_ERROR) {
2383 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2384 bundle->getFileSpecEntry(i), zipFileName);
2385 goto bail;
2386 }
2387 }
2388
2389 /* update the archive */
2390 zip->flush();
2391
2392bail:
2393 delete zip;
2394 return (result != NO_ERROR);
2395}
2396
Adam Lesinski3921e872014-05-13 10:56:25 -07002397static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002398 const size_t numDirs = dir->getDirs().size();
2399 for (size_t i = 0; i < numDirs; i++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002400 bool ignore = ignoreConfig;
2401 const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2402 const char* dirStr = subDir->getLeaf().string();
2403 if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2404 ignore = true;
2405 }
2406 status_t err = addResourcesToBuilder(subDir, builder, ignore);
Adam Lesinskifab50872014-04-16 14:40:42 -07002407 if (err != NO_ERROR) {
2408 return err;
2409 }
2410 }
2411
2412 const size_t numFiles = dir->getFiles().size();
2413 for (size_t i = 0; i < numFiles; i++) {
2414 sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2415 const size_t numConfigs = gp->getFiles().size();
2416 for (size_t j = 0; j < numConfigs; j++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002417 status_t err = NO_ERROR;
2418 if (ignoreConfig) {
2419 err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2420 } else {
2421 err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2422 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002423 if (err != NO_ERROR) {
2424 fprintf(stderr, "Failed to add %s (%s) to builder.\n",
2425 gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
2426 return err;
2427 }
2428 }
2429 }
2430 return NO_ERROR;
2431}
2432
2433static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2434 if (split->isBase()) {
2435 return original;
2436 }
2437
2438 String8 ext(original.getPathExtension());
2439 if (ext == String8(".apk")) {
2440 return String8::format("%s_%s%s",
2441 original.getBasePath().string(),
2442 split->getDirectorySafeName().string(),
2443 ext.string());
2444 }
2445
2446 return String8::format("%s_%s", original.string(),
2447 split->getDirectorySafeName().string());
2448}
Adam Lesinski282e1812014-01-23 18:17:42 -08002449
2450/*
2451 * Package up an asset directory and associated application files.
2452 */
2453int doPackage(Bundle* bundle)
2454{
2455 const char* outputAPKFile;
2456 int retVal = 1;
2457 status_t err;
2458 sp<AaptAssets> assets;
2459 int N;
2460 FILE* fp;
2461 String8 dependencyFile;
Adam Lesinskifab50872014-04-16 14:40:42 -07002462 sp<ApkBuilder> builder;
Adam Lesinski282e1812014-01-23 18:17:42 -08002463
Anton Krumina2ef5c02014-03-12 14:46:44 -07002464 // -c en_XA or/and ar_XB means do pseudolocalization
Adam Lesinskifab50872014-04-16 14:40:42 -07002465 sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2466 err = configFilter->parse(bundle->getConfigurations());
Adam Lesinski282e1812014-01-23 18:17:42 -08002467 if (err != NO_ERROR) {
2468 goto bail;
2469 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002470 if (configFilter->containsPseudo()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002471 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2472 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002473 if (configFilter->containsPseudoBidi()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002474 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
Adam Lesinski282e1812014-01-23 18:17:42 -08002475 }
2476
2477 N = bundle->getFileSpecCount();
2478 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
Adam Lesinski09384302014-01-22 16:07:42 -08002479 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002480 fprintf(stderr, "ERROR: no input files\n");
2481 goto bail;
2482 }
2483
2484 outputAPKFile = bundle->getOutputAPKFile();
2485
2486 // Make sure the filenames provided exist and are of the appropriate type.
2487 if (outputAPKFile) {
2488 FileType type;
2489 type = getFileType(outputAPKFile);
2490 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2491 fprintf(stderr,
2492 "ERROR: output file '%s' exists but is not regular file\n",
2493 outputAPKFile);
2494 goto bail;
2495 }
2496 }
2497
2498 // Load the assets.
2499 assets = new AaptAssets();
2500
2501 // Set up the resource gathering in assets if we're going to generate
2502 // dependency files. Every time we encounter a resource while slurping
2503 // the tree, we'll add it to these stores so we have full resource paths
2504 // to write to a dependency file.
2505 if (bundle->getGenDependencies()) {
2506 sp<FilePathStore> resPathStore = new FilePathStore;
2507 assets->setFullResPaths(resPathStore);
2508 sp<FilePathStore> assetPathStore = new FilePathStore;
2509 assets->setFullAssetPaths(assetPathStore);
2510 }
2511
2512 err = assets->slurpFromArgs(bundle);
2513 if (err < 0) {
2514 goto bail;
2515 }
2516
2517 if (bundle->getVerbose()) {
2518 assets->print(String8());
2519 }
2520
Adam Lesinskifab50872014-04-16 14:40:42 -07002521 // Create the ApkBuilder, which will collect the compiled files
2522 // to write to the final APK (or sets of APKs if we are building
2523 // a Split APK.
2524 builder = new ApkBuilder(configFilter);
2525
2526 // If we are generating a Split APK, find out which configurations to split on.
2527 if (bundle->getSplitConfigurations().size() > 0) {
2528 const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2529 const size_t numSplits = splitStrs.size();
2530 for (size_t i = 0; i < numSplits; i++) {
2531 std::set<ConfigDescription> configs;
2532 if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2533 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2534 goto bail;
2535 }
2536
2537 err = builder->createSplitForConfigs(configs);
2538 if (err != NO_ERROR) {
2539 goto bail;
2540 }
2541 }
2542 }
2543
Adam Lesinski282e1812014-01-23 18:17:42 -08002544 // If they asked for any fileAs that need to be compiled, do so.
2545 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002546 err = buildResources(bundle, assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002547 if (err != 0) {
2548 goto bail;
2549 }
2550 }
2551
2552 // At this point we've read everything and processed everything. From here
2553 // on out it's just writing output files.
2554 if (SourcePos::hasErrors()) {
2555 goto bail;
2556 }
2557
2558 // Update symbols with information about which ones are needed as Java symbols.
2559 assets->applyJavaSymbols();
2560 if (SourcePos::hasErrors()) {
2561 goto bail;
2562 }
2563
2564 // If we've been asked to generate a dependency file, do that here
2565 if (bundle->getGenDependencies()) {
2566 // If this is the packaging step, generate the dependency file next to
2567 // the output apk (e.g. bin/resources.ap_.d)
2568 if (outputAPKFile) {
2569 dependencyFile = String8(outputAPKFile);
2570 // Add the .d extension to the dependency file.
2571 dependencyFile.append(".d");
2572 } else {
2573 // Else if this is the R.java dependency generation step,
2574 // generate the dependency file in the R.java package subdirectory
2575 // e.g. gen/com/foo/app/R.java.d
2576 dependencyFile = String8(bundle->getRClassDir());
2577 dependencyFile.appendPath("R.java.d");
2578 }
2579 // Make sure we have a clean dependency file to start with
2580 fp = fopen(dependencyFile, "w");
2581 fclose(fp);
2582 }
2583
2584 // Write out R.java constants
2585 if (!assets->havePrivateSymbols()) {
2586 if (bundle->getCustomPackage() == NULL) {
2587 // Write the R.java file into the appropriate class directory
2588 // e.g. gen/com/foo/app/R.java
Adam Lesinski1e4663852014-08-15 14:47:28 -07002589 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002590 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002591 } else {
2592 const String8 customPkg(bundle->getCustomPackage());
Adam Lesinski1e4663852014-08-15 14:47:28 -07002593 err = writeResourceSymbols(bundle, assets, customPkg, true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002594 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002595 }
2596 if (err < 0) {
2597 goto bail;
2598 }
2599 // If we have library files, we're going to write our R.java file into
2600 // the appropriate class directory for those libraries as well.
2601 // e.g. gen/com/foo/app/lib/R.java
2602 if (bundle->getExtraPackages() != NULL) {
2603 // Split on colon
2604 String8 libs(bundle->getExtraPackages());
2605 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2606 while (packageString != NULL) {
2607 // Write the R.java file out with the correct package name
Marcin Kosiba0f3a5a62014-09-11 13:48:48 +01002608 err = writeResourceSymbols(bundle, assets, String8(packageString), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002609 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002610 if (err < 0) {
2611 goto bail;
2612 }
2613 packageString = strtok(NULL, ":");
2614 }
2615 libs.unlockBuffer();
2616 }
2617 } else {
Adam Lesinski1e4663852014-08-15 14:47:28 -07002618 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002619 if (err < 0) {
2620 goto bail;
2621 }
Adam Lesinski1e4663852014-08-15 14:47:28 -07002622 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002623 if (err < 0) {
2624 goto bail;
2625 }
2626 }
2627
2628 // Write out the ProGuard file
2629 err = writeProguardFile(bundle, assets);
2630 if (err < 0) {
2631 goto bail;
2632 }
2633
Rohit Agrawal86229cb2016-04-21 16:29:58 -07002634 // Write out the Main Dex ProGuard file
2635 err = writeMainDexProguardFile(bundle, assets);
2636 if (err < 0) {
2637 goto bail;
2638 }
2639
Adam Lesinski282e1812014-01-23 18:17:42 -08002640 // Write the apk
2641 if (outputAPKFile) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002642 // Gather all resources and add them to the APK Builder. The builder will then
2643 // figure out which Split they belong in.
2644 err = addResourcesToBuilder(assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002645 if (err != NO_ERROR) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002646 goto bail;
2647 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002648
2649 const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2650 const size_t numSplits = splits.size();
2651 for (size_t i = 0; i < numSplits; i++) {
2652 const sp<ApkSplit>& split = splits[i];
2653 String8 outputPath = buildApkName(String8(outputAPKFile), split);
2654 err = writeAPK(bundle, outputPath, split);
2655 if (err != NO_ERROR) {
2656 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2657 goto bail;
2658 }
2659 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002660 }
2661
2662 // If we've been asked to generate a dependency file, we need to finish up here.
2663 // the writeResourceSymbols and writeAPK functions have already written the target
2664 // half of the dependency file, now we need to write the prerequisites. (files that
2665 // the R.java file or .ap_ file depend on)
2666 if (bundle->getGenDependencies()) {
2667 // Now that writeResourceSymbols or writeAPK has taken care of writing
2668 // the targets to our dependency file, we'll write the prereqs
2669 fp = fopen(dependencyFile, "a+");
2670 fprintf(fp, " : ");
2671 bool includeRaw = (outputAPKFile != NULL);
2672 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2673 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2674 // and therefore was not added to our pathstores during slurping
2675 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2676 fclose(fp);
2677 }
2678
2679 retVal = 0;
2680bail:
2681 if (SourcePos::hasErrors()) {
2682 SourcePos::printErrors(stderr);
2683 }
2684 return retVal;
2685}
2686
2687/*
2688 * Do PNG Crunching
2689 * PRECONDITIONS
2690 * -S flag points to a source directory containing drawable* folders
2691 * -C flag points to destination directory. The folder structure in the
2692 * source directory will be mirrored to the destination (cache) directory
2693 *
2694 * POSTCONDITIONS
2695 * Destination directory will be updated to match the PNG files in
Maurice Chu2675f762013-10-22 17:33:11 -07002696 * the source directory.
Adam Lesinski282e1812014-01-23 18:17:42 -08002697 */
2698int doCrunch(Bundle* bundle)
2699{
2700 fprintf(stdout, "Crunching PNG Files in ");
2701 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2702 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2703
2704 updatePreProcessedCache(bundle);
2705
2706 return NO_ERROR;
2707}
2708
2709/*
2710 * Do PNG Crunching on a single flag
2711 * -i points to a single png file
2712 * -o points to a single png output file
2713 */
2714int doSingleCrunch(Bundle* bundle)
2715{
2716 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2717 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2718
2719 String8 input(bundle->getSingleCrunchInputFile());
2720 String8 output(bundle->getSingleCrunchOutputFile());
2721
2722 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2723 // we can't return the status_t as it gets truncate to the lower 8 bits.
2724 return 42;
2725 }
2726
2727 return NO_ERROR;
2728}
2729
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002730int runInDaemonMode(Bundle* bundle) {
2731 std::cout << "Ready" << std::endl;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002732 for (std::string cmd; std::getline(std::cin, cmd);) {
2733 if (cmd == "quit") {
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002734 return NO_ERROR;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002735 } else if (cmd == "s") {
2736 // Two argument crunch
2737 std::string inputFile, outputFile;
2738 std::getline(std::cin, inputFile);
2739 std::getline(std::cin, outputFile);
2740 bundle->setSingleCrunchInputFile(inputFile.c_str());
2741 bundle->setSingleCrunchOutputFile(outputFile.c_str());
2742 std::cout << "Crunching " << inputFile << std::endl;
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002743 if (doSingleCrunch(bundle) != NO_ERROR) {
2744 std::cout << "Error" << std::endl;
2745 }
2746 std::cout << "Done" << std::endl;
2747 } else {
2748 // in case of invalid command, just bail out.
2749 std::cerr << "Unknown command" << std::endl;
2750 return -1;
2751 }
2752 }
2753 return -1;
2754}
2755
Adam Lesinski282e1812014-01-23 18:17:42 -08002756char CONSOLE_DATA[2925] = {
2757 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2758 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2759 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2760 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2761 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2762 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2763 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2764 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2765 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2766 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2767 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2768 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2769 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2770 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2771 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2772 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2773 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2774 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2775 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2776 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2777 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2778 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2779 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2780 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2781 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2782 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2783 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2784 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2785 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2786 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2787 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2788 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2789 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2790 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2791 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2792 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2793 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2794 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2795 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2796 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2797 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2798 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2799 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2800 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2801 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2802 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2803 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2804 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2805 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2806 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2807 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2808 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2809 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2810 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2811 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2812 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2813 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2814 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2815 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2816 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2817 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2818 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2819 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2820 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2821 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2822 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2823 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2824 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2825 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2826 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2827 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2828 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2829 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2830 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2831 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2832 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2833 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2834 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2835 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2836 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2837 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2838 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2839 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2840 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2841 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2842 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2843 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2844 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2845 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2846 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2847 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2848 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2849 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2850 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2851 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2852 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2853 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2854 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2855 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2856 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2857 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 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, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2860 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2861 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2862 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2863 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2864 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2865 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2866 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2867 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2868 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2869 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2870 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2871 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2872 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2873 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2874 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2875 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2876 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2877 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2878 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2879 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2880 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2881 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2882 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2883 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2884 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2885 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2886 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2887 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2888 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2889 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2890 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2891 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2892 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2893 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2894 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2895 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2896 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2897 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2898 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2899 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2900 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2901 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2902 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2903 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2904 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2905 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2906 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2907 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2908 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2909 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2910 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2911 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2912 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2913 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2914 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2915 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2916 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2917 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2918 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2919 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2920 };