blob: 42b752d98baa93bb9e8641e462e47956b90b6aed [file] [log] [blame]
Adam Lesinski282e1812014-01-23 18:17:42 -08001//
2// Copyright 2006 The Android Open Source Project
3//
4// Android Asset Packaging Tool main entry point.
5//
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07006#include "AaptXml.h"
Adam Lesinskifab50872014-04-16 14:40:42 -07007#include "ApkBuilder.h"
Adam Lesinski282e1812014-01-23 18:17:42 -08008#include "Bundle.h"
Adam Lesinski2c72b682014-06-24 09:56:01 -07009#include "Images.h"
10#include "Main.h"
Adam Lesinski282e1812014-01-23 18:17:42 -080011#include "ResourceFilter.h"
12#include "ResourceTable.h"
Adam Lesinski282e1812014-01-23 18:17:42 -080013#include "XMLNode.h"
14
Adam Lesinski282e1812014-01-23 18:17:42 -080015#include <utils/Errors.h>
Adam Lesinski2c72b682014-06-24 09:56:01 -070016#include <utils/KeyedVector.h>
17#include <utils/List.h>
18#include <utils/Log.h>
19#include <utils/SortedVector.h>
20#include <utils/threads.h>
21#include <utils/Vector.h>
Adam Lesinski282e1812014-01-23 18:17:42 -080022
Adam Lesinski282e1812014-01-23 18:17:42 -080023#include <errno.h>
Adam Lesinski2c72b682014-06-24 09:56:01 -070024#include <fcntl.h>
Adam Lesinski282e1812014-01-23 18:17:42 -080025
Jerome Dochez6f1280c2014-09-26 10:21:21 -070026#include <iostream>
27#include <string>
28#include <sstream>
29
Adam Lesinski282e1812014-01-23 18:17:42 -080030using namespace android;
31
Adam Lesinskiad751222014-08-18 14:06:38 -070032#ifndef AAPT_VERSION
33 #define AAPT_VERSION ""
34#endif
35
Adam Lesinski282e1812014-01-23 18:17:42 -080036/*
37 * Show version info. All the cool kids do it.
38 */
39int doVersion(Bundle* bundle)
40{
41 if (bundle->getFileSpecCount() != 0) {
42 printf("(ignoring extra arguments)\n");
43 }
Adam Lesinskiad751222014-08-18 14:06:38 -070044 printf("Android Asset Packaging Tool, v0.2-" AAPT_VERSION "\n");
Adam Lesinski282e1812014-01-23 18:17:42 -080045
46 return 0;
47}
48
49
50/*
51 * Open the file read only. The call fails if the file doesn't exist.
52 *
53 * Returns NULL on failure.
54 */
55ZipFile* openReadOnly(const char* fileName)
56{
57 ZipFile* zip;
58 status_t result;
59
60 zip = new ZipFile;
61 result = zip->open(fileName, ZipFile::kOpenReadOnly);
62 if (result != NO_ERROR) {
63 if (result == NAME_NOT_FOUND) {
64 fprintf(stderr, "ERROR: '%s' not found\n", fileName);
65 } else if (result == PERMISSION_DENIED) {
66 fprintf(stderr, "ERROR: '%s' access denied\n", fileName);
67 } else {
68 fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n",
69 fileName);
70 }
71 delete zip;
72 return NULL;
73 }
74
75 return zip;
76}
77
78/*
79 * Open the file read-write. The file will be created if it doesn't
80 * already exist and "okayToCreate" is set.
81 *
82 * Returns NULL on failure.
83 */
84ZipFile* openReadWrite(const char* fileName, bool okayToCreate)
85{
86 ZipFile* zip = NULL;
87 status_t result;
88 int flags;
89
90 flags = ZipFile::kOpenReadWrite;
91 if (okayToCreate) {
92 flags |= ZipFile::kOpenCreate;
93 }
94
95 zip = new ZipFile;
96 result = zip->open(fileName, flags);
97 if (result != NO_ERROR) {
98 delete zip;
99 zip = NULL;
100 goto bail;
101 }
102
103bail:
104 return zip;
105}
106
107
108/*
109 * Return a short string describing the compression method.
110 */
111const char* compressionName(int method)
112{
113 if (method == ZipEntry::kCompressStored) {
114 return "Stored";
115 } else if (method == ZipEntry::kCompressDeflated) {
116 return "Deflated";
117 } else {
118 return "Unknown";
119 }
120}
121
122/*
123 * Return the percent reduction in size (0% == no compression).
124 */
125int calcPercent(long uncompressedLen, long compressedLen)
126{
127 if (!uncompressedLen) {
128 return 0;
129 } else {
130 return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5);
131 }
132}
133
134/*
135 * Handle the "list" command, which can be a simple file dump or
136 * a verbose listing.
137 *
138 * The verbose listing closely matches the output of the Info-ZIP "unzip"
139 * command.
140 */
141int doList(Bundle* bundle)
142{
143 int result = 1;
144 ZipFile* zip = NULL;
145 const ZipEntry* entry;
146 long totalUncLen, totalCompLen;
147 const char* zipFileName;
148
149 if (bundle->getFileSpecCount() != 1) {
150 fprintf(stderr, "ERROR: specify zip file name (only)\n");
151 goto bail;
152 }
153 zipFileName = bundle->getFileSpecEntry(0);
154
155 zip = openReadOnly(zipFileName);
156 if (zip == NULL) {
157 goto bail;
158 }
159
160 int count, i;
161
162 if (bundle->getVerbose()) {
163 printf("Archive: %s\n", zipFileName);
164 printf(
165 " Length Method Size Ratio Offset Date Time CRC-32 Name\n");
166 printf(
167 "-------- ------ ------- ----- ------- ---- ---- ------ ----\n");
168 }
169
170 totalUncLen = totalCompLen = 0;
171
172 count = zip->getNumEntries();
173 for (i = 0; i < count; i++) {
174 entry = zip->getEntryByIndex(i);
175 if (bundle->getVerbose()) {
176 char dateBuf[32];
177 time_t when;
178
179 when = entry->getModWhen();
180 strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M",
181 localtime(&when));
182
183 printf("%8ld %-7.7s %7ld %3d%% %8zd %s %08lx %s\n",
184 (long) entry->getUncompressedLen(),
185 compressionName(entry->getCompressionMethod()),
186 (long) entry->getCompressedLen(),
187 calcPercent(entry->getUncompressedLen(),
188 entry->getCompressedLen()),
189 (size_t) entry->getLFHOffset(),
190 dateBuf,
191 entry->getCRC32(),
192 entry->getFileName());
193 } else {
194 printf("%s\n", entry->getFileName());
195 }
196
197 totalUncLen += entry->getUncompressedLen();
198 totalCompLen += entry->getCompressedLen();
199 }
200
201 if (bundle->getVerbose()) {
202 printf(
203 "-------- ------- --- -------\n");
204 printf("%8ld %7ld %2d%% %d files\n",
205 totalUncLen,
206 totalCompLen,
207 calcPercent(totalUncLen, totalCompLen),
208 zip->getNumEntries());
209 }
210
211 if (bundle->getAndroidList()) {
212 AssetManager assets;
213 if (!assets.addAssetPath(String8(zipFileName), NULL)) {
214 fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n");
215 goto bail;
216 }
217
Elliott Hughesba3fe562015-08-12 14:49:53 -0700218#ifdef __ANDROID__
Andreas Gampeb8dc7bc2014-10-01 19:07:51 -0700219 static const bool kHaveAndroidOs = true;
220#else
221 static const bool kHaveAndroidOs = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800222#endif
Andreas Gampeb8dc7bc2014-10-01 19:07:51 -0700223 const ResTable& res = assets.getResources(false);
224 if (!kHaveAndroidOs) {
225 printf("\nResource table:\n");
226 res.print(false);
227 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800228
229 Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
230 Asset::ACCESS_BUFFER);
231 if (manifestAsset == NULL) {
232 printf("\nNo AndroidManifest.xml found.\n");
233 } else {
234 printf("\nAndroid manifest:\n");
235 ResXMLTree tree;
236 tree.setTo(manifestAsset->getBuffer(true),
237 manifestAsset->getLength());
238 printXMLBlock(&tree);
239 }
240 delete manifestAsset;
241 }
242
243 result = 0;
244
245bail:
246 delete zip;
247 return result;
248}
249
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700250static void printResolvedResourceAttribute(const ResTable& resTable, const ResXMLTree& tree,
Chih-Hung Hsieh8bd37ba2016-08-10 14:15:30 -0700251 uint32_t attrRes, const String8& attrLabel, String8* outError)
Maurice Chu76327312013-10-16 18:28:46 -0700252{
253 Res_value value;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700254 AaptXml::getResolvedResourceAttribute(resTable, tree, attrRes, &value, outError);
Maurice Chu76327312013-10-16 18:28:46 -0700255 if (*outError != "") {
256 *outError = "error print resolved resource attribute";
257 return;
258 }
259 if (value.dataType == Res_value::TYPE_STRING) {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700260 String8 result = AaptXml::getResolvedAttribute(resTable, tree, attrRes, outError);
Maurice Chu2675f762013-10-22 17:33:11 -0700261 printf("%s='%s'", attrLabel.string(),
262 ResTable::normalizeForOutput(result.string()).string());
Maurice Chu76327312013-10-16 18:28:46 -0700263 } else if (Res_value::TYPE_FIRST_INT <= value.dataType &&
264 value.dataType <= Res_value::TYPE_LAST_INT) {
265 printf("%s='%d'", attrLabel.string(), value.data);
266 } else {
267 printf("%s='0x%x'", attrLabel.string(), (int)value.data);
268 }
269}
270
Adam Lesinski282e1812014-01-23 18:17:42 -0800271// These are attribute resource constants for the platform, as found
272// in android.R.attr
273enum {
274 LABEL_ATTR = 0x01010001,
275 ICON_ATTR = 0x01010002,
276 NAME_ATTR = 0x01010003,
Adam Lesinskia5018c92013-09-30 16:23:15 -0700277 PERMISSION_ATTR = 0x01010006,
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700278 EXPORTED_ATTR = 0x01010010,
279 GRANT_URI_PERMISSIONS_ATTR = 0x0101001b,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700280 RESOURCE_ATTR = 0x01010025,
Adam Lesinski282e1812014-01-23 18:17:42 -0800281 DEBUGGABLE_ATTR = 0x0101000f,
282 VALUE_ATTR = 0x01010024,
283 VERSION_CODE_ATTR = 0x0101021b,
284 VERSION_NAME_ATTR = 0x0101021c,
285 SCREEN_ORIENTATION_ATTR = 0x0101001e,
286 MIN_SDK_VERSION_ATTR = 0x0101020c,
287 MAX_SDK_VERSION_ATTR = 0x01010271,
288 REQ_TOUCH_SCREEN_ATTR = 0x01010227,
289 REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
290 REQ_HARD_KEYBOARD_ATTR = 0x01010229,
291 REQ_NAVIGATION_ATTR = 0x0101022a,
292 REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
293 TARGET_SDK_VERSION_ATTR = 0x01010270,
294 TEST_ONLY_ATTR = 0x01010272,
295 ANY_DENSITY_ATTR = 0x0101026c,
296 GL_ES_VERSION_ATTR = 0x01010281,
297 SMALL_SCREEN_ATTR = 0x01010284,
298 NORMAL_SCREEN_ATTR = 0x01010285,
299 LARGE_SCREEN_ATTR = 0x01010286,
300 XLARGE_SCREEN_ATTR = 0x010102bf,
301 REQUIRED_ATTR = 0x0101028e,
Adam Lesinskicaf797c2014-08-22 12:56:26 -0700302 INSTALL_LOCATION_ATTR = 0x010102b7,
Adam Lesinski282e1812014-01-23 18:17:42 -0800303 SCREEN_SIZE_ATTR = 0x010102ca,
304 SCREEN_DENSITY_ATTR = 0x010102cb,
305 REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
306 COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
307 LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
308 PUBLIC_KEY_ATTR = 0x010103a6,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700309 CATEGORY_ATTR = 0x010103e8,
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800310 BANNER_ATTR = 0x10103f2,
Tim Kilbournd9b1cad2014-10-24 12:43:41 -0700311 ISGAME_ATTR = 0x10103f4,
Adam Lesinski282e1812014-01-23 18:17:42 -0800312};
313
Maurice Chu2675f762013-10-22 17:33:11 -0700314String8 getComponentName(String8 &pkgName, String8 &componentName) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800315 ssize_t idx = componentName.find(".");
316 String8 retStr(pkgName);
317 if (idx == 0) {
318 retStr += componentName;
319 } else if (idx < 0) {
320 retStr += ".";
321 retStr += componentName;
322 } else {
Maurice Chu2675f762013-10-22 17:33:11 -0700323 return componentName;
Adam Lesinski282e1812014-01-23 18:17:42 -0800324 }
Maurice Chu2675f762013-10-22 17:33:11 -0700325 return retStr;
Adam Lesinski282e1812014-01-23 18:17:42 -0800326}
327
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700328static void printCompatibleScreens(ResXMLTree& tree, String8* outError) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800329 size_t len;
330 ResXMLTree::event_code_t code;
331 int depth = 0;
332 bool first = true;
333 printf("compatible-screens:");
334 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
335 if (code == ResXMLTree::END_TAG) {
336 depth--;
337 if (depth < 0) {
338 break;
339 }
340 continue;
341 }
342 if (code != ResXMLTree::START_TAG) {
343 continue;
344 }
345 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700346 const char16_t* ctag16 = tree.getElementName(&len);
347 if (ctag16 == NULL) {
348 *outError = "failed to get XML element name (bad string pool)";
349 return;
350 }
351 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800352 if (tag == "screen") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700353 int32_t screenSize = AaptXml::getIntegerAttribute(tree,
354 SCREEN_SIZE_ATTR);
355 int32_t screenDensity = AaptXml::getIntegerAttribute(tree,
356 SCREEN_DENSITY_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -0800357 if (screenSize > 0 && screenDensity > 0) {
358 if (!first) {
359 printf(",");
360 }
361 first = false;
362 printf("'%d/%d'", screenSize, screenDensity);
363 }
364 }
365 }
366 printf("\n");
367}
368
Adam Lesinski58f1f362013-11-12 12:59:08 -0800369static void printUsesPermission(const String8& name, bool optional=false, int maxSdkVersion=-1) {
370 printf("uses-permission: name='%s'", ResTable::normalizeForOutput(name.string()).string());
371 if (maxSdkVersion != -1) {
372 printf(" maxSdkVersion='%d'", maxSdkVersion);
373 }
374 printf("\n");
375
376 if (optional) {
377 printf("optional-permission: name='%s'",
378 ResTable::normalizeForOutput(name.string()).string());
379 if (maxSdkVersion != -1) {
380 printf(" maxSdkVersion='%d'", maxSdkVersion);
381 }
382 printf("\n");
383 }
384}
385
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800386static void printUsesPermissionSdk23(const String8& name, int maxSdkVersion=-1) {
387 printf("uses-permission-sdk-23: ");
388
389 printf("name='%s'", ResTable::normalizeForOutput(name.string()).string());
390 if (maxSdkVersion != -1) {
391 printf(" maxSdkVersion='%d'", maxSdkVersion);
392 }
393 printf("\n");
394}
395
Adam Lesinski58f1f362013-11-12 12:59:08 -0800396static void printUsesImpliedPermission(const String8& name, const String8& reason) {
397 printf("uses-implied-permission: name='%s' reason='%s'\n",
398 ResTable::normalizeForOutput(name.string()).string(),
399 ResTable::normalizeForOutput(reason.string()).string());
400}
401
Chih-Hung Hsieh8bd37ba2016-08-10 14:15:30 -0700402Vector<String8> getNfcAidCategories(AssetManager& assets, const String8& xmlPath, bool offHost,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700403 String8 *outError = NULL)
404{
405 Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER);
406 if (aidAsset == NULL) {
407 if (outError != NULL) *outError = "xml resource does not exist";
408 return Vector<String8>();
409 }
410
411 const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service");
412
413 bool withinApduService = false;
414 Vector<String8> categories;
415
416 String8 error;
417 ResXMLTree tree;
418 tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength());
419
420 size_t len;
421 int depth = 0;
422 ResXMLTree::event_code_t code;
423 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
424 if (code == ResXMLTree::END_TAG) {
425 depth--;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700426 const char16_t* ctag16 = tree.getElementName(&len);
427 if (ctag16 == NULL) {
428 *outError = "failed to get XML element name (bad string pool)";
429 return Vector<String8>();
430 }
431 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700432
433 if (depth == 0 && tag == serviceTagName) {
434 withinApduService = false;
435 }
436
437 } else if (code == ResXMLTree::START_TAG) {
438 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700439 const char16_t* ctag16 = tree.getElementName(&len);
440 if (ctag16 == NULL) {
441 *outError = "failed to get XML element name (bad string pool)";
442 return Vector<String8>();
443 }
444 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700445
446 if (depth == 1) {
447 if (tag == serviceTagName) {
448 withinApduService = true;
449 }
450 } else if (depth == 2 && withinApduService) {
451 if (tag == "aid-group") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700452 String8 category = AaptXml::getAttribute(tree, CATEGORY_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700453 if (error != "") {
454 if (outError != NULL) *outError = error;
455 return Vector<String8>();
456 }
457
458 categories.add(category);
459 }
460 }
461 }
462 }
463 aidAsset->close();
464 return categories;
465}
466
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700467static void printComponentPresence(const char* componentName) {
468 printf("provides-component:'%s'\n", componentName);
469}
470
Adam Lesinski2c72b682014-06-24 09:56:01 -0700471/**
472 * Represents a feature that has been automatically added due to
473 * a pre-requisite or some other reason.
474 */
475struct ImpliedFeature {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800476 ImpliedFeature() : impliedBySdk23(false) {}
477 ImpliedFeature(const String8& n, bool sdk23) : name(n), impliedBySdk23(sdk23) {}
478
Adam Lesinski2c72b682014-06-24 09:56:01 -0700479 /**
480 * Name of the implied feature.
481 */
482 String8 name;
483
484 /**
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800485 * Was this implied by a permission from SDK 23 (<uses-permission-sdk-23 />)?
486 */
487 bool impliedBySdk23;
488
489 /**
Adam Lesinski2c72b682014-06-24 09:56:01 -0700490 * List of human-readable reasons for why this feature was implied.
491 */
492 SortedVector<String8> reasons;
493};
494
Adam Lesinski694d0a72016-04-06 16:12:04 -0700495struct Feature {
496 Feature() : required(false), version(-1) {}
497 Feature(bool required, int32_t version = -1) : required(required), version(version) {}
498
499 /**
500 * Whether the feature is required.
501 */
502 bool required;
503
504 /**
505 * What version of the feature is requested.
506 */
507 int32_t version;
508};
509
Adam Lesinski2c72b682014-06-24 09:56:01 -0700510/**
511 * Represents a <feature-group> tag in the AndroidManifest.xml
512 */
513struct FeatureGroup {
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700514 FeatureGroup() : openGLESVersion(-1) {}
515
Adam Lesinski2c72b682014-06-24 09:56:01 -0700516 /**
517 * Human readable label
518 */
519 String8 label;
520
521 /**
522 * Explicit features defined in the group
523 */
Adam Lesinski694d0a72016-04-06 16:12:04 -0700524 KeyedVector<String8, Feature> features;
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700525
526 /**
527 * OpenGL ES version required
528 */
529 int openGLESVersion;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700530};
531
Adam Lesinskica955a42016-08-01 16:44:29 -0700532static bool hasFeature(const char* name, const FeatureGroup& grp,
533 const KeyedVector<String8, ImpliedFeature>& implied) {
534 String8 name8(name);
535 ssize_t idx = grp.features.indexOfKey(name8);
536 if (idx < 0) {
537 idx = implied.indexOfKey(name8);
538 }
539 return idx >= 0;
540}
541
Adam Lesinski2c72b682014-06-24 09:56:01 -0700542static void addImpliedFeature(KeyedVector<String8, ImpliedFeature>* impliedFeatures,
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800543 const char* name, const char* reason, bool sdk23) {
Adam Lesinski2c72b682014-06-24 09:56:01 -0700544 String8 name8(name);
545 ssize_t idx = impliedFeatures->indexOfKey(name8);
546 if (idx < 0) {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800547 idx = impliedFeatures->add(name8, ImpliedFeature(name8, sdk23));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700548 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800549
550 ImpliedFeature* feature = &impliedFeatures->editValueAt(idx);
551
552 // A non-sdk 23 implied feature takes precedence.
553 if (feature->impliedBySdk23 && !sdk23) {
554 feature->impliedBySdk23 = false;
555 }
556 feature->reasons.add(String8(reason));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700557}
558
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800559static void printFeatureGroupImpl(const FeatureGroup& grp,
560 const KeyedVector<String8, ImpliedFeature>* impliedFeatures) {
Adam Lesinski2c72b682014-06-24 09:56:01 -0700561 printf("feature-group: label='%s'\n", grp.label.string());
562
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700563 if (grp.openGLESVersion > 0) {
564 printf(" uses-gl-es: '0x%x'\n", grp.openGLESVersion);
565 }
566
Adam Lesinski2c72b682014-06-24 09:56:01 -0700567 const size_t numFeatures = grp.features.size();
568 for (size_t i = 0; i < numFeatures; i++) {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700569 const Feature& feature = grp.features[i];
570 const bool required = feature.required;
571 const int32_t version = feature.version;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700572
573 const String8& featureName = grp.features.keyAt(i);
Adam Lesinski694d0a72016-04-06 16:12:04 -0700574 printf(" uses-feature%s: name='%s'", (required ? "" : "-not-required"),
Adam Lesinski2c72b682014-06-24 09:56:01 -0700575 ResTable::normalizeForOutput(featureName.string()).string());
Adam Lesinski694d0a72016-04-06 16:12:04 -0700576
577 if (version > 0) {
578 printf(" version='%d'", version);
579 }
580 printf("\n");
Adam Lesinski2c72b682014-06-24 09:56:01 -0700581 }
582
583 const size_t numImpliedFeatures =
584 (impliedFeatures != NULL) ? impliedFeatures->size() : 0;
585 for (size_t i = 0; i < numImpliedFeatures; i++) {
586 const ImpliedFeature& impliedFeature = impliedFeatures->valueAt(i);
587 if (grp.features.indexOfKey(impliedFeature.name) >= 0) {
588 // The feature is explicitly set, no need to use implied
589 // definition.
590 continue;
591 }
592
593 String8 printableFeatureName(ResTable::normalizeForOutput(
594 impliedFeature.name.string()));
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800595 const char* sdk23Suffix = impliedFeature.impliedBySdk23 ? "-sdk-23" : "";
596
597 printf(" uses-feature%s: name='%s'\n", sdk23Suffix, printableFeatureName.string());
598 printf(" uses-implied-feature%s: name='%s' reason='", sdk23Suffix,
599 printableFeatureName.string());
Adam Lesinski2c72b682014-06-24 09:56:01 -0700600 const size_t numReasons = impliedFeature.reasons.size();
601 for (size_t j = 0; j < numReasons; j++) {
602 printf("%s", impliedFeature.reasons[j].string());
603 if (j + 2 < numReasons) {
604 printf(", ");
605 } else if (j + 1 < numReasons) {
606 printf(", and ");
607 }
608 }
609 printf("'\n");
610 }
611}
612
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800613static void printFeatureGroup(const FeatureGroup& grp) {
614 printFeatureGroupImpl(grp, NULL);
615}
616
617static void printDefaultFeatureGroup(const FeatureGroup& grp,
618 const KeyedVector<String8, ImpliedFeature>& impliedFeatures) {
619 printFeatureGroupImpl(grp, &impliedFeatures);
620}
621
Adam Lesinski2c72b682014-06-24 09:56:01 -0700622static void addParentFeatures(FeatureGroup* grp, const String8& name) {
623 if (name == "android.hardware.camera.autofocus" ||
624 name == "android.hardware.camera.flash") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700625 grp->features.add(String8("android.hardware.camera"), Feature(true));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700626 } else if (name == "android.hardware.location.gps" ||
627 name == "android.hardware.location.network") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700628 grp->features.add(String8("android.hardware.location"), Feature(true));
Adam Lesinskica955a42016-08-01 16:44:29 -0700629 } else if (name == "android.hardware.faketouch.multitouch") {
630 grp->features.add(String8("android.hardware.faketouch"), Feature(true));
631 } else if (name == "android.hardware.faketouch.multitouch.distinct" ||
632 name == "android.hardware.faketouch.multitouch.jazzhands") {
633 grp->features.add(String8("android.hardware.faketouch.multitouch"), Feature(true));
634 grp->features.add(String8("android.hardware.faketouch"), Feature(true));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700635 } else if (name == "android.hardware.touchscreen.multitouch") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700636 grp->features.add(String8("android.hardware.touchscreen"), Feature(true));
Adam Lesinskica955a42016-08-01 16:44:29 -0700637 } else if (name == "android.hardware.touchscreen.multitouch.distinct" ||
638 name == "android.hardware.touchscreen.multitouch.jazzhands") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700639 grp->features.add(String8("android.hardware.touchscreen.multitouch"), Feature(true));
640 grp->features.add(String8("android.hardware.touchscreen"), Feature(true));
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700641 } else if (name == "android.hardware.opengles.aep") {
642 const int openGLESVersion31 = 0x00030001;
643 if (openGLESVersion31 > grp->openGLESVersion) {
644 grp->openGLESVersion = openGLESVersion31;
645 }
Adam Lesinski2c72b682014-06-24 09:56:01 -0700646 }
647}
648
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800649static void addImpliedFeaturesForPermission(const int targetSdk, const String8& name,
650 KeyedVector<String8, ImpliedFeature>* impliedFeatures,
651 bool impliedBySdk23Permission) {
652 if (name == "android.permission.CAMERA") {
653 addImpliedFeature(impliedFeatures, "android.hardware.camera",
654 String8::format("requested %s permission", name.string())
655 .string(), impliedBySdk23Permission);
656 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
657 addImpliedFeature(impliedFeatures, "android.hardware.location.gps",
658 String8::format("requested %s permission", name.string())
659 .string(), impliedBySdk23Permission);
660 addImpliedFeature(impliedFeatures, "android.hardware.location",
661 String8::format("requested %s permission", name.string())
662 .string(), impliedBySdk23Permission);
663 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
664 addImpliedFeature(impliedFeatures, "android.hardware.location",
665 String8::format("requested %s permission", name.string())
666 .string(), impliedBySdk23Permission);
667 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
668 addImpliedFeature(impliedFeatures, "android.hardware.location.network",
669 String8::format("requested %s permission", name.string())
670 .string(), impliedBySdk23Permission);
671 addImpliedFeature(impliedFeatures, "android.hardware.location",
672 String8::format("requested %s permission", name.string())
673 .string(), impliedBySdk23Permission);
674 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
675 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
676 addImpliedFeature(impliedFeatures, "android.hardware.location",
677 String8::format("requested %s permission", name.string())
678 .string(), impliedBySdk23Permission);
679 } else if (name == "android.permission.BLUETOOTH" ||
680 name == "android.permission.BLUETOOTH_ADMIN") {
681 if (targetSdk > 4) {
682 addImpliedFeature(impliedFeatures, "android.hardware.bluetooth",
683 String8::format("requested %s permission", name.string())
684 .string(), impliedBySdk23Permission);
685 addImpliedFeature(impliedFeatures, "android.hardware.bluetooth",
686 "targetSdkVersion > 4", impliedBySdk23Permission);
687 }
688 } else if (name == "android.permission.RECORD_AUDIO") {
689 addImpliedFeature(impliedFeatures, "android.hardware.microphone",
690 String8::format("requested %s permission", name.string())
691 .string(), impliedBySdk23Permission);
692 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
693 name == "android.permission.CHANGE_WIFI_STATE" ||
694 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
695 addImpliedFeature(impliedFeatures, "android.hardware.wifi",
696 String8::format("requested %s permission", name.string())
697 .string(), impliedBySdk23Permission);
698 } else if (name == "android.permission.CALL_PHONE" ||
699 name == "android.permission.CALL_PRIVILEGED" ||
700 name == "android.permission.MODIFY_PHONE_STATE" ||
701 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
702 name == "android.permission.READ_SMS" ||
703 name == "android.permission.RECEIVE_SMS" ||
704 name == "android.permission.RECEIVE_MMS" ||
705 name == "android.permission.RECEIVE_WAP_PUSH" ||
706 name == "android.permission.SEND_SMS" ||
707 name == "android.permission.WRITE_APN_SETTINGS" ||
708 name == "android.permission.WRITE_SMS") {
709 addImpliedFeature(impliedFeatures, "android.hardware.telephony",
710 String8("requested a telephony permission").string(),
711 impliedBySdk23Permission);
712 }
713}
714
Adam Lesinski282e1812014-01-23 18:17:42 -0800715/*
716 * Handle the "dump" command, to extract select data from an archive.
717 */
718extern char CONSOLE_DATA[2925]; // see EOF
719int doDump(Bundle* bundle)
720{
721 status_t result = UNKNOWN_ERROR;
Adam Lesinski282e1812014-01-23 18:17:42 -0800722
723 if (bundle->getFileSpecCount() < 1) {
724 fprintf(stderr, "ERROR: no dump option specified\n");
725 return 1;
726 }
727
728 if (bundle->getFileSpecCount() < 2) {
729 fprintf(stderr, "ERROR: no dump file specified\n");
730 return 1;
731 }
732
733 const char* option = bundle->getFileSpecEntry(0);
734 const char* filename = bundle->getFileSpecEntry(1);
735
736 AssetManager assets;
Narayan Kamathf85e41f2014-01-24 14:14:30 +0000737 int32_t assetsCookie;
Adam Lesinski282e1812014-01-23 18:17:42 -0800738 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
739 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
740 return 1;
741 }
742
743 // Make a dummy config for retrieving resources... we need to supply
744 // non-default values for some configs so that we can retrieve resources
745 // in the app that don't have a default. The most important of these is
746 // the API version because key resources like icons will have an implicit
747 // version if they are using newer config types like density.
748 ResTable_config config;
Narayan Kamath91447d82014-01-21 15:32:36 +0000749 memset(&config, 0, sizeof(ResTable_config));
Adam Lesinski282e1812014-01-23 18:17:42 -0800750 config.language[0] = 'e';
751 config.language[1] = 'n';
752 config.country[0] = 'U';
753 config.country[1] = 'S';
754 config.orientation = ResTable_config::ORIENTATION_PORT;
755 config.density = ResTable_config::DENSITY_MEDIUM;
756 config.sdkVersion = 10000; // Very high.
757 config.screenWidthDp = 320;
758 config.screenHeightDp = 480;
759 config.smallestScreenWidthDp = 320;
Adam Lesinskic2dea8d2014-08-04 16:40:41 -0700760 config.screenLayout |= ResTable_config::SCREENSIZE_NORMAL;
Adam Lesinski282e1812014-01-23 18:17:42 -0800761 assets.setConfiguration(config);
762
763 const ResTable& res = assets.getResources(false);
Dan Albert68001652014-09-09 09:51:01 -0700764 if (res.getError() != NO_ERROR) {
Adam Lesinski25e9d552014-05-19 15:01:43 -0700765 fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
Adam Lesinski63e646e2014-07-30 11:40:39 -0700766 return 1;
Adam Lesinski282e1812014-01-23 18:17:42 -0800767 }
768
Adam Lesinski694d0a72016-04-06 16:12:04 -0700769 // Source for AndroidManifest.xml
770 const String8 manifestFile = String8::format("%s@AndroidManifest.xml", filename);
771
Adam Lesinski2cb761e2014-08-15 13:59:02 -0700772 // The dynamicRefTable can be null if there are no resources for this asset cookie.
773 // This fine.
Adam Lesinski63e646e2014-07-30 11:40:39 -0700774 const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700775
776 Asset* asset = NULL;
777
Adam Lesinski282e1812014-01-23 18:17:42 -0800778 if (strcmp("resources", option) == 0) {
Elliott Hughesba3fe562015-08-12 14:49:53 -0700779#ifndef __ANDROID__
Adam Lesinski282e1812014-01-23 18:17:42 -0800780 res.print(bundle->getValues());
781#endif
782
783 } else if (strcmp("strings", option) == 0) {
784 const ResStringPool* pool = res.getTableStringBlock(0);
785 printStringPool(pool);
786
787 } else if (strcmp("xmltree", option) == 0) {
788 if (bundle->getFileSpecCount() < 3) {
789 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
790 goto bail;
791 }
792
793 for (int i=2; i<bundle->getFileSpecCount(); i++) {
794 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700795 ResXMLTree tree(dynamicRefTable);
796 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800797 if (asset == NULL) {
798 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
799 goto bail;
800 }
801
802 if (tree.setTo(asset->getBuffer(true),
803 asset->getLength()) != NO_ERROR) {
804 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
805 goto bail;
806 }
807 tree.restart();
808 printXMLBlock(&tree);
809 tree.uninit();
810 delete asset;
811 asset = NULL;
812 }
813
814 } else if (strcmp("xmlstrings", option) == 0) {
815 if (bundle->getFileSpecCount() < 3) {
816 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
817 goto bail;
818 }
819
820 for (int i=2; i<bundle->getFileSpecCount(); i++) {
821 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700822 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800823 if (asset == NULL) {
824 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
825 goto bail;
826 }
827
Adam Lesinski63e646e2014-07-30 11:40:39 -0700828 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800829 if (tree.setTo(asset->getBuffer(true),
830 asset->getLength()) != NO_ERROR) {
831 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
832 goto bail;
833 }
834 printStringPool(&tree.getStrings());
835 delete asset;
836 asset = NULL;
837 }
838
839 } else {
Adam Lesinski63e646e2014-07-30 11:40:39 -0700840 asset = assets.openNonAsset(assetsCookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800841 if (asset == NULL) {
842 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
843 goto bail;
844 }
845
Adam Lesinski63e646e2014-07-30 11:40:39 -0700846 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800847 if (tree.setTo(asset->getBuffer(true),
848 asset->getLength()) != NO_ERROR) {
849 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
850 goto bail;
851 }
852 tree.restart();
853
854 if (strcmp("permissions", option) == 0) {
855 size_t len;
856 ResXMLTree::event_code_t code;
857 int depth = 0;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800858 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
859 code != ResXMLTree::BAD_DOCUMENT) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800860 if (code == ResXMLTree::END_TAG) {
861 depth--;
862 continue;
863 }
864 if (code != ResXMLTree::START_TAG) {
865 continue;
866 }
867 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700868 const char16_t* ctag16 = tree.getElementName(&len);
869 if (ctag16 == NULL) {
870 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
871 goto bail;
872 }
873 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800874 //printf("Depth %d tag %s\n", depth, tag.string());
875 if (depth == 1) {
876 if (tag != "manifest") {
877 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
878 goto bail;
879 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700880 String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -0700881 printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800882 } else if (depth == 2) {
883 if (tag == "permission") {
884 String8 error;
885 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
886 if (error != "") {
887 fprintf(stderr, "ERROR: %s\n", error.string());
888 goto bail;
889 }
890
891 if (name == "") {
892 fprintf(stderr, "ERROR: missing 'android:name' for permission\n");
893 goto bail;
894 }
895 printf("permission: %s\n",
896 ResTable::normalizeForOutput(name.string()).string());
897 } else if (tag == "uses-permission") {
898 String8 error;
899 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
900 if (error != "") {
901 fprintf(stderr, "ERROR: %s\n", error.string());
902 goto bail;
903 }
904
905 if (name == "") {
906 fprintf(stderr, "ERROR: missing 'android:name' for uses-permission\n");
907 goto bail;
908 }
909 printUsesPermission(name,
910 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
911 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
912 } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
913 String8 error;
914 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
915 if (error != "") {
916 fprintf(stderr, "ERROR: %s\n", error.string());
917 goto bail;
918 }
919
920 if (name == "") {
921 fprintf(stderr, "ERROR: missing 'android:name' for "
922 "uses-permission-sdk-23\n");
923 goto bail;
924 }
925 printUsesPermissionSdk23(
926 name,
927 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
Adam Lesinski282e1812014-01-23 18:17:42 -0800928 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800929 }
930 }
931 } else if (strcmp("badging", option) == 0) {
932 Vector<String8> locales;
933 res.getLocales(&locales);
934
935 Vector<ResTable_config> configs;
936 res.getConfigurations(&configs);
937 SortedVector<int> densities;
938 const size_t NC = configs.size();
939 for (size_t i=0; i<NC; i++) {
940 int dens = configs[i].density;
941 if (dens == 0) {
942 dens = 160;
943 }
944 densities.add(dens);
945 }
946
947 size_t len;
948 ResXMLTree::event_code_t code;
949 int depth = 0;
950 String8 error;
951 bool withinActivity = false;
952 bool isMainActivity = false;
953 bool isLauncherActivity = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800954 bool isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800955 bool isSearchable = false;
956 bool withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700957 bool withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700958 bool withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800959 bool withinReceiver = false;
960 bool withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700961 bool withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800962 bool withinIntentFilter = false;
963 bool hasMainActivity = false;
964 bool hasOtherActivities = false;
965 bool hasOtherReceivers = false;
966 bool hasOtherServices = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700967 bool hasIntentFilter = false;
968
Adam Lesinski282e1812014-01-23 18:17:42 -0800969 bool hasWallpaperService = false;
970 bool hasImeService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700971 bool hasAccessibilityService = false;
972 bool hasPrintService = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800973 bool hasWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700974 bool hasDeviceAdminReceiver = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700975 bool hasPaymentService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700976 bool hasDocumentsProvider = false;
977 bool hasCameraActivity = false;
978 bool hasCameraSecureActivity = false;
979 bool hasLauncher = false;
980 bool hasNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400981 bool hasDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700982
Adam Lesinski282e1812014-01-23 18:17:42 -0800983 bool actMainActivity = false;
984 bool actWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700985 bool actDeviceAdminEnabled = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800986 bool actImeService = false;
987 bool actWallpaperService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700988 bool actAccessibilityService = false;
989 bool actPrintService = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700990 bool actHostApduService = false;
991 bool actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700992 bool actDocumentsProvider = false;
993 bool actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400994 bool actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700995 bool actCamera = false;
996 bool actCameraSecure = false;
997 bool catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700998 bool hasMetaHostPaymentCategory = false;
999 bool hasMetaOffHostPaymentCategory = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001000
1001 // These permissions are required by services implementing services
1002 // the system binds to (IME, Accessibility, PrintServices, etc.)
1003 bool hasBindDeviceAdminPermission = false;
1004 bool hasBindInputMethodPermission = false;
1005 bool hasBindAccessibilityServicePermission = false;
1006 bool hasBindPrintServicePermission = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001007 bool hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001008 bool hasRequiredSafAttributes = false;
1009 bool hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001010 bool hasBindDreamServicePermission = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001011
1012 // These two implement the implicit permissions that are granted
1013 // to pre-1.6 applications.
1014 bool hasWriteExternalStoragePermission = false;
1015 bool hasReadPhoneStatePermission = false;
1016
1017 // If an app requests write storage, they will also get read storage.
1018 bool hasReadExternalStoragePermission = false;
1019
1020 // Implement transition to read and write call log.
1021 bool hasReadContactsPermission = false;
1022 bool hasWriteContactsPermission = false;
1023 bool hasReadCallLogPermission = false;
1024 bool hasWriteCallLogPermission = false;
1025
Adam Lesinskie47fd122014-08-15 22:25:36 -07001026 // If an app declares itself as multiArch, we report the
1027 // native libraries differently.
1028 bool hasMultiArch = false;
1029
Adam Lesinski282e1812014-01-23 18:17:42 -08001030 // This next group of variables is used to implement a group of
1031 // backward-compatibility heuristics necessitated by the addition of
1032 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
1033 // heuristic is "if an app requests a permission but doesn't explicitly
1034 // request the corresponding <uses-feature>, presume it's there anyway".
Adam Lesinski2c72b682014-06-24 09:56:01 -07001035
Adam Lesinski282e1812014-01-23 18:17:42 -08001036 // 2.2 also added some other features that apps can request, but that
1037 // have no corresponding permission, so we cannot implement any
1038 // back-compatibility heuristic for them. The below are thus unnecessary
1039 // (but are retained here for documentary purposes.)
1040 //bool specCompassFeature = false;
1041 //bool specAccelerometerFeature = false;
1042 //bool specProximityFeature = false;
1043 //bool specAmbientLightFeature = false;
1044 //bool specLiveWallpaperFeature = false;
1045
1046 int targetSdk = 0;
1047 int smallScreen = 1;
1048 int normalScreen = 1;
1049 int largeScreen = 1;
1050 int xlargeScreen = 1;
1051 int anyDensity = 1;
1052 int requiresSmallestWidthDp = 0;
1053 int compatibleWidthLimitDp = 0;
1054 int largestWidthLimitDp = 0;
1055 String8 pkg;
1056 String8 activityName;
1057 String8 activityLabel;
1058 String8 activityIcon;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001059 String8 activityBanner;
Adam Lesinski282e1812014-01-23 18:17:42 -08001060 String8 receiverName;
1061 String8 serviceName;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001062 Vector<String8> supportedInput;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001063
1064 FeatureGroup commonFeatures;
1065 Vector<FeatureGroup> featureGroups;
1066 KeyedVector<String8, ImpliedFeature> impliedFeatures;
1067
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001068 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
1069 code != ResXMLTree::BAD_DOCUMENT) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001070 if (code == ResXMLTree::END_TAG) {
1071 depth--;
1072 if (depth < 2) {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001073 if (withinSupportsInput && !supportedInput.isEmpty()) {
1074 printf("supports-input: '");
1075 const size_t N = supportedInput.size();
1076 for (size_t i=0; i<N; i++) {
Maurice Chu2675f762013-10-22 17:33:11 -07001077 printf("%s", ResTable::normalizeForOutput(
1078 supportedInput[i].string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001079 if (i != N - 1) {
1080 printf("' '");
1081 } else {
1082 printf("'\n");
1083 }
1084 }
1085 supportedInput.clear();
1086 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001087 withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001088 withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001089 withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001090 } else if (depth < 3) {
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001091 if (withinActivity && isMainActivity) {
Maurice Chu2675f762013-10-22 17:33:11 -07001092 String8 aName(getComponentName(pkg, activityName));
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001093 if (isLauncherActivity) {
1094 printf("launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001095 if (aName.length() > 0) {
1096 printf(" name='%s' ",
1097 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001098 }
1099 printf(" label='%s' icon='%s'\n",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001100 ResTable::normalizeForOutput(activityLabel.string())
1101 .string(),
1102 ResTable::normalizeForOutput(activityIcon.string())
1103 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001104 }
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001105 if (isLeanbackLauncherActivity) {
1106 printf("leanback-launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001107 if (aName.length() > 0) {
1108 printf(" name='%s' ",
1109 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001110 }
1111 printf(" label='%s' icon='%s' banner='%s'\n",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001112 ResTable::normalizeForOutput(activityLabel.string())
1113 .string(),
1114 ResTable::normalizeForOutput(activityIcon.string())
1115 .string(),
1116 ResTable::normalizeForOutput(activityBanner.string())
1117 .string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001118 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001119 }
1120 if (!hasIntentFilter) {
1121 hasOtherActivities |= withinActivity;
1122 hasOtherReceivers |= withinReceiver;
1123 hasOtherServices |= withinService;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001124 } else {
1125 if (withinService) {
1126 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
1127 hasBindNfcServicePermission);
1128 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
1129 hasBindNfcServicePermission);
1130 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001131 }
1132 withinActivity = false;
1133 withinService = false;
1134 withinReceiver = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001135 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001136 hasIntentFilter = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001137 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001138 } else if (depth < 4) {
1139 if (withinIntentFilter) {
1140 if (withinActivity) {
1141 hasMainActivity |= actMainActivity;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001142 hasLauncher |= catLauncher;
1143 hasCameraActivity |= actCamera;
1144 hasCameraSecureActivity |= actCameraSecure;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001145 hasOtherActivities |=
1146 !actMainActivity && !actCamera && !actCameraSecure;
Adam Lesinski282e1812014-01-23 18:17:42 -08001147 } else if (withinReceiver) {
1148 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001149 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
1150 hasBindDeviceAdminPermission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001151 hasOtherReceivers |=
1152 (!actWidgetReceivers && !actDeviceAdminEnabled);
Adam Lesinski282e1812014-01-23 18:17:42 -08001153 } else if (withinService) {
1154 hasImeService |= actImeService;
1155 hasWallpaperService |= actWallpaperService;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001156 hasAccessibilityService |= (actAccessibilityService &&
1157 hasBindAccessibilityServicePermission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001158 hasPrintService |=
1159 (actPrintService && hasBindPrintServicePermission);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001160 hasNotificationListenerService |= actNotificationListenerService &&
1161 hasBindNotificationListenerServicePermission;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001162 hasDreamService |= actDreamService && hasBindDreamServicePermission;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001163 hasOtherServices |= (!actImeService && !actWallpaperService &&
Adam Lesinski94fc9122013-09-30 17:16:09 -07001164 !actAccessibilityService && !actPrintService &&
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001165 !actHostApduService && !actOffHostApduService &&
1166 !actNotificationListenerService);
1167 } else if (withinProvider) {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001168 hasDocumentsProvider |=
1169 actDocumentsProvider && hasRequiredSafAttributes;
Adam Lesinski282e1812014-01-23 18:17:42 -08001170 }
1171 }
1172 withinIntentFilter = false;
1173 }
1174 continue;
1175 }
1176 if (code != ResXMLTree::START_TAG) {
1177 continue;
1178 }
1179 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001180
1181 const char16_t* ctag16 = tree.getElementName(&len);
1182 if (ctag16 == NULL) {
1183 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
1184 goto bail;
1185 }
1186 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -08001187 //printf("Depth %d, %s\n", depth, tag.string());
1188 if (depth == 1) {
1189 if (tag != "manifest") {
1190 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
1191 goto bail;
1192 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001193 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -07001194 printf("package: name='%s' ",
1195 ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001196 int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR,
1197 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001198 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001199 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n",
1200 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001201 goto bail;
1202 }
1203 if (versionCode > 0) {
1204 printf("versionCode='%d' ", versionCode);
1205 } else {
1206 printf("versionCode='' ");
1207 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001208 String8 versionName = AaptXml::getResolvedAttribute(res, tree,
1209 VERSION_NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001210 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001211 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n",
1212 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001213 goto bail;
1214 }
Adam Lesinski25d35a92014-08-11 09:41:56 -07001215 printf("versionName='%s'",
Maurice Chu2675f762013-10-22 17:33:11 -07001216 ResTable::normalizeForOutput(versionName.string()).string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001217
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001218 String8 splitName = AaptXml::getAttribute(tree, NULL, "split");
Adam Lesinski25d35a92014-08-11 09:41:56 -07001219 if (!splitName.isEmpty()) {
1220 printf(" split='%s'", ResTable::normalizeForOutput(
1221 splitName.string()).string());
1222 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001223
Adam Lesinski5283fab2014-08-29 11:23:55 -07001224 String8 platformVersionName = AaptXml::getAttribute(tree, NULL,
1225 "platformBuildVersionName");
1226 printf(" platformBuildVersionName='%s'", platformVersionName.string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001227 printf("\n");
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001228
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001229 int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
1230 INSTALL_LOCATION_ATTR, &error);
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001231 if (error != "") {
1232 fprintf(stderr, "ERROR getting 'android:installLocation' attribute: %s\n",
1233 error.string());
1234 goto bail;
1235 }
1236
1237 if (installLocation >= 0) {
1238 printf("install-location:'");
1239 switch (installLocation) {
1240 case 0:
1241 printf("auto");
1242 break;
1243 case 1:
1244 printf("internalOnly");
1245 break;
1246 case 2:
1247 printf("preferExternal");
1248 break;
1249 default:
1250 fprintf(stderr, "Invalid installLocation %d\n", installLocation);
1251 goto bail;
1252 }
1253 printf("'\n");
1254 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001255 } else if (depth == 2) {
1256 withinApplication = false;
1257 if (tag == "application") {
1258 withinApplication = true;
1259
1260 String8 label;
1261 const size_t NL = locales.size();
1262 for (size_t i=0; i<NL; i++) {
1263 const char* localeStr = locales[i].string();
1264 assets.setLocale(localeStr != NULL ? localeStr : "");
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001265 String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1266 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001267 if (llabel != "") {
1268 if (localeStr == NULL || strlen(localeStr) == 0) {
1269 label = llabel;
Maurice Chu2675f762013-10-22 17:33:11 -07001270 printf("application-label:'%s'\n",
1271 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001272 } else {
1273 if (label == "") {
1274 label = llabel;
1275 }
1276 printf("application-label-%s:'%s'\n", localeStr,
Maurice Chu2675f762013-10-22 17:33:11 -07001277 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001278 }
1279 }
1280 }
1281
1282 ResTable_config tmpConfig = config;
1283 const size_t ND = densities.size();
1284 for (size_t i=0; i<ND; i++) {
1285 tmpConfig.density = densities[i];
1286 assets.setConfiguration(tmpConfig);
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001287 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1288 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001289 if (icon != "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001290 printf("application-icon-%d:'%s'\n", densities[i],
1291 ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001292 }
1293 }
1294 assets.setConfiguration(config);
1295
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001296 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001297 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001298 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1299 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001300 goto bail;
1301 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001302 int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0,
1303 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001304 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001305 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n",
1306 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001307 goto bail;
1308 }
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001309
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001310 String8 banner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1311 &error);
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001312 if (error != "") {
1313 fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
1314 error.string());
1315 goto bail;
1316 }
Maurice Chu2675f762013-10-22 17:33:11 -07001317 printf("application: label='%s' ",
1318 ResTable::normalizeForOutput(label.string()).string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001319 printf("icon='%s'", ResTable::normalizeForOutput(icon.string()).string());
1320 if (banner != "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001321 printf(" banner='%s'",
1322 ResTable::normalizeForOutput(banner.string()).string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001323 }
1324 printf("\n");
Adam Lesinski282e1812014-01-23 18:17:42 -08001325 if (testOnly != 0) {
1326 printf("testOnly='%d'\n", testOnly);
1327 }
1328
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001329 int32_t isGame = AaptXml::getResolvedIntegerAttribute(res, tree,
1330 ISGAME_ATTR, 0, &error);
1331 if (error != "") {
1332 fprintf(stderr, "ERROR getting 'android:isGame' attribute: %s\n",
1333 error.string());
1334 goto bail;
1335 }
1336 if (isGame != 0) {
1337 printf("application-isGame\n");
1338 }
1339
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001340 int32_t debuggable = AaptXml::getResolvedIntegerAttribute(res, tree,
1341 DEBUGGABLE_ATTR, 0, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001342 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001343 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n",
1344 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001345 goto bail;
1346 }
1347 if (debuggable != 0) {
1348 printf("application-debuggable\n");
1349 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07001350
1351 // We must search by name because the multiArch flag hasn't been API
1352 // frozen yet.
1353 int32_t multiArchIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
1354 "multiArch");
1355 if (multiArchIndex >= 0) {
1356 Res_value value;
1357 if (tree.getAttributeValue(multiArchIndex, &value) != NO_ERROR) {
1358 if (value.dataType >= Res_value::TYPE_FIRST_INT &&
1359 value.dataType <= Res_value::TYPE_LAST_INT) {
1360 hasMultiArch = value.data;
1361 }
1362 }
1363 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001364 } else if (tag == "uses-sdk") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001365 int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR,
1366 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001367 if (error != "") {
1368 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001369 String8 name = AaptXml::getResolvedAttribute(res, tree,
1370 MIN_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001371 if (error != "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001372 fprintf(stderr,
1373 "ERROR getting 'android:minSdkVersion' attribute: %s\n",
Adam Lesinski282e1812014-01-23 18:17:42 -08001374 error.string());
1375 goto bail;
1376 }
1377 if (name == "Donut") targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001378 printf("sdkVersion:'%s'\n",
1379 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001380 } else if (code != -1) {
1381 targetSdk = code;
1382 printf("sdkVersion:'%d'\n", code);
1383 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001384 code = AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -08001385 if (code != -1) {
1386 printf("maxSdkVersion:'%d'\n", code);
1387 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001388 code = AaptXml::getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001389 if (error != "") {
1390 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001391 String8 name = AaptXml::getResolvedAttribute(res, tree,
1392 TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001393 if (error != "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001394 fprintf(stderr,
1395 "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
Adam Lesinski282e1812014-01-23 18:17:42 -08001396 error.string());
1397 goto bail;
1398 }
1399 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001400 printf("targetSdkVersion:'%s'\n",
1401 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001402 } else if (code != -1) {
1403 if (targetSdk < code) {
1404 targetSdk = code;
1405 }
1406 printf("targetSdkVersion:'%d'\n", code);
1407 }
1408 } else if (tag == "uses-configuration") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001409 int32_t reqTouchScreen = AaptXml::getIntegerAttribute(tree,
1410 REQ_TOUCH_SCREEN_ATTR, 0);
1411 int32_t reqKeyboardType = AaptXml::getIntegerAttribute(tree,
1412 REQ_KEYBOARD_TYPE_ATTR, 0);
1413 int32_t reqHardKeyboard = AaptXml::getIntegerAttribute(tree,
1414 REQ_HARD_KEYBOARD_ATTR, 0);
1415 int32_t reqNavigation = AaptXml::getIntegerAttribute(tree,
1416 REQ_NAVIGATION_ATTR, 0);
1417 int32_t reqFiveWayNav = AaptXml::getIntegerAttribute(tree,
1418 REQ_FIVE_WAY_NAV_ATTR, 0);
Adam Lesinski282e1812014-01-23 18:17:42 -08001419 printf("uses-configuration:");
1420 if (reqTouchScreen != 0) {
1421 printf(" reqTouchScreen='%d'", reqTouchScreen);
1422 }
1423 if (reqKeyboardType != 0) {
1424 printf(" reqKeyboardType='%d'", reqKeyboardType);
1425 }
1426 if (reqHardKeyboard != 0) {
1427 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1428 }
1429 if (reqNavigation != 0) {
1430 printf(" reqNavigation='%d'", reqNavigation);
1431 }
1432 if (reqFiveWayNav != 0) {
1433 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1434 }
1435 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001436 } else if (tag == "supports-input") {
1437 withinSupportsInput = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001438 } else if (tag == "supports-screens") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001439 smallScreen = AaptXml::getIntegerAttribute(tree,
1440 SMALL_SCREEN_ATTR, 1);
1441 normalScreen = AaptXml::getIntegerAttribute(tree,
1442 NORMAL_SCREEN_ATTR, 1);
1443 largeScreen = AaptXml::getIntegerAttribute(tree,
1444 LARGE_SCREEN_ATTR, 1);
1445 xlargeScreen = AaptXml::getIntegerAttribute(tree,
1446 XLARGE_SCREEN_ATTR, 1);
1447 anyDensity = AaptXml::getIntegerAttribute(tree,
1448 ANY_DENSITY_ATTR, 1);
1449 requiresSmallestWidthDp = AaptXml::getIntegerAttribute(tree,
1450 REQUIRES_SMALLEST_WIDTH_DP_ATTR, 0);
1451 compatibleWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1452 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, 0);
1453 largestWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1454 LARGEST_WIDTH_LIMIT_DP_ATTR, 0);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001455 } else if (tag == "feature-group") {
1456 withinFeatureGroup = true;
1457 FeatureGroup group;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001458 group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001459 if (error != "") {
1460 fprintf(stderr, "ERROR getting 'android:label' attribute:"
1461 " %s\n", error.string());
1462 goto bail;
1463 }
1464 featureGroups.add(group);
1465
Adam Lesinski282e1812014-01-23 18:17:42 -08001466 } else if (tag == "uses-feature") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001467 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001468 if (name != "" && error == "") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001469 const char* androidSchema =
1470 "http://schemas.android.com/apk/res/android";
Adam Lesinski282e1812014-01-23 18:17:42 -08001471
Adam Lesinski694d0a72016-04-06 16:12:04 -07001472 int32_t req = AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1,
1473 &error);
1474 if (error != "") {
1475 SourcePos(manifestFile, tree.getLineNumber()).error(
1476 "failed to read attribute 'android:required': %s",
1477 error.string());
1478 goto bail;
1479 }
1480
1481 int32_t version = AaptXml::getIntegerAttribute(tree, androidSchema,
1482 "version", 0, &error);
1483 if (error != "") {
1484 SourcePos(manifestFile, tree.getLineNumber()).error(
1485 "failed to read attribute 'android:version': %s",
1486 error.string());
1487 goto bail;
1488 }
1489
1490 commonFeatures.features.add(name, Feature(req != 0, version));
Adam Lesinski2c72b682014-06-24 09:56:01 -07001491 if (req) {
1492 addParentFeatures(&commonFeatures, name);
Adam Lesinski282e1812014-01-23 18:17:42 -08001493 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001494 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001495 int vers = AaptXml::getIntegerAttribute(tree,
Adam Lesinski282e1812014-01-23 18:17:42 -08001496 GL_ES_VERSION_ATTR, &error);
1497 if (error == "") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001498 if (vers > commonFeatures.openGLESVersion) {
1499 commonFeatures.openGLESVersion = vers;
1500 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001501 }
1502 }
1503 } else if (tag == "uses-permission") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001504 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001505 if (error != "") {
Adam Lesinski282e1812014-01-23 18:17:42 -08001506 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1507 error.string());
1508 goto bail;
1509 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001510
1511 if (name == "") {
1512 fprintf(stderr, "ERROR: missing 'android:name' for uses-permission\n");
1513 goto bail;
1514 }
1515
1516 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, false);
1517
1518 if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1519 hasWriteExternalStoragePermission = true;
1520 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1521 hasReadExternalStoragePermission = true;
1522 } else if (name == "android.permission.READ_PHONE_STATE") {
1523 hasReadPhoneStatePermission = true;
1524 } else if (name == "android.permission.READ_CONTACTS") {
1525 hasReadContactsPermission = true;
1526 } else if (name == "android.permission.WRITE_CONTACTS") {
1527 hasWriteContactsPermission = true;
1528 } else if (name == "android.permission.READ_CALL_LOG") {
1529 hasReadCallLogPermission = true;
1530 } else if (name == "android.permission.WRITE_CALL_LOG") {
1531 hasWriteCallLogPermission = true;
1532 }
1533
1534 printUsesPermission(name,
1535 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
1536 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
1537
1538 } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
1539 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1540 if (error != "") {
1541 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1542 error.string());
1543 goto bail;
1544 }
1545
1546 if (name == "") {
1547 fprintf(stderr, "ERROR: missing 'android:name' for "
1548 "uses-permission-sdk-23\n");
1549 goto bail;
1550 }
1551
1552 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, true);
1553
1554 printUsesPermissionSdk23(
1555 name, AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
1556
Adam Lesinski282e1812014-01-23 18:17:42 -08001557 } else if (tag == "uses-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001558 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001559 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001560 printf("uses-package:'%s'\n",
1561 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001562 } else {
1563 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1564 error.string());
1565 goto bail;
1566 }
1567 } else if (tag == "original-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001568 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001569 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001570 printf("original-package:'%s'\n",
1571 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001572 } else {
1573 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1574 error.string());
1575 goto bail;
1576 }
1577 } else if (tag == "supports-gl-texture") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001578 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001579 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001580 printf("supports-gl-texture:'%s'\n",
1581 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001582 } else {
1583 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1584 error.string());
1585 goto bail;
1586 }
1587 } else if (tag == "compatible-screens") {
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001588 printCompatibleScreens(tree, &error);
1589 if (error != "") {
1590 fprintf(stderr, "ERROR getting compatible screens: %s\n",
1591 error.string());
1592 goto bail;
1593 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001594 depth--;
1595 } else if (tag == "package-verifier") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001596 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001597 if (name != "" && error == "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001598 String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR,
1599 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001600 if (publicKey != "" && error == "") {
1601 printf("package-verifier: name='%s' publicKey='%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001602 ResTable::normalizeForOutput(name.string()).string(),
1603 ResTable::normalizeForOutput(publicKey.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001604 }
1605 }
1606 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001607 } else if (depth == 3) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001608 withinActivity = false;
1609 withinReceiver = false;
1610 withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001611 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001612 hasIntentFilter = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001613 hasMetaHostPaymentCategory = false;
1614 hasMetaOffHostPaymentCategory = false;
1615 hasBindDeviceAdminPermission = false;
1616 hasBindInputMethodPermission = false;
1617 hasBindAccessibilityServicePermission = false;
1618 hasBindPrintServicePermission = false;
1619 hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001620 hasRequiredSafAttributes = false;
1621 hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001622 hasBindDreamServicePermission = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001623 if (withinApplication) {
1624 if(tag == "activity") {
1625 withinActivity = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001626 activityName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001627 if (error != "") {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001628 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1629 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001630 goto bail;
1631 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001632
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001633 activityLabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1634 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001635 if (error != "") {
1636 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
1637 error.string());
1638 goto bail;
1639 }
1640
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001641 activityIcon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1642 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001643 if (error != "") {
1644 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1645 error.string());
1646 goto bail;
1647 }
1648
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001649 activityBanner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1650 &error);
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001651 if (error != "") {
1652 fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
1653 error.string());
1654 goto bail;
1655 }
1656
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001657 int32_t orien = AaptXml::getResolvedIntegerAttribute(res, tree,
Michael Wrightec4fdec2013-09-06 16:50:52 -07001658 SCREEN_ORIENTATION_ATTR, &error);
1659 if (error == "") {
1660 if (orien == 0 || orien == 6 || orien == 8) {
1661 // Requests landscape, sensorLandscape, or reverseLandscape.
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001662 addImpliedFeature(&impliedFeatures,
1663 "android.hardware.screen.landscape",
1664 "one or more activities have specified a "
1665 "landscape orientation",
1666 false);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001667 } else if (orien == 1 || orien == 7 || orien == 9) {
1668 // Requests portrait, sensorPortrait, or reversePortrait.
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001669 addImpliedFeature(&impliedFeatures,
1670 "android.hardware.screen.portrait",
1671 "one or more activities have specified a "
1672 "portrait orientation",
1673 false);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001674 }
1675 }
1676 } else if (tag == "uses-library") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001677 String8 libraryName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001678 if (error != "") {
1679 fprintf(stderr,
1680 "ERROR getting 'android:name' attribute for uses-library"
1681 " %s\n", error.string());
1682 goto bail;
1683 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001684 int req = AaptXml::getIntegerAttribute(tree,
1685 REQUIRED_ATTR, 1);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001686 printf("uses-library%s:'%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001687 req ? "" : "-not-required", ResTable::normalizeForOutput(
1688 libraryName.string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001689 } else if (tag == "receiver") {
1690 withinReceiver = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001691 receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001692
1693 if (error != "") {
1694 fprintf(stderr,
1695 "ERROR getting 'android:name' attribute for receiver:"
1696 " %s\n", error.string());
1697 goto bail;
1698 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001699
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001700 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1701 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001702 if (error == "") {
1703 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1704 hasBindDeviceAdminPermission = true;
1705 }
1706 } else {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001707 fprintf(stderr,
1708 "ERROR getting 'android:permission' attribute for"
1709 " receiver '%s': %s\n",
1710 receiverName.string(), error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001711 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001712 } else if (tag == "service") {
1713 withinService = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001714 serviceName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001715
1716 if (error != "") {
1717 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1718 "service:%s\n", error.string());
1719 goto bail;
1720 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001721
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001722 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1723 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001724 if (error == "") {
1725 if (permission == "android.permission.BIND_INPUT_METHOD") {
1726 hasBindInputMethodPermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001727 } else if (permission ==
1728 "android.permission.BIND_ACCESSIBILITY_SERVICE") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001729 hasBindAccessibilityServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001730 } else if (permission ==
1731 "android.permission.BIND_PRINT_SERVICE") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001732 hasBindPrintServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001733 } else if (permission ==
1734 "android.permission.BIND_NFC_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07001735 hasBindNfcServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001736 } else if (permission ==
1737 "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001738 hasBindNotificationListenerServicePermission = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001739 } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
1740 hasBindDreamServicePermission = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001741 }
1742 } else {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001743 fprintf(stderr, "ERROR getting 'android:permission' attribute for "
1744 "service '%s': %s\n", serviceName.string(), error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001745 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001746 } else if (tag == "provider") {
1747 withinProvider = true;
1748
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001749 bool exported = AaptXml::getResolvedIntegerAttribute(res, tree,
1750 EXPORTED_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001751 if (error != "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001752 fprintf(stderr,
1753 "ERROR getting 'android:exported' attribute for provider:"
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001754 " %s\n", error.string());
1755 goto bail;
1756 }
1757
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001758 bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute(
1759 res, tree, GRANT_URI_PERMISSIONS_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001760 if (error != "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001761 fprintf(stderr,
1762 "ERROR getting 'android:grantUriPermissions' attribute for "
1763 "provider: %s\n", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001764 goto bail;
1765 }
1766
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001767 String8 permission = AaptXml::getResolvedAttribute(res, tree,
1768 PERMISSION_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001769 if (error != "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001770 fprintf(stderr, "ERROR getting 'android:permission' attribute for "
1771 "provider: %s\n", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001772 goto bail;
1773 }
1774
1775 hasRequiredSafAttributes |= exported && grantUriPermissions &&
1776 permission == "android.permission.MANAGE_DOCUMENTS";
1777
Michael Wrightec4fdec2013-09-06 16:50:52 -07001778 } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001779 String8 metaDataName = AaptXml::getResolvedAttribute(res, tree,
1780 NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001781 if (error != "") {
1782 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1783 "meta-data:%s\n", error.string());
1784 goto bail;
1785 }
Maurice Chu2675f762013-10-22 17:33:11 -07001786 printf("meta-data: name='%s' ",
1787 ResTable::normalizeForOutput(metaDataName.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001788 printResolvedResourceAttribute(res, tree, VALUE_ATTR, String8("value"),
Maurice Chu76327312013-10-16 18:28:46 -07001789 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001790 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001791 // Try looking for a RESOURCE_ATTR
1792 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001793 printResolvedResourceAttribute(res, tree, RESOURCE_ATTR,
Maurice Chu76327312013-10-16 18:28:46 -07001794 String8("resource"), &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001795 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001796 fprintf(stderr, "ERROR getting 'android:value' or "
1797 "'android:resource' attribute for "
1798 "meta-data:%s\n", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001799 goto bail;
1800 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001801 }
Maurice Chu76327312013-10-16 18:28:46 -07001802 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001803 } else if (withinSupportsInput && tag == "input-type") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001804 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001805 if (name != "" && error == "") {
1806 supportedInput.add(name);
1807 } else {
1808 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1809 error.string());
1810 goto bail;
1811 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001812 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001813 } else if (withinFeatureGroup && tag == "uses-feature") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001814 const String8 androidSchema("http://schemas.android.com/apk/res/android");
Adam Lesinski2c72b682014-06-24 09:56:01 -07001815 FeatureGroup& top = featureGroups.editTop();
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001816
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001817 String8 name = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001818 if (name != "" && error == "") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001819 Feature feature(true);
1820
1821 int32_t featureVers = AaptXml::getIntegerAttribute(
1822 tree, androidSchema.string(), "version", 0, &error);
1823 if (error == "") {
1824 feature.version = featureVers;
1825 } else {
1826 SourcePos(manifestFile, tree.getLineNumber()).error(
1827 "failed to read attribute 'android:version': %s",
1828 error.string());
1829 goto bail;
1830 }
1831
1832 top.features.add(name, feature);
Adam Lesinskid3edfde2014-08-08 17:32:44 -07001833 addParentFeatures(&top, name);
Adam Lesinski694d0a72016-04-06 16:12:04 -07001834
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001835 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001836 int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR,
1837 &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001838 if (error == "") {
1839 if (vers > top.openGLESVersion) {
1840 top.openGLESVersion = vers;
1841 }
1842 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001843 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001844 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001845 } else if (depth == 4) {
1846 if (tag == "intent-filter") {
1847 hasIntentFilter = true;
1848 withinIntentFilter = true;
1849 actMainActivity = false;
1850 actWidgetReceivers = false;
1851 actImeService = false;
1852 actWallpaperService = false;
1853 actAccessibilityService = false;
1854 actPrintService = false;
1855 actDeviceAdminEnabled = false;
1856 actHostApduService = false;
1857 actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001858 actDocumentsProvider = false;
1859 actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001860 actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001861 actCamera = false;
1862 actCameraSecure = false;
1863 catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001864 } else if (withinService && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001865 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001866 if (error != "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001867 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1868 "meta-data tag in service '%s': %s\n", serviceName.string(),
1869 error.string());
Adam Lesinski94fc9122013-09-30 17:16:09 -07001870 goto bail;
1871 }
1872
1873 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1874 name == "android.nfc.cardemulation.off_host_apdu_service") {
1875 bool offHost = true;
1876 if (name == "android.nfc.cardemulation.host_apdu_service") {
1877 offHost = false;
1878 }
1879
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001880 String8 xmlPath = AaptXml::getResolvedAttribute(res, tree,
1881 RESOURCE_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001882 if (error != "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001883 fprintf(stderr, "ERROR getting 'android:resource' attribute for "
1884 "meta-data tag in service '%s': %s\n",
1885 serviceName.string(), error.string());
Adam Lesinski94fc9122013-09-30 17:16:09 -07001886 goto bail;
1887 }
1888
1889 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1890 offHost, &error);
1891 if (error != "") {
1892 fprintf(stderr, "ERROR getting AID category for service '%s'\n",
1893 serviceName.string());
1894 goto bail;
1895 }
1896
1897 const size_t catLen = categories.size();
1898 for (size_t i = 0; i < catLen; i++) {
1899 bool paymentCategory = (categories[i] == "payment");
1900 if (offHost) {
1901 hasMetaOffHostPaymentCategory |= paymentCategory;
1902 } else {
1903 hasMetaHostPaymentCategory |= paymentCategory;
1904 }
1905 }
1906 }
1907 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001908 } else if ((depth == 5) && withinIntentFilter) {
1909 String8 action;
1910 if (tag == "action") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001911 action = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001912 if (error != "") {
1913 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1914 error.string());
1915 goto bail;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001916 }
1917
Adam Lesinskia5018c92013-09-30 16:23:15 -07001918 if (withinActivity) {
1919 if (action == "android.intent.action.MAIN") {
1920 isMainActivity = true;
1921 actMainActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001922 } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
1923 action == "android.media.action.VIDEO_CAMERA") {
1924 actCamera = true;
1925 } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
1926 actCameraSecure = true;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001927 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001928 } else if (withinReceiver) {
1929 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1930 actWidgetReceivers = true;
1931 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1932 actDeviceAdminEnabled = true;
1933 }
1934 } else if (withinService) {
1935 if (action == "android.view.InputMethod") {
1936 actImeService = true;
1937 } else if (action == "android.service.wallpaper.WallpaperService") {
1938 actWallpaperService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001939 } else if (action ==
1940 "android.accessibilityservice.AccessibilityService") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001941 actAccessibilityService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001942 } else if (action =="android.printservice.PrintService") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001943 actPrintService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001944 } else if (action ==
1945 "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07001946 actHostApduService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001947 } else if (action ==
1948 "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07001949 actOffHostApduService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001950 } else if (action ==
1951 "android.service.notification.NotificationListenerService") {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001952 actNotificationListenerService = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001953 } else if (action == "android.service.dreams.DreamService") {
1954 actDreamService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001955 }
1956 } else if (withinProvider) {
1957 if (action == "android.content.action.DOCUMENTS_PROVIDER") {
1958 actDocumentsProvider = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001959 }
1960 }
1961 if (action == "android.intent.action.SEARCH") {
1962 isSearchable = true;
1963 }
1964 }
1965
1966 if (tag == "category") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001967 String8 category = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001968 if (error != "") {
1969 fprintf(stderr, "ERROR getting 'name' attribute: %s\n",
1970 error.string());
1971 goto bail;
1972 }
1973 if (withinActivity) {
1974 if (category == "android.intent.category.LAUNCHER") {
1975 isLauncherActivity = true;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001976 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
1977 isLeanbackLauncherActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001978 } else if (category == "android.intent.category.HOME") {
1979 catLauncher = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001980 }
1981 }
1982 }
1983 }
1984 }
1985
1986 // Pre-1.6 implicitly granted permission compatibility logic
1987 if (targetSdk < 4) {
1988 if (!hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001989 printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
1990 printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
1991 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001992 hasWriteExternalStoragePermission = true;
1993 }
1994 if (!hasReadPhoneStatePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001995 printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
1996 printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
1997 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001998 }
1999 }
2000
2001 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
2002 // force them to always take READ_EXTERNAL_STORAGE as well. We always
2003 // do this (regardless of target API version) because we can't have
2004 // an app with write permission but not read permission.
2005 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002006 printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"));
2007 printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
2008 String8("requested WRITE_EXTERNAL_STORAGE"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002009 }
2010
2011 // Pre-JellyBean call log permission compatibility.
2012 if (targetSdk < 16) {
2013 if (!hasReadCallLogPermission && hasReadContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002014 printUsesPermission(String8("android.permission.READ_CALL_LOG"));
2015 printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
2016 String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002017 }
2018 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002019 printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
2020 printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
2021 String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002022 }
2023 }
2024
Adam Lesinskica955a42016-08-01 16:44:29 -07002025 // If the app hasn't declared the touchscreen as a feature requirement (either
2026 // directly or implied, required or not), then the faketouch feature is implied.
2027 if (!hasFeature("android.hardware.touchscreen", commonFeatures, impliedFeatures)) {
2028 addImpliedFeature(&impliedFeatures, "android.hardware.faketouch",
2029 "default feature for all apps", false);
2030 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07002031
2032 const size_t numFeatureGroups = featureGroups.size();
2033 if (numFeatureGroups == 0) {
2034 // If no <feature-group> tags were defined, apply auto-implied features.
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002035 printDefaultFeatureGroup(commonFeatures, impliedFeatures);
Adam Lesinski2c72b682014-06-24 09:56:01 -07002036
2037 } else {
2038 // <feature-group> tags are defined, so we ignore implied features and
2039 for (size_t i = 0; i < numFeatureGroups; i++) {
2040 FeatureGroup& grp = featureGroups.editItemAt(i);
2041
Adam Lesinskid7a94da2014-07-25 14:38:54 -07002042 if (commonFeatures.openGLESVersion > grp.openGLESVersion) {
2043 grp.openGLESVersion = commonFeatures.openGLESVersion;
2044 }
2045
Adam Lesinski2c72b682014-06-24 09:56:01 -07002046 // Merge the features defined in the top level (not inside a <feature-group>)
2047 // with this feature group.
2048 const size_t numCommonFeatures = commonFeatures.features.size();
2049 for (size_t j = 0; j < numCommonFeatures; j++) {
2050 if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07002051 grp.features.add(commonFeatures.features.keyAt(j),
2052 commonFeatures.features[j]);
Adam Lesinski2c72b682014-06-24 09:56:01 -07002053 }
2054 }
2055
Adam Lesinski73a05112014-12-08 12:53:17 -08002056 if (!grp.features.isEmpty()) {
Adam Lesinski2c72b682014-06-24 09:56:01 -07002057 printFeatureGroup(grp);
Adam Lesinski282e1812014-01-23 18:17:42 -08002058 }
2059 }
2060 }
2061
Adam Lesinski282e1812014-01-23 18:17:42 -08002062
Adam Lesinski282e1812014-01-23 18:17:42 -08002063 if (hasWidgetReceivers) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002064 printComponentPresence("app-widget");
Adam Lesinski282e1812014-01-23 18:17:42 -08002065 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002066 if (hasDeviceAdminReceiver) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002067 printComponentPresence("device-admin");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002068 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002069 if (hasImeService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002070 printComponentPresence("ime");
Adam Lesinski282e1812014-01-23 18:17:42 -08002071 }
2072 if (hasWallpaperService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002073 printComponentPresence("wallpaper");
Adam Lesinski282e1812014-01-23 18:17:42 -08002074 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002075 if (hasAccessibilityService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002076 printComponentPresence("accessibility");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002077 }
2078 if (hasPrintService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002079 printComponentPresence("print-service");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002080 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07002081 if (hasPaymentService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002082 printComponentPresence("payment");
2083 }
2084 if (isSearchable) {
2085 printComponentPresence("search");
2086 }
2087 if (hasDocumentsProvider) {
2088 printComponentPresence("document-provider");
2089 }
2090 if (hasLauncher) {
2091 printComponentPresence("launcher");
2092 }
2093 if (hasNotificationListenerService) {
2094 printComponentPresence("notification-listener");
2095 }
John Spurlockeb8d1be2014-06-25 17:46:15 -04002096 if (hasDreamService) {
2097 printComponentPresence("dream");
2098 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002099 if (hasCameraActivity) {
2100 printComponentPresence("camera");
2101 }
2102 if (hasCameraSecureActivity) {
2103 printComponentPresence("camera-secure");
2104 }
2105
2106 if (hasMainActivity) {
2107 printf("main\n");
Adam Lesinski94fc9122013-09-30 17:16:09 -07002108 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002109 if (hasOtherActivities) {
2110 printf("other-activities\n");
2111 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002112 if (hasOtherReceivers) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002113 printf("other-receivers\n");
2114 }
2115 if (hasOtherServices) {
2116 printf("other-services\n");
2117 }
2118
2119 // For modern apps, if screen size buckets haven't been specified
2120 // but the new width ranges have, then infer the buckets from them.
2121 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
2122 && requiresSmallestWidthDp > 0) {
2123 int compatWidth = compatibleWidthLimitDp;
2124 if (compatWidth <= 0) {
2125 compatWidth = requiresSmallestWidthDp;
2126 }
2127 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
2128 smallScreen = -1;
2129 } else {
2130 smallScreen = 0;
2131 }
2132 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
2133 normalScreen = -1;
2134 } else {
2135 normalScreen = 0;
2136 }
2137 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
2138 largeScreen = -1;
2139 } else {
2140 largeScreen = 0;
2141 }
2142 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
2143 xlargeScreen = -1;
2144 } else {
2145 xlargeScreen = 0;
2146 }
2147 }
2148
2149 // Determine default values for any unspecified screen sizes,
2150 // based on the target SDK of the package. As of 4 (donut)
2151 // the screen size support was introduced, so all default to
2152 // enabled.
2153 if (smallScreen > 0) {
2154 smallScreen = targetSdk >= 4 ? -1 : 0;
2155 }
2156 if (normalScreen > 0) {
2157 normalScreen = -1;
2158 }
2159 if (largeScreen > 0) {
2160 largeScreen = targetSdk >= 4 ? -1 : 0;
2161 }
2162 if (xlargeScreen > 0) {
2163 // Introduced in Gingerbread.
2164 xlargeScreen = targetSdk >= 9 ? -1 : 0;
2165 }
2166 if (anyDensity > 0) {
2167 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
2168 || compatibleWidthLimitDp > 0) ? -1 : 0;
2169 }
2170 printf("supports-screens:");
2171 if (smallScreen != 0) {
2172 printf(" 'small'");
2173 }
2174 if (normalScreen != 0) {
2175 printf(" 'normal'");
2176 }
2177 if (largeScreen != 0) {
2178 printf(" 'large'");
2179 }
2180 if (xlargeScreen != 0) {
2181 printf(" 'xlarge'");
2182 }
2183 printf("\n");
2184 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
2185 if (requiresSmallestWidthDp > 0) {
2186 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
2187 }
2188 if (compatibleWidthLimitDp > 0) {
2189 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
2190 }
2191 if (largestWidthLimitDp > 0) {
2192 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
2193 }
2194
2195 printf("locales:");
2196 const size_t NL = locales.size();
2197 for (size_t i=0; i<NL; i++) {
2198 const char* localeStr = locales[i].string();
2199 if (localeStr == NULL || strlen(localeStr) == 0) {
2200 localeStr = "--_--";
2201 }
2202 printf(" '%s'", localeStr);
2203 }
2204 printf("\n");
2205
2206 printf("densities:");
2207 const size_t ND = densities.size();
2208 for (size_t i=0; i<ND; i++) {
2209 printf(" '%d'", densities[i]);
2210 }
2211 printf("\n");
2212
2213 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
2214 if (dir != NULL) {
2215 if (dir->getFileCount() > 0) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002216 SortedVector<String8> architectures;
Adam Lesinski282e1812014-01-23 18:17:42 -08002217 for (size_t i=0; i<dir->getFileCount(); i++) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002218 architectures.add(ResTable::normalizeForOutput(
2219 dir->getFileName(i).string()));
Adam Lesinski282e1812014-01-23 18:17:42 -08002220 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07002221
2222 bool outputAltNativeCode = false;
2223 // A multiArch package is one that contains 64-bit and
2224 // 32-bit versions of native code and expects 3rd-party
2225 // apps to load these native code libraries. Since most
2226 // 64-bit systems also support 32-bit apps, the apps
2227 // loading this multiArch package's code may be either
2228 // 32-bit or 64-bit.
2229 if (hasMultiArch) {
2230 // If this is a multiArch package, report the 64-bit
2231 // version only. Then as a separate entry, report the
2232 // rest.
2233 //
2234 // If we report the 32-bit architecture, this APK will
2235 // be installed on a 32-bit device, causing a large waste
2236 // of bandwidth and disk space. This assumes that
2237 // the developer of the multiArch package has also
2238 // made a version that is 32-bit only.
2239 String8 intel64("x86_64");
2240 String8 arm64("arm64-v8a");
2241 ssize_t index = architectures.indexOf(intel64);
2242 if (index < 0) {
2243 index = architectures.indexOf(arm64);
2244 }
2245
2246 if (index >= 0) {
2247 printf("native-code: '%s'\n", architectures[index].string());
2248 architectures.removeAt(index);
2249 outputAltNativeCode = true;
2250 }
2251 }
2252
2253 const size_t archCount = architectures.size();
2254 if (archCount > 0) {
2255 if (outputAltNativeCode) {
2256 printf("alt-");
2257 }
2258 printf("native-code:");
2259 for (size_t i = 0; i < archCount; i++) {
2260 printf(" '%s'", architectures[i].string());
2261 }
2262 printf("\n");
2263 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002264 }
2265 delete dir;
2266 }
2267 } else if (strcmp("badger", option) == 0) {
2268 printf("%s", CONSOLE_DATA);
2269 } else if (strcmp("configurations", option) == 0) {
2270 Vector<ResTable_config> configs;
2271 res.getConfigurations(&configs);
2272 const size_t N = configs.size();
2273 for (size_t i=0; i<N; i++) {
2274 printf("%s\n", configs[i].toString().string());
2275 }
2276 } else {
2277 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2278 goto bail;
2279 }
2280 }
2281
2282 result = NO_ERROR;
2283
2284bail:
2285 if (asset) {
2286 delete asset;
2287 }
2288 return (result != NO_ERROR);
2289}
2290
2291
2292/*
2293 * Handle the "add" command, which wants to add files to a new or
2294 * pre-existing archive.
2295 */
2296int doAdd(Bundle* bundle)
2297{
2298 ZipFile* zip = NULL;
2299 status_t result = UNKNOWN_ERROR;
2300 const char* zipFileName;
2301
2302 if (bundle->getUpdate()) {
2303 /* avoid confusion */
2304 fprintf(stderr, "ERROR: can't use '-u' with add\n");
2305 goto bail;
2306 }
2307
2308 if (bundle->getFileSpecCount() < 1) {
2309 fprintf(stderr, "ERROR: must specify zip file name\n");
2310 goto bail;
2311 }
2312 zipFileName = bundle->getFileSpecEntry(0);
2313
2314 if (bundle->getFileSpecCount() < 2) {
2315 fprintf(stderr, "NOTE: nothing to do\n");
2316 goto bail;
2317 }
2318
2319 zip = openReadWrite(zipFileName, true);
2320 if (zip == NULL) {
2321 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2322 goto bail;
2323 }
2324
2325 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2326 const char* fileName = bundle->getFileSpecEntry(i);
2327
2328 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
2329 printf(" '%s'... (from gzip)\n", fileName);
2330 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
2331 } else {
2332 if (bundle->getJunkPath()) {
2333 String8 storageName = String8(fileName).getPathLeaf();
Maurice Chu2675f762013-10-22 17:33:11 -07002334 printf(" '%s' as '%s'...\n", fileName,
2335 ResTable::normalizeForOutput(storageName.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08002336 result = zip->add(fileName, storageName.string(),
2337 bundle->getCompressionMethod(), NULL);
2338 } else {
2339 printf(" '%s'...\n", fileName);
2340 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2341 }
2342 }
2343 if (result != NO_ERROR) {
2344 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2345 if (result == NAME_NOT_FOUND) {
2346 fprintf(stderr, ": file not found\n");
2347 } else if (result == ALREADY_EXISTS) {
2348 fprintf(stderr, ": already exists in archive\n");
2349 } else {
2350 fprintf(stderr, "\n");
2351 }
2352 goto bail;
2353 }
2354 }
2355
2356 result = NO_ERROR;
2357
2358bail:
2359 delete zip;
2360 return (result != NO_ERROR);
2361}
2362
2363
2364/*
2365 * Delete files from an existing archive.
2366 */
2367int doRemove(Bundle* bundle)
2368{
2369 ZipFile* zip = NULL;
2370 status_t result = UNKNOWN_ERROR;
2371 const char* zipFileName;
2372
2373 if (bundle->getFileSpecCount() < 1) {
2374 fprintf(stderr, "ERROR: must specify zip file name\n");
2375 goto bail;
2376 }
2377 zipFileName = bundle->getFileSpecEntry(0);
2378
2379 if (bundle->getFileSpecCount() < 2) {
2380 fprintf(stderr, "NOTE: nothing to do\n");
2381 goto bail;
2382 }
2383
2384 zip = openReadWrite(zipFileName, false);
2385 if (zip == NULL) {
2386 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2387 zipFileName);
2388 goto bail;
2389 }
2390
2391 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2392 const char* fileName = bundle->getFileSpecEntry(i);
2393 ZipEntry* entry;
2394
2395 entry = zip->getEntryByName(fileName);
2396 if (entry == NULL) {
2397 printf(" '%s' NOT FOUND\n", fileName);
2398 continue;
2399 }
2400
2401 result = zip->remove(entry);
2402
2403 if (result != NO_ERROR) {
2404 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2405 bundle->getFileSpecEntry(i), zipFileName);
2406 goto bail;
2407 }
2408 }
2409
2410 /* update the archive */
2411 zip->flush();
2412
2413bail:
2414 delete zip;
2415 return (result != NO_ERROR);
2416}
2417
Adam Lesinski3921e872014-05-13 10:56:25 -07002418static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002419 const size_t numDirs = dir->getDirs().size();
2420 for (size_t i = 0; i < numDirs; i++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002421 bool ignore = ignoreConfig;
2422 const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2423 const char* dirStr = subDir->getLeaf().string();
2424 if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2425 ignore = true;
2426 }
2427 status_t err = addResourcesToBuilder(subDir, builder, ignore);
Adam Lesinskifab50872014-04-16 14:40:42 -07002428 if (err != NO_ERROR) {
2429 return err;
2430 }
2431 }
2432
2433 const size_t numFiles = dir->getFiles().size();
2434 for (size_t i = 0; i < numFiles; i++) {
2435 sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2436 const size_t numConfigs = gp->getFiles().size();
2437 for (size_t j = 0; j < numConfigs; j++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002438 status_t err = NO_ERROR;
2439 if (ignoreConfig) {
2440 err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2441 } else {
2442 err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2443 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002444 if (err != NO_ERROR) {
2445 fprintf(stderr, "Failed to add %s (%s) to builder.\n",
2446 gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
2447 return err;
2448 }
2449 }
2450 }
2451 return NO_ERROR;
2452}
2453
2454static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2455 if (split->isBase()) {
2456 return original;
2457 }
2458
2459 String8 ext(original.getPathExtension());
2460 if (ext == String8(".apk")) {
2461 return String8::format("%s_%s%s",
2462 original.getBasePath().string(),
2463 split->getDirectorySafeName().string(),
2464 ext.string());
2465 }
2466
2467 return String8::format("%s_%s", original.string(),
2468 split->getDirectorySafeName().string());
2469}
Adam Lesinski282e1812014-01-23 18:17:42 -08002470
2471/*
2472 * Package up an asset directory and associated application files.
2473 */
2474int doPackage(Bundle* bundle)
2475{
2476 const char* outputAPKFile;
2477 int retVal = 1;
2478 status_t err;
2479 sp<AaptAssets> assets;
2480 int N;
2481 FILE* fp;
2482 String8 dependencyFile;
Adam Lesinskifab50872014-04-16 14:40:42 -07002483 sp<ApkBuilder> builder;
Adam Lesinski282e1812014-01-23 18:17:42 -08002484
Anton Krumina2ef5c02014-03-12 14:46:44 -07002485 // -c en_XA or/and ar_XB means do pseudolocalization
Adam Lesinskifab50872014-04-16 14:40:42 -07002486 sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2487 err = configFilter->parse(bundle->getConfigurations());
Adam Lesinski282e1812014-01-23 18:17:42 -08002488 if (err != NO_ERROR) {
2489 goto bail;
2490 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002491 if (configFilter->containsPseudo()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002492 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2493 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002494 if (configFilter->containsPseudoBidi()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002495 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
Adam Lesinski282e1812014-01-23 18:17:42 -08002496 }
2497
2498 N = bundle->getFileSpecCount();
2499 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
Adam Lesinski09384302014-01-22 16:07:42 -08002500 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002501 fprintf(stderr, "ERROR: no input files\n");
2502 goto bail;
2503 }
2504
2505 outputAPKFile = bundle->getOutputAPKFile();
2506
2507 // Make sure the filenames provided exist and are of the appropriate type.
2508 if (outputAPKFile) {
2509 FileType type;
2510 type = getFileType(outputAPKFile);
2511 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2512 fprintf(stderr,
2513 "ERROR: output file '%s' exists but is not regular file\n",
2514 outputAPKFile);
2515 goto bail;
2516 }
2517 }
2518
2519 // Load the assets.
2520 assets = new AaptAssets();
2521
2522 // Set up the resource gathering in assets if we're going to generate
2523 // dependency files. Every time we encounter a resource while slurping
2524 // the tree, we'll add it to these stores so we have full resource paths
2525 // to write to a dependency file.
2526 if (bundle->getGenDependencies()) {
2527 sp<FilePathStore> resPathStore = new FilePathStore;
2528 assets->setFullResPaths(resPathStore);
2529 sp<FilePathStore> assetPathStore = new FilePathStore;
2530 assets->setFullAssetPaths(assetPathStore);
2531 }
2532
2533 err = assets->slurpFromArgs(bundle);
2534 if (err < 0) {
2535 goto bail;
2536 }
2537
2538 if (bundle->getVerbose()) {
2539 assets->print(String8());
2540 }
2541
Adam Lesinskifab50872014-04-16 14:40:42 -07002542 // Create the ApkBuilder, which will collect the compiled files
2543 // to write to the final APK (or sets of APKs if we are building
2544 // a Split APK.
2545 builder = new ApkBuilder(configFilter);
2546
2547 // If we are generating a Split APK, find out which configurations to split on.
2548 if (bundle->getSplitConfigurations().size() > 0) {
2549 const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2550 const size_t numSplits = splitStrs.size();
2551 for (size_t i = 0; i < numSplits; i++) {
2552 std::set<ConfigDescription> configs;
2553 if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2554 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2555 goto bail;
2556 }
2557
2558 err = builder->createSplitForConfigs(configs);
2559 if (err != NO_ERROR) {
2560 goto bail;
2561 }
2562 }
2563 }
2564
Adam Lesinski282e1812014-01-23 18:17:42 -08002565 // If they asked for any fileAs that need to be compiled, do so.
2566 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002567 err = buildResources(bundle, assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002568 if (err != 0) {
2569 goto bail;
2570 }
2571 }
2572
2573 // At this point we've read everything and processed everything. From here
2574 // on out it's just writing output files.
2575 if (SourcePos::hasErrors()) {
2576 goto bail;
2577 }
2578
2579 // Update symbols with information about which ones are needed as Java symbols.
2580 assets->applyJavaSymbols();
2581 if (SourcePos::hasErrors()) {
2582 goto bail;
2583 }
2584
2585 // If we've been asked to generate a dependency file, do that here
2586 if (bundle->getGenDependencies()) {
2587 // If this is the packaging step, generate the dependency file next to
2588 // the output apk (e.g. bin/resources.ap_.d)
2589 if (outputAPKFile) {
2590 dependencyFile = String8(outputAPKFile);
2591 // Add the .d extension to the dependency file.
2592 dependencyFile.append(".d");
2593 } else {
2594 // Else if this is the R.java dependency generation step,
2595 // generate the dependency file in the R.java package subdirectory
2596 // e.g. gen/com/foo/app/R.java.d
2597 dependencyFile = String8(bundle->getRClassDir());
2598 dependencyFile.appendPath("R.java.d");
2599 }
2600 // Make sure we have a clean dependency file to start with
2601 fp = fopen(dependencyFile, "w");
2602 fclose(fp);
2603 }
2604
2605 // Write out R.java constants
2606 if (!assets->havePrivateSymbols()) {
2607 if (bundle->getCustomPackage() == NULL) {
2608 // Write the R.java file into the appropriate class directory
2609 // e.g. gen/com/foo/app/R.java
Adam Lesinski1e4663852014-08-15 14:47:28 -07002610 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002611 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002612 } else {
2613 const String8 customPkg(bundle->getCustomPackage());
Adam Lesinski1e4663852014-08-15 14:47:28 -07002614 err = writeResourceSymbols(bundle, assets, customPkg, true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002615 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002616 }
2617 if (err < 0) {
2618 goto bail;
2619 }
2620 // If we have library files, we're going to write our R.java file into
2621 // the appropriate class directory for those libraries as well.
2622 // e.g. gen/com/foo/app/lib/R.java
2623 if (bundle->getExtraPackages() != NULL) {
2624 // Split on colon
2625 String8 libs(bundle->getExtraPackages());
2626 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2627 while (packageString != NULL) {
2628 // Write the R.java file out with the correct package name
Marcin Kosiba0f3a5a62014-09-11 13:48:48 +01002629 err = writeResourceSymbols(bundle, assets, String8(packageString), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002630 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002631 if (err < 0) {
2632 goto bail;
2633 }
2634 packageString = strtok(NULL, ":");
2635 }
2636 libs.unlockBuffer();
2637 }
2638 } else {
Adam Lesinski1e4663852014-08-15 14:47:28 -07002639 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002640 if (err < 0) {
2641 goto bail;
2642 }
Adam Lesinski1e4663852014-08-15 14:47:28 -07002643 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002644 if (err < 0) {
2645 goto bail;
2646 }
2647 }
2648
2649 // Write out the ProGuard file
2650 err = writeProguardFile(bundle, assets);
2651 if (err < 0) {
2652 goto bail;
2653 }
2654
Rohit Agrawal682583c2016-04-21 16:29:58 -07002655 // Write out the Main Dex ProGuard file
2656 err = writeMainDexProguardFile(bundle, assets);
2657 if (err < 0) {
2658 goto bail;
2659 }
2660
Adam Lesinski282e1812014-01-23 18:17:42 -08002661 // Write the apk
2662 if (outputAPKFile) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002663 // Gather all resources and add them to the APK Builder. The builder will then
2664 // figure out which Split they belong in.
2665 err = addResourcesToBuilder(assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002666 if (err != NO_ERROR) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002667 goto bail;
2668 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002669
2670 const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2671 const size_t numSplits = splits.size();
2672 for (size_t i = 0; i < numSplits; i++) {
2673 const sp<ApkSplit>& split = splits[i];
2674 String8 outputPath = buildApkName(String8(outputAPKFile), split);
2675 err = writeAPK(bundle, outputPath, split);
2676 if (err != NO_ERROR) {
2677 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2678 goto bail;
2679 }
2680 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002681 }
2682
2683 // If we've been asked to generate a dependency file, we need to finish up here.
2684 // the writeResourceSymbols and writeAPK functions have already written the target
2685 // half of the dependency file, now we need to write the prerequisites. (files that
2686 // the R.java file or .ap_ file depend on)
2687 if (bundle->getGenDependencies()) {
2688 // Now that writeResourceSymbols or writeAPK has taken care of writing
2689 // the targets to our dependency file, we'll write the prereqs
2690 fp = fopen(dependencyFile, "a+");
2691 fprintf(fp, " : ");
2692 bool includeRaw = (outputAPKFile != NULL);
2693 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2694 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2695 // and therefore was not added to our pathstores during slurping
2696 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2697 fclose(fp);
2698 }
2699
2700 retVal = 0;
2701bail:
2702 if (SourcePos::hasErrors()) {
2703 SourcePos::printErrors(stderr);
2704 }
2705 return retVal;
2706}
2707
2708/*
2709 * Do PNG Crunching
2710 * PRECONDITIONS
2711 * -S flag points to a source directory containing drawable* folders
2712 * -C flag points to destination directory. The folder structure in the
2713 * source directory will be mirrored to the destination (cache) directory
2714 *
2715 * POSTCONDITIONS
2716 * Destination directory will be updated to match the PNG files in
Maurice Chu2675f762013-10-22 17:33:11 -07002717 * the source directory.
Adam Lesinski282e1812014-01-23 18:17:42 -08002718 */
2719int doCrunch(Bundle* bundle)
2720{
2721 fprintf(stdout, "Crunching PNG Files in ");
2722 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2723 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2724
2725 updatePreProcessedCache(bundle);
2726
2727 return NO_ERROR;
2728}
2729
2730/*
2731 * Do PNG Crunching on a single flag
2732 * -i points to a single png file
2733 * -o points to a single png output file
2734 */
2735int doSingleCrunch(Bundle* bundle)
2736{
2737 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2738 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2739
2740 String8 input(bundle->getSingleCrunchInputFile());
2741 String8 output(bundle->getSingleCrunchOutputFile());
2742
2743 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2744 // we can't return the status_t as it gets truncate to the lower 8 bits.
2745 return 42;
2746 }
2747
2748 return NO_ERROR;
2749}
2750
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002751int runInDaemonMode(Bundle* bundle) {
2752 std::cout << "Ready" << std::endl;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002753 for (std::string cmd; std::getline(std::cin, cmd);) {
2754 if (cmd == "quit") {
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002755 return NO_ERROR;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002756 } else if (cmd == "s") {
2757 // Two argument crunch
2758 std::string inputFile, outputFile;
2759 std::getline(std::cin, inputFile);
2760 std::getline(std::cin, outputFile);
2761 bundle->setSingleCrunchInputFile(inputFile.c_str());
2762 bundle->setSingleCrunchOutputFile(outputFile.c_str());
2763 std::cout << "Crunching " << inputFile << std::endl;
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002764 if (doSingleCrunch(bundle) != NO_ERROR) {
2765 std::cout << "Error" << std::endl;
2766 }
2767 std::cout << "Done" << std::endl;
2768 } else {
2769 // in case of invalid command, just bail out.
2770 std::cerr << "Unknown command" << std::endl;
2771 return -1;
2772 }
2773 }
2774 return -1;
2775}
2776
Adam Lesinski282e1812014-01-23 18:17:42 -08002777char CONSOLE_DATA[2925] = {
2778 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2779 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2780 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2781 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2782 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2783 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2784 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2785 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2786 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2787 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2788 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2789 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2790 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2791 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2792 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2793 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2794 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2795 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2796 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2797 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2798 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2799 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2800 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2801 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2802 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2803 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2804 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2805 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2806 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2807 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2808 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2809 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2810 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2811 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2812 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2813 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2814 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2815 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2816 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2817 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2818 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2819 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2820 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2821 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2822 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2823 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2824 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2825 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2826 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2827 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2828 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2829 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2830 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2831 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2832 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2833 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2834 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2835 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2836 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2837 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2838 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2839 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2840 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2841 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2842 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2843 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2844 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2845 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2846 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2847 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2848 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2849 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2850 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2851 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2852 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2853 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2854 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2855 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2856 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2857 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2858 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2859 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2860 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2861 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2862 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2863 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2864 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2865 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2866 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2867 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2868 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2869 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2870 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2871 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2872 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2873 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2874 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2875 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2876 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2877 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2878 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2879 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2880 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2881 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2882 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2883 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2884 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2885 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2886 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2887 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2888 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2889 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2890 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2891 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2892 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2893 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2894 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2895 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2896 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2897 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2898 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2899 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2900 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2901 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2902 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2903 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2904 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2905 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2906 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2907 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2908 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2909 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2910 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2911 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2912 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2913 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2914 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2915 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2916 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2917 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2918 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2919 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2920 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2921 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2922 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2923 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2924 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2925 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2926 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2927 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2928 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2929 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2930 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2931 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2932 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2933 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2934 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2935 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2936 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2937 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2938 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2939 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2940 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2941 };