blob: cb87737c68684433e26481e0ed2aead16277092f [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 Lesinski282e1812014-01-23 18:17:42 -080032/*
33 * Open the file read only. The call fails if the file doesn't exist.
34 *
35 * Returns NULL on failure.
36 */
37ZipFile* openReadOnly(const char* fileName)
38{
39 ZipFile* zip;
40 status_t result;
41
42 zip = new ZipFile;
43 result = zip->open(fileName, ZipFile::kOpenReadOnly);
44 if (result != NO_ERROR) {
45 if (result == NAME_NOT_FOUND) {
46 fprintf(stderr, "ERROR: '%s' not found\n", fileName);
47 } else if (result == PERMISSION_DENIED) {
48 fprintf(stderr, "ERROR: '%s' access denied\n", fileName);
49 } else {
50 fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n",
51 fileName);
52 }
53 delete zip;
54 return NULL;
55 }
56
57 return zip;
58}
59
60/*
61 * Open the file read-write. The file will be created if it doesn't
62 * already exist and "okayToCreate" is set.
63 *
64 * Returns NULL on failure.
65 */
66ZipFile* openReadWrite(const char* fileName, bool okayToCreate)
67{
68 ZipFile* zip = NULL;
69 status_t result;
70 int flags;
71
72 flags = ZipFile::kOpenReadWrite;
73 if (okayToCreate) {
74 flags |= ZipFile::kOpenCreate;
75 }
76
77 zip = new ZipFile;
78 result = zip->open(fileName, flags);
79 if (result != NO_ERROR) {
80 delete zip;
81 zip = NULL;
82 goto bail;
83 }
84
85bail:
86 return zip;
87}
88
89
90/*
91 * Return a short string describing the compression method.
92 */
93const char* compressionName(int method)
94{
95 if (method == ZipEntry::kCompressStored) {
96 return "Stored";
97 } else if (method == ZipEntry::kCompressDeflated) {
98 return "Deflated";
99 } else {
100 return "Unknown";
101 }
102}
103
104/*
105 * Return the percent reduction in size (0% == no compression).
106 */
107int calcPercent(long uncompressedLen, long compressedLen)
108{
109 if (!uncompressedLen) {
110 return 0;
111 } else {
112 return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5);
113 }
114}
115
116/*
117 * Handle the "list" command, which can be a simple file dump or
118 * a verbose listing.
119 *
120 * The verbose listing closely matches the output of the Info-ZIP "unzip"
121 * command.
122 */
123int doList(Bundle* bundle)
124{
125 int result = 1;
126 ZipFile* zip = NULL;
127 const ZipEntry* entry;
128 long totalUncLen, totalCompLen;
129 const char* zipFileName;
130
131 if (bundle->getFileSpecCount() != 1) {
132 fprintf(stderr, "ERROR: specify zip file name (only)\n");
133 goto bail;
134 }
135 zipFileName = bundle->getFileSpecEntry(0);
136
137 zip = openReadOnly(zipFileName);
138 if (zip == NULL) {
139 goto bail;
140 }
141
142 int count, i;
143
144 if (bundle->getVerbose()) {
145 printf("Archive: %s\n", zipFileName);
146 printf(
147 " Length Method Size Ratio Offset Date Time CRC-32 Name\n");
148 printf(
149 "-------- ------ ------- ----- ------- ---- ---- ------ ----\n");
150 }
151
152 totalUncLen = totalCompLen = 0;
153
154 count = zip->getNumEntries();
155 for (i = 0; i < count; i++) {
156 entry = zip->getEntryByIndex(i);
157 if (bundle->getVerbose()) {
158 char dateBuf[32];
159 time_t when;
160
161 when = entry->getModWhen();
162 strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M",
163 localtime(&when));
164
165 printf("%8ld %-7.7s %7ld %3d%% %8zd %s %08lx %s\n",
166 (long) entry->getUncompressedLen(),
167 compressionName(entry->getCompressionMethod()),
168 (long) entry->getCompressedLen(),
169 calcPercent(entry->getUncompressedLen(),
170 entry->getCompressedLen()),
171 (size_t) entry->getLFHOffset(),
172 dateBuf,
173 entry->getCRC32(),
174 entry->getFileName());
175 } else {
176 printf("%s\n", entry->getFileName());
177 }
178
179 totalUncLen += entry->getUncompressedLen();
180 totalCompLen += entry->getCompressedLen();
181 }
182
183 if (bundle->getVerbose()) {
184 printf(
185 "-------- ------- --- -------\n");
186 printf("%8ld %7ld %2d%% %d files\n",
187 totalUncLen,
188 totalCompLen,
189 calcPercent(totalUncLen, totalCompLen),
190 zip->getNumEntries());
191 }
192
193 if (bundle->getAndroidList()) {
194 AssetManager assets;
195 if (!assets.addAssetPath(String8(zipFileName), NULL)) {
196 fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n");
197 goto bail;
198 }
199
Elliott Hughesba3fe562015-08-12 14:49:53 -0700200#ifdef __ANDROID__
Andreas Gampeb8dc7bc2014-10-01 19:07:51 -0700201 static const bool kHaveAndroidOs = true;
202#else
203 static const bool kHaveAndroidOs = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800204#endif
Andreas Gampeb8dc7bc2014-10-01 19:07:51 -0700205 const ResTable& res = assets.getResources(false);
206 if (!kHaveAndroidOs) {
207 printf("\nResource table:\n");
208 res.print(false);
209 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800210
211 Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
212 Asset::ACCESS_BUFFER);
213 if (manifestAsset == NULL) {
214 printf("\nNo AndroidManifest.xml found.\n");
215 } else {
216 printf("\nAndroid manifest:\n");
217 ResXMLTree tree;
218 tree.setTo(manifestAsset->getBuffer(true),
219 manifestAsset->getLength());
220 printXMLBlock(&tree);
221 }
222 delete manifestAsset;
223 }
224
225 result = 0;
226
227bail:
228 delete zip;
229 return result;
230}
231
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700232static void printResolvedResourceAttribute(const ResTable& resTable, const ResXMLTree& tree,
Chih-Hung Hsieh9b8528f2016-08-10 14:15:30 -0700233 uint32_t attrRes, const String8& attrLabel, String8* outError)
Maurice Chu76327312013-10-16 18:28:46 -0700234{
235 Res_value value;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700236 AaptXml::getResolvedResourceAttribute(resTable, tree, attrRes, &value, outError);
Maurice Chu76327312013-10-16 18:28:46 -0700237 if (*outError != "") {
238 *outError = "error print resolved resource attribute";
239 return;
240 }
241 if (value.dataType == Res_value::TYPE_STRING) {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700242 String8 result = AaptXml::getResolvedAttribute(resTable, tree, attrRes, outError);
Maurice Chu2675f762013-10-22 17:33:11 -0700243 printf("%s='%s'", attrLabel.string(),
244 ResTable::normalizeForOutput(result.string()).string());
Maurice Chu76327312013-10-16 18:28:46 -0700245 } else if (Res_value::TYPE_FIRST_INT <= value.dataType &&
246 value.dataType <= Res_value::TYPE_LAST_INT) {
247 printf("%s='%d'", attrLabel.string(), value.data);
248 } else {
249 printf("%s='0x%x'", attrLabel.string(), (int)value.data);
250 }
251}
252
Adam Lesinski282e1812014-01-23 18:17:42 -0800253// These are attribute resource constants for the platform, as found
254// in android.R.attr
255enum {
256 LABEL_ATTR = 0x01010001,
257 ICON_ATTR = 0x01010002,
258 NAME_ATTR = 0x01010003,
Adam Lesinskia5018c92013-09-30 16:23:15 -0700259 PERMISSION_ATTR = 0x01010006,
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700260 EXPORTED_ATTR = 0x01010010,
261 GRANT_URI_PERMISSIONS_ATTR = 0x0101001b,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700262 RESOURCE_ATTR = 0x01010025,
Adam Lesinski282e1812014-01-23 18:17:42 -0800263 DEBUGGABLE_ATTR = 0x0101000f,
264 VALUE_ATTR = 0x01010024,
265 VERSION_CODE_ATTR = 0x0101021b,
266 VERSION_NAME_ATTR = 0x0101021c,
267 SCREEN_ORIENTATION_ATTR = 0x0101001e,
268 MIN_SDK_VERSION_ATTR = 0x0101020c,
269 MAX_SDK_VERSION_ATTR = 0x01010271,
270 REQ_TOUCH_SCREEN_ATTR = 0x01010227,
271 REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
272 REQ_HARD_KEYBOARD_ATTR = 0x01010229,
273 REQ_NAVIGATION_ATTR = 0x0101022a,
274 REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
275 TARGET_SDK_VERSION_ATTR = 0x01010270,
276 TEST_ONLY_ATTR = 0x01010272,
277 ANY_DENSITY_ATTR = 0x0101026c,
278 GL_ES_VERSION_ATTR = 0x01010281,
279 SMALL_SCREEN_ATTR = 0x01010284,
280 NORMAL_SCREEN_ATTR = 0x01010285,
281 LARGE_SCREEN_ATTR = 0x01010286,
282 XLARGE_SCREEN_ATTR = 0x010102bf,
283 REQUIRED_ATTR = 0x0101028e,
Adam Lesinskicaf797c2014-08-22 12:56:26 -0700284 INSTALL_LOCATION_ATTR = 0x010102b7,
Adam Lesinski282e1812014-01-23 18:17:42 -0800285 SCREEN_SIZE_ATTR = 0x010102ca,
286 SCREEN_DENSITY_ATTR = 0x010102cb,
287 REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
288 COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
289 LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
290 PUBLIC_KEY_ATTR = 0x010103a6,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700291 CATEGORY_ATTR = 0x010103e8,
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800292 BANNER_ATTR = 0x10103f2,
Tim Kilbournd9b1cad2014-10-24 12:43:41 -0700293 ISGAME_ATTR = 0x10103f4,
Dianne Hackborncd154e92017-02-28 17:37:35 -0800294 REQUIRED_FEATURE_ATTR = 0x1010557,
295 REQUIRED_NOT_FEATURE_ATTR = 0x1010558,
Adam Lesinski282e1812014-01-23 18:17:42 -0800296};
297
Maurice Chu2675f762013-10-22 17:33:11 -0700298String8 getComponentName(String8 &pkgName, String8 &componentName) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800299 ssize_t idx = componentName.find(".");
300 String8 retStr(pkgName);
301 if (idx == 0) {
302 retStr += componentName;
303 } else if (idx < 0) {
304 retStr += ".";
305 retStr += componentName;
306 } else {
Maurice Chu2675f762013-10-22 17:33:11 -0700307 return componentName;
Adam Lesinski282e1812014-01-23 18:17:42 -0800308 }
Maurice Chu2675f762013-10-22 17:33:11 -0700309 return retStr;
Adam Lesinski282e1812014-01-23 18:17:42 -0800310}
311
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700312static void printCompatibleScreens(ResXMLTree& tree, String8* outError) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800313 size_t len;
314 ResXMLTree::event_code_t code;
315 int depth = 0;
316 bool first = true;
317 printf("compatible-screens:");
318 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
319 if (code == ResXMLTree::END_TAG) {
320 depth--;
321 if (depth < 0) {
322 break;
323 }
324 continue;
325 }
326 if (code != ResXMLTree::START_TAG) {
327 continue;
328 }
329 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700330 const char16_t* ctag16 = tree.getElementName(&len);
331 if (ctag16 == NULL) {
332 *outError = "failed to get XML element name (bad string pool)";
333 return;
334 }
335 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800336 if (tag == "screen") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700337 int32_t screenSize = AaptXml::getIntegerAttribute(tree,
338 SCREEN_SIZE_ATTR);
339 int32_t screenDensity = AaptXml::getIntegerAttribute(tree,
340 SCREEN_DENSITY_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -0800341 if (screenSize > 0 && screenDensity > 0) {
342 if (!first) {
343 printf(",");
344 }
345 first = false;
346 printf("'%d/%d'", screenSize, screenDensity);
347 }
348 }
349 }
350 printf("\n");
351}
352
Dianne Hackborncd154e92017-02-28 17:37:35 -0800353static void printUsesPermission(const String8& name, bool optional=false, int maxSdkVersion=-1,
354 const String8& requiredFeature = String8::empty(),
355 const String8& requiredNotFeature = String8::empty()) {
Adam Lesinski58f1f362013-11-12 12:59:08 -0800356 printf("uses-permission: name='%s'", ResTable::normalizeForOutput(name.string()).string());
357 if (maxSdkVersion != -1) {
358 printf(" maxSdkVersion='%d'", maxSdkVersion);
359 }
Dianne Hackborncd154e92017-02-28 17:37:35 -0800360 if (requiredFeature.length() > 0) {
361 printf(" requiredFeature='%s'", requiredFeature.string());
362 }
363 if (requiredNotFeature.length() > 0) {
364 printf(" requiredNotFeature='%s'", requiredNotFeature.string());
365 }
Adam Lesinski58f1f362013-11-12 12:59:08 -0800366 printf("\n");
367
368 if (optional) {
369 printf("optional-permission: name='%s'",
370 ResTable::normalizeForOutput(name.string()).string());
371 if (maxSdkVersion != -1) {
372 printf(" maxSdkVersion='%d'", maxSdkVersion);
373 }
374 printf("\n");
375 }
376}
377
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800378static void printUsesPermissionSdk23(const String8& name, int maxSdkVersion=-1) {
379 printf("uses-permission-sdk-23: ");
380
381 printf("name='%s'", ResTable::normalizeForOutput(name.string()).string());
382 if (maxSdkVersion != -1) {
383 printf(" maxSdkVersion='%d'", maxSdkVersion);
384 }
385 printf("\n");
386}
387
Adam Lesinski2386df22016-12-28 15:08:58 -0500388static void printUsesImpliedPermission(const String8& name, const String8& reason,
389 const int32_t maxSdkVersion = -1) {
390 printf("uses-implied-permission: name='%s'",
391 ResTable::normalizeForOutput(name.string()).string());
392 if (maxSdkVersion != -1) {
393 printf(" maxSdkVersion='%d'", maxSdkVersion);
394 }
395 printf(" reason='%s'\n", ResTable::normalizeForOutput(reason.string()).string());
Adam Lesinski58f1f362013-11-12 12:59:08 -0800396}
397
Chih-Hung Hsieh9b8528f2016-08-10 14:15:30 -0700398Vector<String8> getNfcAidCategories(AssetManager& assets, const String8& xmlPath, bool offHost,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700399 String8 *outError = NULL)
400{
401 Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER);
402 if (aidAsset == NULL) {
403 if (outError != NULL) *outError = "xml resource does not exist";
404 return Vector<String8>();
405 }
406
407 const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service");
408
409 bool withinApduService = false;
410 Vector<String8> categories;
411
412 String8 error;
413 ResXMLTree tree;
414 tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength());
415
416 size_t len;
417 int depth = 0;
418 ResXMLTree::event_code_t code;
419 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
420 if (code == ResXMLTree::END_TAG) {
421 depth--;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700422 const char16_t* ctag16 = tree.getElementName(&len);
423 if (ctag16 == NULL) {
424 *outError = "failed to get XML element name (bad string pool)";
425 return Vector<String8>();
426 }
427 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700428
429 if (depth == 0 && tag == serviceTagName) {
430 withinApduService = false;
431 }
432
433 } else if (code == ResXMLTree::START_TAG) {
434 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700435 const char16_t* ctag16 = tree.getElementName(&len);
436 if (ctag16 == NULL) {
437 *outError = "failed to get XML element name (bad string pool)";
438 return Vector<String8>();
439 }
440 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700441
442 if (depth == 1) {
443 if (tag == serviceTagName) {
444 withinApduService = true;
445 }
446 } else if (depth == 2 && withinApduService) {
447 if (tag == "aid-group") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700448 String8 category = AaptXml::getAttribute(tree, CATEGORY_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700449 if (error != "") {
450 if (outError != NULL) *outError = error;
451 return Vector<String8>();
452 }
453
454 categories.add(category);
455 }
456 }
457 }
458 }
459 aidAsset->close();
460 return categories;
461}
462
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700463static void printComponentPresence(const char* componentName) {
464 printf("provides-component:'%s'\n", componentName);
465}
466
Adam Lesinski2c72b682014-06-24 09:56:01 -0700467/**
468 * Represents a feature that has been automatically added due to
469 * a pre-requisite or some other reason.
470 */
471struct ImpliedFeature {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800472 ImpliedFeature() : impliedBySdk23(false) {}
473 ImpliedFeature(const String8& n, bool sdk23) : name(n), impliedBySdk23(sdk23) {}
474
Adam Lesinski2c72b682014-06-24 09:56:01 -0700475 /**
476 * Name of the implied feature.
477 */
478 String8 name;
479
480 /**
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800481 * Was this implied by a permission from SDK 23 (<uses-permission-sdk-23 />)?
482 */
483 bool impliedBySdk23;
484
485 /**
Adam Lesinski2c72b682014-06-24 09:56:01 -0700486 * List of human-readable reasons for why this feature was implied.
487 */
488 SortedVector<String8> reasons;
489};
490
Adam Lesinski694d0a72016-04-06 16:12:04 -0700491struct Feature {
492 Feature() : required(false), version(-1) {}
Chih-Hung Hsiehd53e3be2016-05-03 10:02:51 -0700493 explicit Feature(bool required, int32_t version = -1) : required(required), version(version) {}
Adam Lesinski694d0a72016-04-06 16:12:04 -0700494
495 /**
496 * Whether the feature is required.
497 */
498 bool required;
499
500 /**
501 * What version of the feature is requested.
502 */
503 int32_t version;
504};
505
Adam Lesinski2c72b682014-06-24 09:56:01 -0700506/**
507 * Represents a <feature-group> tag in the AndroidManifest.xml
508 */
509struct FeatureGroup {
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700510 FeatureGroup() : openGLESVersion(-1) {}
511
Adam Lesinski2c72b682014-06-24 09:56:01 -0700512 /**
513 * Human readable label
514 */
515 String8 label;
516
517 /**
518 * Explicit features defined in the group
519 */
Adam Lesinski694d0a72016-04-06 16:12:04 -0700520 KeyedVector<String8, Feature> features;
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700521
522 /**
523 * OpenGL ES version required
524 */
525 int openGLESVersion;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700526};
527
Adam Lesinskica955a42016-08-01 16:44:29 -0700528static bool hasFeature(const char* name, const FeatureGroup& grp,
529 const KeyedVector<String8, ImpliedFeature>& implied) {
530 String8 name8(name);
531 ssize_t idx = grp.features.indexOfKey(name8);
532 if (idx < 0) {
533 idx = implied.indexOfKey(name8);
534 }
535 return idx >= 0;
536}
537
Adam Lesinski2c72b682014-06-24 09:56:01 -0700538static void addImpliedFeature(KeyedVector<String8, ImpliedFeature>* impliedFeatures,
Adam Lesinski43158772015-11-11 15:13:55 -0800539 const char* name, const String8& reason, bool sdk23) {
Adam Lesinski2c72b682014-06-24 09:56:01 -0700540 String8 name8(name);
541 ssize_t idx = impliedFeatures->indexOfKey(name8);
542 if (idx < 0) {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800543 idx = impliedFeatures->add(name8, ImpliedFeature(name8, sdk23));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700544 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800545
546 ImpliedFeature* feature = &impliedFeatures->editValueAt(idx);
547
548 // A non-sdk 23 implied feature takes precedence.
549 if (feature->impliedBySdk23 && !sdk23) {
550 feature->impliedBySdk23 = false;
551 }
Adam Lesinski43158772015-11-11 15:13:55 -0800552 feature->reasons.add(reason);
Adam Lesinski2c72b682014-06-24 09:56:01 -0700553}
554
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800555static void printFeatureGroupImpl(const FeatureGroup& grp,
556 const KeyedVector<String8, ImpliedFeature>* impliedFeatures) {
Adam Lesinski2c72b682014-06-24 09:56:01 -0700557 printf("feature-group: label='%s'\n", grp.label.string());
558
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700559 if (grp.openGLESVersion > 0) {
560 printf(" uses-gl-es: '0x%x'\n", grp.openGLESVersion);
561 }
562
Adam Lesinski2c72b682014-06-24 09:56:01 -0700563 const size_t numFeatures = grp.features.size();
564 for (size_t i = 0; i < numFeatures; i++) {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700565 const Feature& feature = grp.features[i];
566 const bool required = feature.required;
567 const int32_t version = feature.version;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700568
569 const String8& featureName = grp.features.keyAt(i);
Adam Lesinski694d0a72016-04-06 16:12:04 -0700570 printf(" uses-feature%s: name='%s'", (required ? "" : "-not-required"),
Adam Lesinski2c72b682014-06-24 09:56:01 -0700571 ResTable::normalizeForOutput(featureName.string()).string());
Adam Lesinski694d0a72016-04-06 16:12:04 -0700572
573 if (version > 0) {
574 printf(" version='%d'", version);
575 }
576 printf("\n");
Adam Lesinski2c72b682014-06-24 09:56:01 -0700577 }
578
579 const size_t numImpliedFeatures =
580 (impliedFeatures != NULL) ? impliedFeatures->size() : 0;
581 for (size_t i = 0; i < numImpliedFeatures; i++) {
582 const ImpliedFeature& impliedFeature = impliedFeatures->valueAt(i);
583 if (grp.features.indexOfKey(impliedFeature.name) >= 0) {
584 // The feature is explicitly set, no need to use implied
585 // definition.
586 continue;
587 }
588
589 String8 printableFeatureName(ResTable::normalizeForOutput(
590 impliedFeature.name.string()));
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800591 const char* sdk23Suffix = impliedFeature.impliedBySdk23 ? "-sdk-23" : "";
592
593 printf(" uses-feature%s: name='%s'\n", sdk23Suffix, printableFeatureName.string());
594 printf(" uses-implied-feature%s: name='%s' reason='", sdk23Suffix,
595 printableFeatureName.string());
Adam Lesinski2c72b682014-06-24 09:56:01 -0700596 const size_t numReasons = impliedFeature.reasons.size();
597 for (size_t j = 0; j < numReasons; j++) {
598 printf("%s", impliedFeature.reasons[j].string());
599 if (j + 2 < numReasons) {
600 printf(", ");
601 } else if (j + 1 < numReasons) {
602 printf(", and ");
603 }
604 }
605 printf("'\n");
606 }
607}
608
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800609static void printFeatureGroup(const FeatureGroup& grp) {
610 printFeatureGroupImpl(grp, NULL);
611}
612
613static void printDefaultFeatureGroup(const FeatureGroup& grp,
614 const KeyedVector<String8, ImpliedFeature>& impliedFeatures) {
615 printFeatureGroupImpl(grp, &impliedFeatures);
616}
617
Adam Lesinski2c72b682014-06-24 09:56:01 -0700618static void addParentFeatures(FeatureGroup* grp, const String8& name) {
619 if (name == "android.hardware.camera.autofocus" ||
620 name == "android.hardware.camera.flash") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700621 grp->features.add(String8("android.hardware.camera"), Feature(true));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700622 } else if (name == "android.hardware.location.gps" ||
623 name == "android.hardware.location.network") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700624 grp->features.add(String8("android.hardware.location"), Feature(true));
Adam Lesinskica955a42016-08-01 16:44:29 -0700625 } else if (name == "android.hardware.faketouch.multitouch") {
626 grp->features.add(String8("android.hardware.faketouch"), Feature(true));
627 } else if (name == "android.hardware.faketouch.multitouch.distinct" ||
628 name == "android.hardware.faketouch.multitouch.jazzhands") {
629 grp->features.add(String8("android.hardware.faketouch.multitouch"), Feature(true));
630 grp->features.add(String8("android.hardware.faketouch"), Feature(true));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700631 } else if (name == "android.hardware.touchscreen.multitouch") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700632 grp->features.add(String8("android.hardware.touchscreen"), Feature(true));
Adam Lesinskica955a42016-08-01 16:44:29 -0700633 } else if (name == "android.hardware.touchscreen.multitouch.distinct" ||
634 name == "android.hardware.touchscreen.multitouch.jazzhands") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700635 grp->features.add(String8("android.hardware.touchscreen.multitouch"), Feature(true));
636 grp->features.add(String8("android.hardware.touchscreen"), Feature(true));
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700637 } else if (name == "android.hardware.opengles.aep") {
638 const int openGLESVersion31 = 0x00030001;
639 if (openGLESVersion31 > grp->openGLESVersion) {
640 grp->openGLESVersion = openGLESVersion31;
641 }
Adam Lesinski2c72b682014-06-24 09:56:01 -0700642 }
643}
644
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800645static void addImpliedFeaturesForPermission(const int targetSdk, const String8& name,
646 KeyedVector<String8, ImpliedFeature>* impliedFeatures,
647 bool impliedBySdk23Permission) {
648 if (name == "android.permission.CAMERA") {
649 addImpliedFeature(impliedFeatures, "android.hardware.camera",
Adam Lesinski43158772015-11-11 15:13:55 -0800650 String8::format("requested %s permission", name.string()),
651 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800652 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
Adam Lesinski43158772015-11-11 15:13:55 -0800653 if (targetSdk < SDK_LOLLIPOP) {
654 addImpliedFeature(impliedFeatures, "android.hardware.location.gps",
655 String8::format("requested %s permission", name.string()),
656 impliedBySdk23Permission);
657 addImpliedFeature(impliedFeatures, "android.hardware.location.gps",
658 String8::format("targetSdkVersion < %d", SDK_LOLLIPOP),
659 impliedBySdk23Permission);
660 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800661 addImpliedFeature(impliedFeatures, "android.hardware.location",
Adam Lesinski43158772015-11-11 15:13:55 -0800662 String8::format("requested %s permission", name.string()),
663 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800664 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
Adam Lesinski43158772015-11-11 15:13:55 -0800665 if (targetSdk < SDK_LOLLIPOP) {
666 addImpliedFeature(impliedFeatures, "android.hardware.location.network",
667 String8::format("requested %s permission", name.string()),
668 impliedBySdk23Permission);
669 addImpliedFeature(impliedFeatures, "android.hardware.location.network",
670 String8::format("targetSdkVersion < %d", SDK_LOLLIPOP),
671 impliedBySdk23Permission);
672 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800673 addImpliedFeature(impliedFeatures, "android.hardware.location",
Adam Lesinski43158772015-11-11 15:13:55 -0800674 String8::format("requested %s permission", name.string()),
675 impliedBySdk23Permission);
676 } else if (name == "android.permission.ACCESS_MOCK_LOCATION" ||
677 name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800678 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
679 addImpliedFeature(impliedFeatures, "android.hardware.location",
Adam Lesinski43158772015-11-11 15:13:55 -0800680 String8::format("requested %s permission", name.string()),
681 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800682 } else if (name == "android.permission.BLUETOOTH" ||
683 name == "android.permission.BLUETOOTH_ADMIN") {
Adam Lesinski43158772015-11-11 15:13:55 -0800684 if (targetSdk > SDK_DONUT) {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800685 addImpliedFeature(impliedFeatures, "android.hardware.bluetooth",
Adam Lesinski43158772015-11-11 15:13:55 -0800686 String8::format("requested %s permission", name.string()),
687 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800688 addImpliedFeature(impliedFeatures, "android.hardware.bluetooth",
Adam Lesinski43158772015-11-11 15:13:55 -0800689 String8::format("targetSdkVersion > %d", SDK_DONUT),
690 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800691 }
692 } else if (name == "android.permission.RECORD_AUDIO") {
693 addImpliedFeature(impliedFeatures, "android.hardware.microphone",
Adam Lesinski43158772015-11-11 15:13:55 -0800694 String8::format("requested %s permission", name.string()),
695 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800696 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
697 name == "android.permission.CHANGE_WIFI_STATE" ||
698 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
699 addImpliedFeature(impliedFeatures, "android.hardware.wifi",
Adam Lesinski43158772015-11-11 15:13:55 -0800700 String8::format("requested %s permission", name.string()),
701 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800702 } else if (name == "android.permission.CALL_PHONE" ||
703 name == "android.permission.CALL_PRIVILEGED" ||
704 name == "android.permission.MODIFY_PHONE_STATE" ||
705 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
706 name == "android.permission.READ_SMS" ||
707 name == "android.permission.RECEIVE_SMS" ||
708 name == "android.permission.RECEIVE_MMS" ||
709 name == "android.permission.RECEIVE_WAP_PUSH" ||
710 name == "android.permission.SEND_SMS" ||
711 name == "android.permission.WRITE_APN_SETTINGS" ||
712 name == "android.permission.WRITE_SMS") {
713 addImpliedFeature(impliedFeatures, "android.hardware.telephony",
Adam Lesinski43158772015-11-11 15:13:55 -0800714 String8("requested a telephony permission"),
715 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800716 }
717}
718
Adam Lesinski282e1812014-01-23 18:17:42 -0800719/*
720 * Handle the "dump" command, to extract select data from an archive.
721 */
722extern char CONSOLE_DATA[2925]; // see EOF
723int doDump(Bundle* bundle)
724{
725 status_t result = UNKNOWN_ERROR;
Adam Lesinski282e1812014-01-23 18:17:42 -0800726
727 if (bundle->getFileSpecCount() < 1) {
728 fprintf(stderr, "ERROR: no dump option specified\n");
729 return 1;
730 }
731
732 if (bundle->getFileSpecCount() < 2) {
733 fprintf(stderr, "ERROR: no dump file specified\n");
734 return 1;
735 }
736
737 const char* option = bundle->getFileSpecEntry(0);
738 const char* filename = bundle->getFileSpecEntry(1);
739
740 AssetManager assets;
Narayan Kamathf85e41f2014-01-24 14:14:30 +0000741 int32_t assetsCookie;
Adam Lesinski282e1812014-01-23 18:17:42 -0800742
Donald Chaid1ac6e12017-10-12 21:00:45 -0700743 // Add any dependencies passed in.
Adam Lesinski57fe4832017-05-10 15:42:22 -0700744 for (size_t i = 0; i < bundle->getPackageIncludes().size(); i++) {
745 const String8& assetPath = bundle->getPackageIncludes()[i];
746 if (!assets.addAssetPath(assetPath, NULL)) {
747 fprintf(stderr, "ERROR: included asset path %s could not be loaded\n", assetPath.string());
748 return 1;
749 }
750 }
751
Donald Chaid1ac6e12017-10-12 21:00:45 -0700752 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
753 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
754 return 1;
755 }
756
Adam Lesinski282e1812014-01-23 18:17:42 -0800757 // Make a dummy config for retrieving resources... we need to supply
758 // non-default values for some configs so that we can retrieve resources
759 // in the app that don't have a default. The most important of these is
760 // the API version because key resources like icons will have an implicit
761 // version if they are using newer config types like density.
762 ResTable_config config;
Narayan Kamath91447d82014-01-21 15:32:36 +0000763 memset(&config, 0, sizeof(ResTable_config));
Adam Lesinski282e1812014-01-23 18:17:42 -0800764 config.language[0] = 'e';
765 config.language[1] = 'n';
766 config.country[0] = 'U';
767 config.country[1] = 'S';
768 config.orientation = ResTable_config::ORIENTATION_PORT;
769 config.density = ResTable_config::DENSITY_MEDIUM;
770 config.sdkVersion = 10000; // Very high.
771 config.screenWidthDp = 320;
772 config.screenHeightDp = 480;
773 config.smallestScreenWidthDp = 320;
Adam Lesinskic2dea8d2014-08-04 16:40:41 -0700774 config.screenLayout |= ResTable_config::SCREENSIZE_NORMAL;
Adam Lesinski282e1812014-01-23 18:17:42 -0800775 assets.setConfiguration(config);
776
777 const ResTable& res = assets.getResources(false);
Dan Albert68001652014-09-09 09:51:01 -0700778 if (res.getError() != NO_ERROR) {
Adam Lesinski25e9d552014-05-19 15:01:43 -0700779 fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
Adam Lesinski63e646e2014-07-30 11:40:39 -0700780 return 1;
Adam Lesinski282e1812014-01-23 18:17:42 -0800781 }
782
Adam Lesinski694d0a72016-04-06 16:12:04 -0700783 // Source for AndroidManifest.xml
Adam Lesinski10de3af12016-07-13 10:14:03 -0700784 const String8 manifestFile("AndroidManifest.xml");
Adam Lesinski694d0a72016-04-06 16:12:04 -0700785
Adam Lesinski2cb761e2014-08-15 13:59:02 -0700786 // The dynamicRefTable can be null if there are no resources for this asset cookie.
787 // This fine.
Adam Lesinski63e646e2014-07-30 11:40:39 -0700788 const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700789
790 Asset* asset = NULL;
791
Adam Lesinski282e1812014-01-23 18:17:42 -0800792 if (strcmp("resources", option) == 0) {
Elliott Hughesba3fe562015-08-12 14:49:53 -0700793#ifndef __ANDROID__
Adam Lesinski282e1812014-01-23 18:17:42 -0800794 res.print(bundle->getValues());
795#endif
796
797 } else if (strcmp("strings", option) == 0) {
798 const ResStringPool* pool = res.getTableStringBlock(0);
799 printStringPool(pool);
800
801 } else if (strcmp("xmltree", option) == 0) {
802 if (bundle->getFileSpecCount() < 3) {
803 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
804 goto bail;
805 }
806
807 for (int i=2; i<bundle->getFileSpecCount(); i++) {
808 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700809 ResXMLTree tree(dynamicRefTable);
810 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800811 if (asset == NULL) {
Adam Lesinskifcb5f7b2016-11-02 13:17:10 -0700812 fprintf(stderr, "ERROR: dump failed because resource %s not found\n", resname);
Adam Lesinski282e1812014-01-23 18:17:42 -0800813 goto bail;
814 }
815
816 if (tree.setTo(asset->getBuffer(true),
817 asset->getLength()) != NO_ERROR) {
818 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
819 goto bail;
820 }
821 tree.restart();
822 printXMLBlock(&tree);
823 tree.uninit();
824 delete asset;
825 asset = NULL;
826 }
827
828 } else if (strcmp("xmlstrings", option) == 0) {
829 if (bundle->getFileSpecCount() < 3) {
830 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
831 goto bail;
832 }
833
834 for (int i=2; i<bundle->getFileSpecCount(); i++) {
835 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700836 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800837 if (asset == NULL) {
838 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
839 goto bail;
840 }
841
Adam Lesinski63e646e2014-07-30 11:40:39 -0700842 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800843 if (tree.setTo(asset->getBuffer(true),
844 asset->getLength()) != NO_ERROR) {
845 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
846 goto bail;
847 }
848 printStringPool(&tree.getStrings());
849 delete asset;
850 asset = NULL;
851 }
852
853 } else {
Adam Lesinski63e646e2014-07-30 11:40:39 -0700854 asset = assets.openNonAsset(assetsCookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800855 if (asset == NULL) {
856 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
857 goto bail;
858 }
859
Adam Lesinski63e646e2014-07-30 11:40:39 -0700860 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800861 if (tree.setTo(asset->getBuffer(true),
862 asset->getLength()) != NO_ERROR) {
863 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
864 goto bail;
865 }
866 tree.restart();
867
868 if (strcmp("permissions", option) == 0) {
869 size_t len;
870 ResXMLTree::event_code_t code;
871 int depth = 0;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800872 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
873 code != ResXMLTree::BAD_DOCUMENT) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800874 if (code == ResXMLTree::END_TAG) {
875 depth--;
876 continue;
877 }
878 if (code != ResXMLTree::START_TAG) {
879 continue;
880 }
881 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700882 const char16_t* ctag16 = tree.getElementName(&len);
883 if (ctag16 == NULL) {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700884 SourcePos(manifestFile, tree.getLineNumber()).error(
885 "ERROR: failed to get XML element name (bad string pool)");
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700886 goto bail;
887 }
888 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800889 //printf("Depth %d tag %s\n", depth, tag.string());
890 if (depth == 1) {
891 if (tag != "manifest") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700892 SourcePos(manifestFile, tree.getLineNumber()).error(
893 "ERROR: manifest does not start with <manifest> tag");
Adam Lesinski282e1812014-01-23 18:17:42 -0800894 goto bail;
895 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700896 String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -0700897 printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800898 } else if (depth == 2) {
899 if (tag == "permission") {
900 String8 error;
901 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
902 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700903 SourcePos(manifestFile, tree.getLineNumber()).error(
904 "ERROR getting 'android:name': %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800905 goto bail;
906 }
907
908 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700909 SourcePos(manifestFile, tree.getLineNumber()).error(
910 "ERROR: missing 'android:name' for permission");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800911 goto bail;
912 }
913 printf("permission: %s\n",
914 ResTable::normalizeForOutput(name.string()).string());
915 } else if (tag == "uses-permission") {
916 String8 error;
917 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
918 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700919 SourcePos(manifestFile, tree.getLineNumber()).error(
920 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800921 goto bail;
922 }
923
924 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700925 SourcePos(manifestFile, tree.getLineNumber()).error(
926 "ERROR: missing 'android:name' for uses-permission");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800927 goto bail;
928 }
929 printUsesPermission(name,
930 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
931 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
932 } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
933 String8 error;
934 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
935 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700936 SourcePos(manifestFile, tree.getLineNumber()).error(
937 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800938 goto bail;
939 }
940
941 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700942 SourcePos(manifestFile, tree.getLineNumber()).error(
943 "ERROR: missing 'android:name' for uses-permission-sdk-23");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800944 goto bail;
945 }
946 printUsesPermissionSdk23(
947 name,
948 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
Adam Lesinski282e1812014-01-23 18:17:42 -0800949 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800950 }
951 }
952 } else if (strcmp("badging", option) == 0) {
953 Vector<String8> locales;
954 res.getLocales(&locales);
955
956 Vector<ResTable_config> configs;
957 res.getConfigurations(&configs);
958 SortedVector<int> densities;
959 const size_t NC = configs.size();
960 for (size_t i=0; i<NC; i++) {
961 int dens = configs[i].density;
962 if (dens == 0) {
963 dens = 160;
964 }
965 densities.add(dens);
966 }
967
968 size_t len;
969 ResXMLTree::event_code_t code;
970 int depth = 0;
971 String8 error;
972 bool withinActivity = false;
973 bool isMainActivity = false;
974 bool isLauncherActivity = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800975 bool isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800976 bool isSearchable = false;
977 bool withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700978 bool withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700979 bool withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800980 bool withinReceiver = false;
981 bool withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700982 bool withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800983 bool withinIntentFilter = false;
984 bool hasMainActivity = false;
985 bool hasOtherActivities = false;
986 bool hasOtherReceivers = false;
987 bool hasOtherServices = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700988 bool hasIntentFilter = false;
989
Adam Lesinski282e1812014-01-23 18:17:42 -0800990 bool hasWallpaperService = false;
991 bool hasImeService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700992 bool hasAccessibilityService = false;
993 bool hasPrintService = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800994 bool hasWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700995 bool hasDeviceAdminReceiver = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700996 bool hasPaymentService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700997 bool hasDocumentsProvider = false;
998 bool hasCameraActivity = false;
999 bool hasCameraSecureActivity = false;
1000 bool hasLauncher = false;
1001 bool hasNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001002 bool hasDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001003
Adam Lesinski282e1812014-01-23 18:17:42 -08001004 bool actMainActivity = false;
1005 bool actWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001006 bool actDeviceAdminEnabled = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001007 bool actImeService = false;
1008 bool actWallpaperService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001009 bool actAccessibilityService = false;
1010 bool actPrintService = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001011 bool actHostApduService = false;
1012 bool actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001013 bool actDocumentsProvider = false;
1014 bool actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001015 bool actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001016 bool actCamera = false;
1017 bool actCameraSecure = false;
1018 bool catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001019 bool hasMetaHostPaymentCategory = false;
1020 bool hasMetaOffHostPaymentCategory = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001021
1022 // These permissions are required by services implementing services
1023 // the system binds to (IME, Accessibility, PrintServices, etc.)
1024 bool hasBindDeviceAdminPermission = false;
1025 bool hasBindInputMethodPermission = false;
1026 bool hasBindAccessibilityServicePermission = false;
1027 bool hasBindPrintServicePermission = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001028 bool hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001029 bool hasRequiredSafAttributes = false;
1030 bool hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001031 bool hasBindDreamServicePermission = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001032
1033 // These two implement the implicit permissions that are granted
1034 // to pre-1.6 applications.
1035 bool hasWriteExternalStoragePermission = false;
Adam Lesinski2386df22016-12-28 15:08:58 -05001036 int32_t writeExternalStoragePermissionMaxSdkVersion = -1;
Adam Lesinski282e1812014-01-23 18:17:42 -08001037 bool hasReadPhoneStatePermission = false;
1038
1039 // If an app requests write storage, they will also get read storage.
1040 bool hasReadExternalStoragePermission = false;
1041
1042 // Implement transition to read and write call log.
1043 bool hasReadContactsPermission = false;
1044 bool hasWriteContactsPermission = false;
1045 bool hasReadCallLogPermission = false;
1046 bool hasWriteCallLogPermission = false;
1047
Adam Lesinskie47fd122014-08-15 22:25:36 -07001048 // If an app declares itself as multiArch, we report the
1049 // native libraries differently.
1050 bool hasMultiArch = false;
1051
Adam Lesinski282e1812014-01-23 18:17:42 -08001052 // This next group of variables is used to implement a group of
1053 // backward-compatibility heuristics necessitated by the addition of
1054 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
1055 // heuristic is "if an app requests a permission but doesn't explicitly
1056 // request the corresponding <uses-feature>, presume it's there anyway".
Adam Lesinski2c72b682014-06-24 09:56:01 -07001057
Adam Lesinski282e1812014-01-23 18:17:42 -08001058 // 2.2 also added some other features that apps can request, but that
1059 // have no corresponding permission, so we cannot implement any
1060 // back-compatibility heuristic for them. The below are thus unnecessary
1061 // (but are retained here for documentary purposes.)
1062 //bool specCompassFeature = false;
1063 //bool specAccelerometerFeature = false;
1064 //bool specProximityFeature = false;
1065 //bool specAmbientLightFeature = false;
1066 //bool specLiveWallpaperFeature = false;
1067
1068 int targetSdk = 0;
1069 int smallScreen = 1;
1070 int normalScreen = 1;
1071 int largeScreen = 1;
1072 int xlargeScreen = 1;
1073 int anyDensity = 1;
1074 int requiresSmallestWidthDp = 0;
1075 int compatibleWidthLimitDp = 0;
1076 int largestWidthLimitDp = 0;
1077 String8 pkg;
1078 String8 activityName;
1079 String8 activityLabel;
1080 String8 activityIcon;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001081 String8 activityBanner;
Adam Lesinski282e1812014-01-23 18:17:42 -08001082 String8 receiverName;
1083 String8 serviceName;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001084 Vector<String8> supportedInput;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001085
1086 FeatureGroup commonFeatures;
1087 Vector<FeatureGroup> featureGroups;
1088 KeyedVector<String8, ImpliedFeature> impliedFeatures;
1089
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001090 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
1091 code != ResXMLTree::BAD_DOCUMENT) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001092 if (code == ResXMLTree::END_TAG) {
1093 depth--;
1094 if (depth < 2) {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001095 if (withinSupportsInput && !supportedInput.isEmpty()) {
1096 printf("supports-input: '");
1097 const size_t N = supportedInput.size();
1098 for (size_t i=0; i<N; i++) {
Maurice Chu2675f762013-10-22 17:33:11 -07001099 printf("%s", ResTable::normalizeForOutput(
1100 supportedInput[i].string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001101 if (i != N - 1) {
1102 printf("' '");
1103 } else {
1104 printf("'\n");
1105 }
1106 }
1107 supportedInput.clear();
1108 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001109 withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001110 withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001111 withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001112 } else if (depth < 3) {
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001113 if (withinActivity && isMainActivity) {
Maurice Chu2675f762013-10-22 17:33:11 -07001114 String8 aName(getComponentName(pkg, activityName));
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001115 if (isLauncherActivity) {
1116 printf("launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001117 if (aName.length() > 0) {
1118 printf(" name='%s' ",
1119 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001120 }
1121 printf(" label='%s' icon='%s'\n",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001122 ResTable::normalizeForOutput(activityLabel.string())
1123 .string(),
1124 ResTable::normalizeForOutput(activityIcon.string())
1125 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001126 }
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001127 if (isLeanbackLauncherActivity) {
1128 printf("leanback-launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001129 if (aName.length() > 0) {
1130 printf(" name='%s' ",
1131 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001132 }
1133 printf(" label='%s' icon='%s' banner='%s'\n",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001134 ResTable::normalizeForOutput(activityLabel.string())
1135 .string(),
1136 ResTable::normalizeForOutput(activityIcon.string())
1137 .string(),
1138 ResTable::normalizeForOutput(activityBanner.string())
1139 .string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001140 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001141 }
1142 if (!hasIntentFilter) {
1143 hasOtherActivities |= withinActivity;
1144 hasOtherReceivers |= withinReceiver;
1145 hasOtherServices |= withinService;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001146 } else {
1147 if (withinService) {
1148 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
1149 hasBindNfcServicePermission);
1150 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
1151 hasBindNfcServicePermission);
1152 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001153 }
1154 withinActivity = false;
1155 withinService = false;
1156 withinReceiver = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001157 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001158 hasIntentFilter = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001159 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001160 } else if (depth < 4) {
1161 if (withinIntentFilter) {
1162 if (withinActivity) {
1163 hasMainActivity |= actMainActivity;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001164 hasLauncher |= catLauncher;
1165 hasCameraActivity |= actCamera;
1166 hasCameraSecureActivity |= actCameraSecure;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001167 hasOtherActivities |=
1168 !actMainActivity && !actCamera && !actCameraSecure;
Adam Lesinski282e1812014-01-23 18:17:42 -08001169 } else if (withinReceiver) {
1170 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001171 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
1172 hasBindDeviceAdminPermission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001173 hasOtherReceivers |=
1174 (!actWidgetReceivers && !actDeviceAdminEnabled);
Adam Lesinski282e1812014-01-23 18:17:42 -08001175 } else if (withinService) {
1176 hasImeService |= actImeService;
1177 hasWallpaperService |= actWallpaperService;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001178 hasAccessibilityService |= (actAccessibilityService &&
1179 hasBindAccessibilityServicePermission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001180 hasPrintService |=
1181 (actPrintService && hasBindPrintServicePermission);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001182 hasNotificationListenerService |= actNotificationListenerService &&
1183 hasBindNotificationListenerServicePermission;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001184 hasDreamService |= actDreamService && hasBindDreamServicePermission;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001185 hasOtherServices |= (!actImeService && !actWallpaperService &&
Adam Lesinski94fc9122013-09-30 17:16:09 -07001186 !actAccessibilityService && !actPrintService &&
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001187 !actHostApduService && !actOffHostApduService &&
1188 !actNotificationListenerService);
1189 } else if (withinProvider) {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001190 hasDocumentsProvider |=
1191 actDocumentsProvider && hasRequiredSafAttributes;
Adam Lesinski282e1812014-01-23 18:17:42 -08001192 }
1193 }
1194 withinIntentFilter = false;
1195 }
1196 continue;
1197 }
1198 if (code != ResXMLTree::START_TAG) {
1199 continue;
1200 }
1201 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001202
1203 const char16_t* ctag16 = tree.getElementName(&len);
1204 if (ctag16 == NULL) {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001205 SourcePos(manifestFile, tree.getLineNumber()).error(
1206 "ERROR: failed to get XML element name (bad string pool)");
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001207 goto bail;
1208 }
1209 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -08001210 //printf("Depth %d, %s\n", depth, tag.string());
1211 if (depth == 1) {
1212 if (tag != "manifest") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001213 SourcePos(manifestFile, tree.getLineNumber()).error(
1214 "ERROR: manifest does not start with <manifest> tag");
Adam Lesinski282e1812014-01-23 18:17:42 -08001215 goto bail;
1216 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001217 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -07001218 printf("package: name='%s' ",
1219 ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001220 int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR,
1221 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001222 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001223 SourcePos(manifestFile, tree.getLineNumber()).error(
1224 "ERROR getting 'android:versionCode' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001225 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001226 goto bail;
1227 }
1228 if (versionCode > 0) {
1229 printf("versionCode='%d' ", versionCode);
1230 } else {
1231 printf("versionCode='' ");
1232 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001233 String8 versionName = AaptXml::getResolvedAttribute(res, tree,
1234 VERSION_NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001235 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001236 SourcePos(manifestFile, tree.getLineNumber()).error(
1237 "ERROR getting 'android:versionName' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001238 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001239 goto bail;
1240 }
Adam Lesinski25d35a92014-08-11 09:41:56 -07001241 printf("versionName='%s'",
Maurice Chu2675f762013-10-22 17:33:11 -07001242 ResTable::normalizeForOutput(versionName.string()).string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001243
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001244 String8 splitName = AaptXml::getAttribute(tree, NULL, "split");
Adam Lesinski25d35a92014-08-11 09:41:56 -07001245 if (!splitName.isEmpty()) {
1246 printf(" split='%s'", ResTable::normalizeForOutput(
1247 splitName.string()).string());
1248 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001249
Adam Lesinski5283fab2014-08-29 11:23:55 -07001250 String8 platformVersionName = AaptXml::getAttribute(tree, NULL,
1251 "platformBuildVersionName");
1252 printf(" platformBuildVersionName='%s'", platformVersionName.string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001253 printf("\n");
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001254
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001255 int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
1256 INSTALL_LOCATION_ATTR, &error);
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001257 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001258 SourcePos(manifestFile, tree.getLineNumber()).error(
1259 "ERROR getting 'android:installLocation' attribute: %s",
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001260 error.string());
1261 goto bail;
1262 }
1263
1264 if (installLocation >= 0) {
1265 printf("install-location:'");
1266 switch (installLocation) {
1267 case 0:
1268 printf("auto");
1269 break;
1270 case 1:
1271 printf("internalOnly");
1272 break;
1273 case 2:
1274 printf("preferExternal");
1275 break;
1276 default:
1277 fprintf(stderr, "Invalid installLocation %d\n", installLocation);
1278 goto bail;
1279 }
1280 printf("'\n");
1281 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001282 } else if (depth == 2) {
1283 withinApplication = false;
1284 if (tag == "application") {
1285 withinApplication = true;
1286
1287 String8 label;
1288 const size_t NL = locales.size();
1289 for (size_t i=0; i<NL; i++) {
1290 const char* localeStr = locales[i].string();
Adam Lesinskia77685f2016-10-03 16:26:28 -07001291 assets.setConfiguration(config, localeStr != NULL ? localeStr : "");
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001292 String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1293 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001294 if (llabel != "") {
1295 if (localeStr == NULL || strlen(localeStr) == 0) {
1296 label = llabel;
Maurice Chu2675f762013-10-22 17:33:11 -07001297 printf("application-label:'%s'\n",
1298 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001299 } else {
1300 if (label == "") {
1301 label = llabel;
1302 }
1303 printf("application-label-%s:'%s'\n", localeStr,
Maurice Chu2675f762013-10-22 17:33:11 -07001304 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001305 }
1306 }
1307 }
1308
1309 ResTable_config tmpConfig = config;
1310 const size_t ND = densities.size();
1311 for (size_t i=0; i<ND; i++) {
1312 tmpConfig.density = densities[i];
1313 assets.setConfiguration(tmpConfig);
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001314 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1315 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001316 if (icon != "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001317 printf("application-icon-%d:'%s'\n", densities[i],
1318 ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001319 }
1320 }
1321 assets.setConfiguration(config);
1322
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001323 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001324 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001325 SourcePos(manifestFile, tree.getLineNumber()).error(
1326 "ERROR getting 'android:icon' attribute: %s", error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001327 goto bail;
1328 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001329 int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0,
1330 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001331 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001332 SourcePos(manifestFile, tree.getLineNumber()).error(
1333 "ERROR getting 'android:testOnly' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001334 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001335 goto bail;
1336 }
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001337
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001338 String8 banner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1339 &error);
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001340 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001341 SourcePos(manifestFile, tree.getLineNumber()).error(
1342 "ERROR getting 'android:banner' attribute: %s", error.string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001343 goto bail;
1344 }
Maurice Chu2675f762013-10-22 17:33:11 -07001345 printf("application: label='%s' ",
1346 ResTable::normalizeForOutput(label.string()).string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001347 printf("icon='%s'", ResTable::normalizeForOutput(icon.string()).string());
1348 if (banner != "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001349 printf(" banner='%s'",
1350 ResTable::normalizeForOutput(banner.string()).string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001351 }
1352 printf("\n");
Adam Lesinski282e1812014-01-23 18:17:42 -08001353 if (testOnly != 0) {
1354 printf("testOnly='%d'\n", testOnly);
1355 }
1356
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001357 int32_t isGame = AaptXml::getResolvedIntegerAttribute(res, tree,
1358 ISGAME_ATTR, 0, &error);
1359 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001360 SourcePos(manifestFile, tree.getLineNumber()).error(
1361 "ERROR getting 'android:isGame' attribute: %s", error.string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001362 goto bail;
1363 }
1364 if (isGame != 0) {
1365 printf("application-isGame\n");
1366 }
1367
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001368 int32_t debuggable = AaptXml::getResolvedIntegerAttribute(res, tree,
1369 DEBUGGABLE_ATTR, 0, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001370 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001371 SourcePos(manifestFile, tree.getLineNumber()).error(
1372 "ERROR getting 'android:debuggable' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001373 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001374 goto bail;
1375 }
1376 if (debuggable != 0) {
1377 printf("application-debuggable\n");
1378 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07001379
1380 // We must search by name because the multiArch flag hasn't been API
1381 // frozen yet.
1382 int32_t multiArchIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
1383 "multiArch");
1384 if (multiArchIndex >= 0) {
1385 Res_value value;
1386 if (tree.getAttributeValue(multiArchIndex, &value) != NO_ERROR) {
1387 if (value.dataType >= Res_value::TYPE_FIRST_INT &&
1388 value.dataType <= Res_value::TYPE_LAST_INT) {
1389 hasMultiArch = value.data;
1390 }
1391 }
1392 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001393 } else if (tag == "uses-sdk") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001394 int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR,
1395 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001396 if (error != "") {
1397 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001398 String8 name = AaptXml::getResolvedAttribute(res, tree,
1399 MIN_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001400 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001401 SourcePos(manifestFile, tree.getLineNumber()).error(
1402 "ERROR getting 'android:minSdkVersion' attribute: %s",
Adam Lesinski282e1812014-01-23 18:17:42 -08001403 error.string());
1404 goto bail;
1405 }
1406 if (name == "Donut") targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001407 printf("sdkVersion:'%s'\n",
1408 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001409 } else if (code != -1) {
1410 targetSdk = code;
1411 printf("sdkVersion:'%d'\n", code);
1412 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001413 code = AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -08001414 if (code != -1) {
1415 printf("maxSdkVersion:'%d'\n", code);
1416 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001417 code = AaptXml::getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001418 if (error != "") {
1419 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001420 String8 name = AaptXml::getResolvedAttribute(res, tree,
1421 TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001422 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001423 SourcePos(manifestFile, tree.getLineNumber()).error(
1424 "ERROR getting 'android:targetSdkVersion' attribute: %s",
Adam Lesinski282e1812014-01-23 18:17:42 -08001425 error.string());
1426 goto bail;
1427 }
1428 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001429 printf("targetSdkVersion:'%s'\n",
1430 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001431 } else if (code != -1) {
1432 if (targetSdk < code) {
1433 targetSdk = code;
1434 }
1435 printf("targetSdkVersion:'%d'\n", code);
1436 }
1437 } else if (tag == "uses-configuration") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001438 int32_t reqTouchScreen = AaptXml::getIntegerAttribute(tree,
1439 REQ_TOUCH_SCREEN_ATTR, 0);
1440 int32_t reqKeyboardType = AaptXml::getIntegerAttribute(tree,
1441 REQ_KEYBOARD_TYPE_ATTR, 0);
1442 int32_t reqHardKeyboard = AaptXml::getIntegerAttribute(tree,
1443 REQ_HARD_KEYBOARD_ATTR, 0);
1444 int32_t reqNavigation = AaptXml::getIntegerAttribute(tree,
1445 REQ_NAVIGATION_ATTR, 0);
1446 int32_t reqFiveWayNav = AaptXml::getIntegerAttribute(tree,
1447 REQ_FIVE_WAY_NAV_ATTR, 0);
Adam Lesinski282e1812014-01-23 18:17:42 -08001448 printf("uses-configuration:");
1449 if (reqTouchScreen != 0) {
1450 printf(" reqTouchScreen='%d'", reqTouchScreen);
1451 }
1452 if (reqKeyboardType != 0) {
1453 printf(" reqKeyboardType='%d'", reqKeyboardType);
1454 }
1455 if (reqHardKeyboard != 0) {
1456 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1457 }
1458 if (reqNavigation != 0) {
1459 printf(" reqNavigation='%d'", reqNavigation);
1460 }
1461 if (reqFiveWayNav != 0) {
1462 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1463 }
1464 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001465 } else if (tag == "supports-input") {
1466 withinSupportsInput = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001467 } else if (tag == "supports-screens") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001468 smallScreen = AaptXml::getIntegerAttribute(tree,
1469 SMALL_SCREEN_ATTR, 1);
1470 normalScreen = AaptXml::getIntegerAttribute(tree,
1471 NORMAL_SCREEN_ATTR, 1);
1472 largeScreen = AaptXml::getIntegerAttribute(tree,
1473 LARGE_SCREEN_ATTR, 1);
1474 xlargeScreen = AaptXml::getIntegerAttribute(tree,
1475 XLARGE_SCREEN_ATTR, 1);
1476 anyDensity = AaptXml::getIntegerAttribute(tree,
1477 ANY_DENSITY_ATTR, 1);
1478 requiresSmallestWidthDp = AaptXml::getIntegerAttribute(tree,
1479 REQUIRES_SMALLEST_WIDTH_DP_ATTR, 0);
1480 compatibleWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1481 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, 0);
1482 largestWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1483 LARGEST_WIDTH_LIMIT_DP_ATTR, 0);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001484 } else if (tag == "feature-group") {
1485 withinFeatureGroup = true;
1486 FeatureGroup group;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001487 group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001488 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001489 SourcePos(manifestFile, tree.getLineNumber()).error(
1490 "ERROR getting 'android:label' attribute: %s", error.string());
Adam Lesinski2c72b682014-06-24 09:56:01 -07001491 goto bail;
1492 }
1493 featureGroups.add(group);
1494
Adam Lesinski282e1812014-01-23 18:17:42 -08001495 } else if (tag == "uses-feature") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001496 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001497 if (name != "" && error == "") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001498 const char* androidSchema =
1499 "http://schemas.android.com/apk/res/android";
Adam Lesinski282e1812014-01-23 18:17:42 -08001500
Adam Lesinski694d0a72016-04-06 16:12:04 -07001501 int32_t req = AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1,
1502 &error);
1503 if (error != "") {
1504 SourcePos(manifestFile, tree.getLineNumber()).error(
1505 "failed to read attribute 'android:required': %s",
1506 error.string());
1507 goto bail;
1508 }
1509
1510 int32_t version = AaptXml::getIntegerAttribute(tree, androidSchema,
1511 "version", 0, &error);
1512 if (error != "") {
1513 SourcePos(manifestFile, tree.getLineNumber()).error(
1514 "failed to read attribute 'android:version': %s",
1515 error.string());
1516 goto bail;
1517 }
1518
1519 commonFeatures.features.add(name, Feature(req != 0, version));
Adam Lesinski2c72b682014-06-24 09:56:01 -07001520 if (req) {
1521 addParentFeatures(&commonFeatures, name);
Adam Lesinski282e1812014-01-23 18:17:42 -08001522 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001523 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001524 int vers = AaptXml::getIntegerAttribute(tree,
Adam Lesinski282e1812014-01-23 18:17:42 -08001525 GL_ES_VERSION_ATTR, &error);
1526 if (error == "") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001527 if (vers > commonFeatures.openGLESVersion) {
1528 commonFeatures.openGLESVersion = vers;
1529 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001530 }
1531 }
1532 } else if (tag == "uses-permission") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001533 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001534 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001535 SourcePos(manifestFile, tree.getLineNumber()).error(
1536 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001537 goto bail;
1538 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001539
1540 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001541 SourcePos(manifestFile, tree.getLineNumber()).error(
1542 "ERROR: missing 'android:name' for uses-permission");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001543 goto bail;
1544 }
1545
1546 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, false);
1547
Adam Lesinski2386df22016-12-28 15:08:58 -05001548 const int32_t maxSdkVersion =
1549 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, -1);
Dianne Hackborncd154e92017-02-28 17:37:35 -08001550 const String8 requiredFeature = AaptXml::getAttribute(tree,
1551 REQUIRED_FEATURE_ATTR, &error);
1552 const String8 requiredNotFeature = AaptXml::getAttribute(tree,
1553 REQUIRED_NOT_FEATURE_ATTR, &error);
Adam Lesinski2386df22016-12-28 15:08:58 -05001554
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001555 if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1556 hasWriteExternalStoragePermission = true;
Adam Lesinski2386df22016-12-28 15:08:58 -05001557 writeExternalStoragePermissionMaxSdkVersion = maxSdkVersion;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001558 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1559 hasReadExternalStoragePermission = true;
1560 } else if (name == "android.permission.READ_PHONE_STATE") {
1561 hasReadPhoneStatePermission = true;
1562 } else if (name == "android.permission.READ_CONTACTS") {
1563 hasReadContactsPermission = true;
1564 } else if (name == "android.permission.WRITE_CONTACTS") {
1565 hasWriteContactsPermission = true;
1566 } else if (name == "android.permission.READ_CALL_LOG") {
1567 hasReadCallLogPermission = true;
1568 } else if (name == "android.permission.WRITE_CALL_LOG") {
1569 hasWriteCallLogPermission = true;
1570 }
1571
1572 printUsesPermission(name,
1573 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
Dianne Hackborncd154e92017-02-28 17:37:35 -08001574 maxSdkVersion, requiredFeature, requiredNotFeature);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001575
1576 } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
1577 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1578 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001579 SourcePos(manifestFile, tree.getLineNumber()).error(
1580 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001581 goto bail;
1582 }
1583
1584 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001585 SourcePos(manifestFile, tree.getLineNumber()).error(
1586 "ERROR: missing 'android:name' for uses-permission-sdk-23");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001587 goto bail;
1588 }
1589
1590 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, true);
1591
1592 printUsesPermissionSdk23(
1593 name, AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
1594
Adam Lesinski282e1812014-01-23 18:17:42 -08001595 } else if (tag == "uses-package") {
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 == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001598 printf("uses-package:'%s'\n",
1599 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001600 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001601 SourcePos(manifestFile, tree.getLineNumber()).error(
1602 "ERROR getting 'android:name' attribute: %s", error.string());
1603 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001604 }
1605 } else if (tag == "original-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001606 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001607 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001608 printf("original-package:'%s'\n",
1609 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001610 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001611 SourcePos(manifestFile, tree.getLineNumber()).error(
1612 "ERROR getting 'android:name' attribute: %s", error.string());
1613 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001614 }
1615 } else if (tag == "supports-gl-texture") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001616 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001617 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001618 printf("supports-gl-texture:'%s'\n",
1619 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001620 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001621 SourcePos(manifestFile, tree.getLineNumber()).error(
1622 "ERROR getting 'android:name' attribute: %s", error.string());
1623 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001624 }
1625 } else if (tag == "compatible-screens") {
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001626 printCompatibleScreens(tree, &error);
1627 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001628 SourcePos(manifestFile, tree.getLineNumber()).error(
1629 "ERROR getting compatible screens: %s", error.string());
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001630 goto bail;
1631 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001632 depth--;
1633 } else if (tag == "package-verifier") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001634 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001635 if (name != "" && error == "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001636 String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR,
1637 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001638 if (publicKey != "" && error == "") {
1639 printf("package-verifier: name='%s' publicKey='%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001640 ResTable::normalizeForOutput(name.string()).string(),
1641 ResTable::normalizeForOutput(publicKey.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001642 }
1643 }
1644 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001645 } else if (depth == 3) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001646 withinActivity = false;
1647 withinReceiver = false;
1648 withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001649 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001650 hasIntentFilter = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001651 hasMetaHostPaymentCategory = false;
1652 hasMetaOffHostPaymentCategory = false;
1653 hasBindDeviceAdminPermission = false;
1654 hasBindInputMethodPermission = false;
1655 hasBindAccessibilityServicePermission = false;
1656 hasBindPrintServicePermission = false;
1657 hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001658 hasRequiredSafAttributes = false;
1659 hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001660 hasBindDreamServicePermission = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001661 if (withinApplication) {
1662 if(tag == "activity") {
1663 withinActivity = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001664 activityName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001665 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001666 SourcePos(manifestFile, tree.getLineNumber()).error(
1667 "ERROR getting 'android:name' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001668 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001669 goto bail;
1670 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001671
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001672 activityLabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1673 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001674 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001675 SourcePos(manifestFile, tree.getLineNumber()).error(
1676 "ERROR getting 'android:label' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001677 error.string());
1678 goto bail;
1679 }
1680
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001681 activityIcon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1682 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001683 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001684 SourcePos(manifestFile, tree.getLineNumber()).error(
1685 "ERROR getting 'android:icon' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001686 error.string());
1687 goto bail;
1688 }
1689
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001690 activityBanner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1691 &error);
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001692 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001693 SourcePos(manifestFile, tree.getLineNumber()).error(
1694 "ERROR getting 'android:banner' attribute: %s",
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001695 error.string());
1696 goto bail;
1697 }
1698
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001699 int32_t orien = AaptXml::getResolvedIntegerAttribute(res, tree,
Michael Wrightec4fdec2013-09-06 16:50:52 -07001700 SCREEN_ORIENTATION_ATTR, &error);
1701 if (error == "") {
1702 if (orien == 0 || orien == 6 || orien == 8) {
1703 // Requests landscape, sensorLandscape, or reverseLandscape.
Adam Lesinski43158772015-11-11 15:13:55 -08001704 addImpliedFeature(
1705 &impliedFeatures, "android.hardware.screen.landscape",
1706 String8("one or more activities have specified a "
1707 "landscape orientation"),
1708 false);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001709 } else if (orien == 1 || orien == 7 || orien == 9) {
1710 // Requests portrait, sensorPortrait, or reversePortrait.
Adam Lesinski43158772015-11-11 15:13:55 -08001711 addImpliedFeature(
1712 &impliedFeatures, "android.hardware.screen.portrait",
1713 String8("one or more activities have specified a "
1714 "portrait orientation"),
1715 false);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001716 }
1717 }
1718 } else if (tag == "uses-library") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001719 String8 libraryName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001720 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001721 SourcePos(manifestFile, tree.getLineNumber()).error(
Michael Wrightec4fdec2013-09-06 16:50:52 -07001722 "ERROR getting 'android:name' attribute for uses-library"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001723 " %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001724 goto bail;
1725 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001726 int req = AaptXml::getIntegerAttribute(tree,
1727 REQUIRED_ATTR, 1);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001728 printf("uses-library%s:'%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001729 req ? "" : "-not-required", ResTable::normalizeForOutput(
1730 libraryName.string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001731 } else if (tag == "receiver") {
1732 withinReceiver = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001733 receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001734
1735 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001736 SourcePos(manifestFile, tree.getLineNumber()).error(
Michael Wrightec4fdec2013-09-06 16:50:52 -07001737 "ERROR getting 'android:name' attribute for receiver:"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001738 " %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001739 goto bail;
1740 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001741
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001742 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1743 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001744 if (error == "") {
1745 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1746 hasBindDeviceAdminPermission = true;
1747 }
1748 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001749 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001750 "ERROR getting 'android:permission' attribute for"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001751 " receiver '%s': %s",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001752 receiverName.string(), error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001753 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001754 } else if (tag == "service") {
1755 withinService = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001756 serviceName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001757
1758 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001759 SourcePos(manifestFile, tree.getLineNumber()).error(
1760 "ERROR getting 'android:name' attribute for "
1761 "service:%s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001762 goto bail;
1763 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001764
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001765 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1766 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001767 if (error == "") {
1768 if (permission == "android.permission.BIND_INPUT_METHOD") {
1769 hasBindInputMethodPermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001770 } else if (permission ==
1771 "android.permission.BIND_ACCESSIBILITY_SERVICE") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001772 hasBindAccessibilityServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001773 } else if (permission ==
1774 "android.permission.BIND_PRINT_SERVICE") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001775 hasBindPrintServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001776 } else if (permission ==
1777 "android.permission.BIND_NFC_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07001778 hasBindNfcServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001779 } else if (permission ==
1780 "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001781 hasBindNotificationListenerServicePermission = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001782 } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
1783 hasBindDreamServicePermission = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001784 }
1785 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001786 SourcePos(manifestFile, tree.getLineNumber()).error(
1787 "ERROR getting 'android:permission' attribute for "
1788 "service '%s': %s", serviceName.string(), error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001789 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001790 } else if (tag == "provider") {
1791 withinProvider = true;
1792
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001793 bool exported = AaptXml::getResolvedIntegerAttribute(res, tree,
1794 EXPORTED_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001795 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001796 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001797 "ERROR getting 'android:exported' attribute for provider:"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001798 " %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001799 goto bail;
1800 }
1801
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001802 bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute(
1803 res, tree, GRANT_URI_PERMISSIONS_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001804 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001805 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001806 "ERROR getting 'android:grantUriPermissions' attribute for "
Adam Lesinski10de3af12016-07-13 10:14:03 -07001807 "provider: %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001808 goto bail;
1809 }
1810
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001811 String8 permission = AaptXml::getResolvedAttribute(res, tree,
1812 PERMISSION_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001813 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001814 SourcePos(manifestFile, tree.getLineNumber()).error(
1815 "ERROR getting 'android:permission' attribute for "
1816 "provider: %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001817 goto bail;
1818 }
1819
1820 hasRequiredSafAttributes |= exported && grantUriPermissions &&
1821 permission == "android.permission.MANAGE_DOCUMENTS";
1822
Michael Wrightec4fdec2013-09-06 16:50:52 -07001823 } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001824 String8 metaDataName = AaptXml::getResolvedAttribute(res, tree,
1825 NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001826 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001827 SourcePos(manifestFile, tree.getLineNumber()).error(
1828 "ERROR getting 'android:name' attribute for "
1829 "meta-data: %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001830 goto bail;
1831 }
Maurice Chu2675f762013-10-22 17:33:11 -07001832 printf("meta-data: name='%s' ",
1833 ResTable::normalizeForOutput(metaDataName.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001834 printResolvedResourceAttribute(res, tree, VALUE_ATTR, String8("value"),
Maurice Chu76327312013-10-16 18:28:46 -07001835 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001836 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001837 // Try looking for a RESOURCE_ATTR
1838 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001839 printResolvedResourceAttribute(res, tree, RESOURCE_ATTR,
Maurice Chu76327312013-10-16 18:28:46 -07001840 String8("resource"), &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001841 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001842 SourcePos(manifestFile, tree.getLineNumber()).error(
1843 "ERROR getting 'android:value' or "
Maurice Chu76327312013-10-16 18:28:46 -07001844 "'android:resource' attribute for "
Adam Lesinski10de3af12016-07-13 10:14:03 -07001845 "meta-data: %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001846 goto bail;
1847 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001848 }
Maurice Chu76327312013-10-16 18:28:46 -07001849 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001850 } else if (withinSupportsInput && tag == "input-type") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001851 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001852 if (name != "" && error == "") {
1853 supportedInput.add(name);
1854 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001855 SourcePos(manifestFile, tree.getLineNumber()).error(
1856 "ERROR getting 'android:name' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001857 error.string());
1858 goto bail;
1859 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001860 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001861 } else if (withinFeatureGroup && tag == "uses-feature") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001862 const String8 androidSchema("http://schemas.android.com/apk/res/android");
Adam Lesinski2c72b682014-06-24 09:56:01 -07001863 FeatureGroup& top = featureGroups.editTop();
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001864
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001865 String8 name = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001866 if (name != "" && error == "") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001867 Feature feature(true);
1868
1869 int32_t featureVers = AaptXml::getIntegerAttribute(
1870 tree, androidSchema.string(), "version", 0, &error);
1871 if (error == "") {
1872 feature.version = featureVers;
1873 } else {
1874 SourcePos(manifestFile, tree.getLineNumber()).error(
1875 "failed to read attribute 'android:version': %s",
1876 error.string());
1877 goto bail;
1878 }
1879
1880 top.features.add(name, feature);
Adam Lesinskid3edfde2014-08-08 17:32:44 -07001881 addParentFeatures(&top, name);
Adam Lesinski694d0a72016-04-06 16:12:04 -07001882
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001883 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001884 int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR,
1885 &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001886 if (error == "") {
1887 if (vers > top.openGLESVersion) {
1888 top.openGLESVersion = vers;
1889 }
1890 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001891 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001892 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001893 } else if (depth == 4) {
1894 if (tag == "intent-filter") {
1895 hasIntentFilter = true;
1896 withinIntentFilter = true;
1897 actMainActivity = false;
1898 actWidgetReceivers = false;
1899 actImeService = false;
1900 actWallpaperService = false;
1901 actAccessibilityService = false;
1902 actPrintService = false;
1903 actDeviceAdminEnabled = false;
1904 actHostApduService = false;
1905 actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001906 actDocumentsProvider = false;
1907 actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001908 actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001909 actCamera = false;
1910 actCameraSecure = false;
1911 catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001912 } else if (withinService && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001913 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001914 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001915 SourcePos(manifestFile, tree.getLineNumber()).error(
1916 "ERROR getting 'android:name' attribute for "
1917 "meta-data tag in service '%s': %s", serviceName.string(),
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001918 error.string());
Adam Lesinski94fc9122013-09-30 17:16:09 -07001919 goto bail;
1920 }
1921
1922 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1923 name == "android.nfc.cardemulation.off_host_apdu_service") {
1924 bool offHost = true;
1925 if (name == "android.nfc.cardemulation.host_apdu_service") {
1926 offHost = false;
1927 }
1928
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001929 String8 xmlPath = AaptXml::getResolvedAttribute(res, tree,
1930 RESOURCE_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001931 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001932 SourcePos(manifestFile, tree.getLineNumber()).error(
1933 "ERROR getting 'android:resource' attribute for "
1934 "meta-data tag in service '%s': %s",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001935 serviceName.string(), error.string());
Adam Lesinski94fc9122013-09-30 17:16:09 -07001936 goto bail;
1937 }
1938
1939 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1940 offHost, &error);
1941 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001942 SourcePos(manifestFile, tree.getLineNumber()).error(
1943 "ERROR getting AID category for service '%s'",
Adam Lesinski94fc9122013-09-30 17:16:09 -07001944 serviceName.string());
1945 goto bail;
1946 }
1947
1948 const size_t catLen = categories.size();
1949 for (size_t i = 0; i < catLen; i++) {
1950 bool paymentCategory = (categories[i] == "payment");
1951 if (offHost) {
1952 hasMetaOffHostPaymentCategory |= paymentCategory;
1953 } else {
1954 hasMetaHostPaymentCategory |= paymentCategory;
1955 }
1956 }
1957 }
1958 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001959 } else if ((depth == 5) && withinIntentFilter) {
1960 String8 action;
1961 if (tag == "action") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001962 action = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001963 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001964 SourcePos(manifestFile, tree.getLineNumber()).error(
1965 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001966 goto bail;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001967 }
1968
Adam Lesinskia5018c92013-09-30 16:23:15 -07001969 if (withinActivity) {
1970 if (action == "android.intent.action.MAIN") {
1971 isMainActivity = true;
1972 actMainActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001973 } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
1974 action == "android.media.action.VIDEO_CAMERA") {
1975 actCamera = true;
1976 } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
1977 actCameraSecure = true;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001978 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001979 } else if (withinReceiver) {
1980 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1981 actWidgetReceivers = true;
1982 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1983 actDeviceAdminEnabled = true;
1984 }
1985 } else if (withinService) {
1986 if (action == "android.view.InputMethod") {
1987 actImeService = true;
1988 } else if (action == "android.service.wallpaper.WallpaperService") {
1989 actWallpaperService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001990 } else if (action ==
1991 "android.accessibilityservice.AccessibilityService") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001992 actAccessibilityService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001993 } else if (action =="android.printservice.PrintService") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001994 actPrintService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001995 } else if (action ==
1996 "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07001997 actHostApduService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001998 } else if (action ==
1999 "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07002000 actOffHostApduService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002001 } else if (action ==
2002 "android.service.notification.NotificationListenerService") {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002003 actNotificationListenerService = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04002004 } else if (action == "android.service.dreams.DreamService") {
2005 actDreamService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002006 }
2007 } else if (withinProvider) {
2008 if (action == "android.content.action.DOCUMENTS_PROVIDER") {
2009 actDocumentsProvider = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07002010 }
2011 }
2012 if (action == "android.intent.action.SEARCH") {
2013 isSearchable = true;
2014 }
2015 }
2016
2017 if (tag == "category") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07002018 String8 category = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07002019 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07002020 SourcePos(manifestFile, tree.getLineNumber()).error(
2021 "ERROR getting 'name' attribute: %s", error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07002022 goto bail;
2023 }
2024 if (withinActivity) {
2025 if (category == "android.intent.category.LAUNCHER") {
2026 isLauncherActivity = true;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08002027 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
2028 isLeanbackLauncherActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002029 } else if (category == "android.intent.category.HOME") {
2030 catLauncher = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08002031 }
2032 }
2033 }
2034 }
2035 }
2036
2037 // Pre-1.6 implicitly granted permission compatibility logic
2038 if (targetSdk < 4) {
2039 if (!hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002040 printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
2041 printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
2042 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002043 hasWriteExternalStoragePermission = true;
2044 }
2045 if (!hasReadPhoneStatePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002046 printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
2047 printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
2048 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002049 }
2050 }
2051
2052 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
2053 // force them to always take READ_EXTERNAL_STORAGE as well. We always
2054 // do this (regardless of target API version) because we can't have
2055 // an app with write permission but not read permission.
2056 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
Adam Lesinski2386df22016-12-28 15:08:58 -05002057 printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
2058 false /* optional */, writeExternalStoragePermissionMaxSdkVersion);
Adam Lesinski58f1f362013-11-12 12:59:08 -08002059 printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
Adam Lesinski2386df22016-12-28 15:08:58 -05002060 String8("requested WRITE_EXTERNAL_STORAGE"),
2061 writeExternalStoragePermissionMaxSdkVersion);
Adam Lesinski282e1812014-01-23 18:17:42 -08002062 }
2063
2064 // Pre-JellyBean call log permission compatibility.
2065 if (targetSdk < 16) {
2066 if (!hasReadCallLogPermission && hasReadContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002067 printUsesPermission(String8("android.permission.READ_CALL_LOG"));
2068 printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
2069 String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002070 }
2071 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002072 printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
2073 printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
2074 String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002075 }
2076 }
2077
Adam Lesinskica955a42016-08-01 16:44:29 -07002078 // If the app hasn't declared the touchscreen as a feature requirement (either
2079 // directly or implied, required or not), then the faketouch feature is implied.
2080 if (!hasFeature("android.hardware.touchscreen", commonFeatures, impliedFeatures)) {
2081 addImpliedFeature(&impliedFeatures, "android.hardware.faketouch",
Adam Lesinski43158772015-11-11 15:13:55 -08002082 String8("default feature for all apps"), false);
Adam Lesinskica955a42016-08-01 16:44:29 -07002083 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07002084
2085 const size_t numFeatureGroups = featureGroups.size();
2086 if (numFeatureGroups == 0) {
2087 // If no <feature-group> tags were defined, apply auto-implied features.
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002088 printDefaultFeatureGroup(commonFeatures, impliedFeatures);
Adam Lesinski2c72b682014-06-24 09:56:01 -07002089
2090 } else {
2091 // <feature-group> tags are defined, so we ignore implied features and
2092 for (size_t i = 0; i < numFeatureGroups; i++) {
2093 FeatureGroup& grp = featureGroups.editItemAt(i);
2094
Adam Lesinskid7a94da2014-07-25 14:38:54 -07002095 if (commonFeatures.openGLESVersion > grp.openGLESVersion) {
2096 grp.openGLESVersion = commonFeatures.openGLESVersion;
2097 }
2098
Adam Lesinski2c72b682014-06-24 09:56:01 -07002099 // Merge the features defined in the top level (not inside a <feature-group>)
2100 // with this feature group.
2101 const size_t numCommonFeatures = commonFeatures.features.size();
2102 for (size_t j = 0; j < numCommonFeatures; j++) {
2103 if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07002104 grp.features.add(commonFeatures.features.keyAt(j),
2105 commonFeatures.features[j]);
Adam Lesinski2c72b682014-06-24 09:56:01 -07002106 }
2107 }
2108
Adam Lesinski73a05112014-12-08 12:53:17 -08002109 if (!grp.features.isEmpty()) {
Adam Lesinski2c72b682014-06-24 09:56:01 -07002110 printFeatureGroup(grp);
Adam Lesinski282e1812014-01-23 18:17:42 -08002111 }
2112 }
2113 }
2114
Adam Lesinski282e1812014-01-23 18:17:42 -08002115
Adam Lesinski282e1812014-01-23 18:17:42 -08002116 if (hasWidgetReceivers) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002117 printComponentPresence("app-widget");
Adam Lesinski282e1812014-01-23 18:17:42 -08002118 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002119 if (hasDeviceAdminReceiver) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002120 printComponentPresence("device-admin");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002121 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002122 if (hasImeService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002123 printComponentPresence("ime");
Adam Lesinski282e1812014-01-23 18:17:42 -08002124 }
2125 if (hasWallpaperService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002126 printComponentPresence("wallpaper");
Adam Lesinski282e1812014-01-23 18:17:42 -08002127 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002128 if (hasAccessibilityService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002129 printComponentPresence("accessibility");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002130 }
2131 if (hasPrintService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002132 printComponentPresence("print-service");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002133 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07002134 if (hasPaymentService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002135 printComponentPresence("payment");
2136 }
2137 if (isSearchable) {
2138 printComponentPresence("search");
2139 }
2140 if (hasDocumentsProvider) {
2141 printComponentPresence("document-provider");
2142 }
2143 if (hasLauncher) {
2144 printComponentPresence("launcher");
2145 }
2146 if (hasNotificationListenerService) {
2147 printComponentPresence("notification-listener");
2148 }
John Spurlockeb8d1be2014-06-25 17:46:15 -04002149 if (hasDreamService) {
2150 printComponentPresence("dream");
2151 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002152 if (hasCameraActivity) {
2153 printComponentPresence("camera");
2154 }
2155 if (hasCameraSecureActivity) {
2156 printComponentPresence("camera-secure");
2157 }
2158
2159 if (hasMainActivity) {
2160 printf("main\n");
Adam Lesinski94fc9122013-09-30 17:16:09 -07002161 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002162 if (hasOtherActivities) {
2163 printf("other-activities\n");
2164 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002165 if (hasOtherReceivers) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002166 printf("other-receivers\n");
2167 }
2168 if (hasOtherServices) {
2169 printf("other-services\n");
2170 }
2171
2172 // For modern apps, if screen size buckets haven't been specified
2173 // but the new width ranges have, then infer the buckets from them.
2174 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
2175 && requiresSmallestWidthDp > 0) {
2176 int compatWidth = compatibleWidthLimitDp;
2177 if (compatWidth <= 0) {
2178 compatWidth = requiresSmallestWidthDp;
2179 }
2180 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
2181 smallScreen = -1;
2182 } else {
2183 smallScreen = 0;
2184 }
2185 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
2186 normalScreen = -1;
2187 } else {
2188 normalScreen = 0;
2189 }
2190 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
2191 largeScreen = -1;
2192 } else {
2193 largeScreen = 0;
2194 }
2195 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
2196 xlargeScreen = -1;
2197 } else {
2198 xlargeScreen = 0;
2199 }
2200 }
2201
2202 // Determine default values for any unspecified screen sizes,
2203 // based on the target SDK of the package. As of 4 (donut)
2204 // the screen size support was introduced, so all default to
2205 // enabled.
2206 if (smallScreen > 0) {
2207 smallScreen = targetSdk >= 4 ? -1 : 0;
2208 }
2209 if (normalScreen > 0) {
2210 normalScreen = -1;
2211 }
2212 if (largeScreen > 0) {
2213 largeScreen = targetSdk >= 4 ? -1 : 0;
2214 }
2215 if (xlargeScreen > 0) {
2216 // Introduced in Gingerbread.
2217 xlargeScreen = targetSdk >= 9 ? -1 : 0;
2218 }
2219 if (anyDensity > 0) {
2220 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
2221 || compatibleWidthLimitDp > 0) ? -1 : 0;
2222 }
2223 printf("supports-screens:");
2224 if (smallScreen != 0) {
2225 printf(" 'small'");
2226 }
2227 if (normalScreen != 0) {
2228 printf(" 'normal'");
2229 }
2230 if (largeScreen != 0) {
2231 printf(" 'large'");
2232 }
2233 if (xlargeScreen != 0) {
2234 printf(" 'xlarge'");
2235 }
2236 printf("\n");
2237 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
2238 if (requiresSmallestWidthDp > 0) {
2239 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
2240 }
2241 if (compatibleWidthLimitDp > 0) {
2242 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
2243 }
2244 if (largestWidthLimitDp > 0) {
2245 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
2246 }
2247
2248 printf("locales:");
2249 const size_t NL = locales.size();
2250 for (size_t i=0; i<NL; i++) {
2251 const char* localeStr = locales[i].string();
2252 if (localeStr == NULL || strlen(localeStr) == 0) {
2253 localeStr = "--_--";
2254 }
2255 printf(" '%s'", localeStr);
2256 }
2257 printf("\n");
2258
2259 printf("densities:");
2260 const size_t ND = densities.size();
2261 for (size_t i=0; i<ND; i++) {
2262 printf(" '%d'", densities[i]);
2263 }
2264 printf("\n");
2265
2266 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
2267 if (dir != NULL) {
2268 if (dir->getFileCount() > 0) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002269 SortedVector<String8> architectures;
Adam Lesinski282e1812014-01-23 18:17:42 -08002270 for (size_t i=0; i<dir->getFileCount(); i++) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002271 architectures.add(ResTable::normalizeForOutput(
2272 dir->getFileName(i).string()));
Adam Lesinski282e1812014-01-23 18:17:42 -08002273 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07002274
2275 bool outputAltNativeCode = false;
2276 // A multiArch package is one that contains 64-bit and
2277 // 32-bit versions of native code and expects 3rd-party
2278 // apps to load these native code libraries. Since most
2279 // 64-bit systems also support 32-bit apps, the apps
2280 // loading this multiArch package's code may be either
2281 // 32-bit or 64-bit.
2282 if (hasMultiArch) {
2283 // If this is a multiArch package, report the 64-bit
2284 // version only. Then as a separate entry, report the
2285 // rest.
2286 //
2287 // If we report the 32-bit architecture, this APK will
2288 // be installed on a 32-bit device, causing a large waste
2289 // of bandwidth and disk space. This assumes that
2290 // the developer of the multiArch package has also
2291 // made a version that is 32-bit only.
2292 String8 intel64("x86_64");
2293 String8 arm64("arm64-v8a");
2294 ssize_t index = architectures.indexOf(intel64);
2295 if (index < 0) {
2296 index = architectures.indexOf(arm64);
2297 }
2298
2299 if (index >= 0) {
2300 printf("native-code: '%s'\n", architectures[index].string());
2301 architectures.removeAt(index);
2302 outputAltNativeCode = true;
2303 }
2304 }
2305
2306 const size_t archCount = architectures.size();
2307 if (archCount > 0) {
2308 if (outputAltNativeCode) {
2309 printf("alt-");
2310 }
2311 printf("native-code:");
2312 for (size_t i = 0; i < archCount; i++) {
2313 printf(" '%s'", architectures[i].string());
2314 }
2315 printf("\n");
2316 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002317 }
2318 delete dir;
2319 }
2320 } else if (strcmp("badger", option) == 0) {
2321 printf("%s", CONSOLE_DATA);
2322 } else if (strcmp("configurations", option) == 0) {
2323 Vector<ResTable_config> configs;
2324 res.getConfigurations(&configs);
2325 const size_t N = configs.size();
2326 for (size_t i=0; i<N; i++) {
2327 printf("%s\n", configs[i].toString().string());
2328 }
2329 } else {
2330 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2331 goto bail;
2332 }
2333 }
2334
2335 result = NO_ERROR;
2336
2337bail:
Adam Lesinski10de3af12016-07-13 10:14:03 -07002338 if (SourcePos::hasErrors()) {
2339 SourcePos::printErrors(stderr);
2340 }
2341
Adam Lesinski282e1812014-01-23 18:17:42 -08002342 if (asset) {
2343 delete asset;
2344 }
2345 return (result != NO_ERROR);
2346}
2347
2348
2349/*
2350 * Handle the "add" command, which wants to add files to a new or
2351 * pre-existing archive.
2352 */
2353int doAdd(Bundle* bundle)
2354{
2355 ZipFile* zip = NULL;
2356 status_t result = UNKNOWN_ERROR;
2357 const char* zipFileName;
2358
2359 if (bundle->getUpdate()) {
2360 /* avoid confusion */
2361 fprintf(stderr, "ERROR: can't use '-u' with add\n");
2362 goto bail;
2363 }
2364
2365 if (bundle->getFileSpecCount() < 1) {
2366 fprintf(stderr, "ERROR: must specify zip file name\n");
2367 goto bail;
2368 }
2369 zipFileName = bundle->getFileSpecEntry(0);
2370
2371 if (bundle->getFileSpecCount() < 2) {
2372 fprintf(stderr, "NOTE: nothing to do\n");
2373 goto bail;
2374 }
2375
2376 zip = openReadWrite(zipFileName, true);
2377 if (zip == NULL) {
2378 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2379 goto bail;
2380 }
2381
2382 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2383 const char* fileName = bundle->getFileSpecEntry(i);
2384
2385 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
2386 printf(" '%s'... (from gzip)\n", fileName);
2387 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
2388 } else {
2389 if (bundle->getJunkPath()) {
2390 String8 storageName = String8(fileName).getPathLeaf();
Maurice Chu2675f762013-10-22 17:33:11 -07002391 printf(" '%s' as '%s'...\n", fileName,
2392 ResTable::normalizeForOutput(storageName.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08002393 result = zip->add(fileName, storageName.string(),
2394 bundle->getCompressionMethod(), NULL);
2395 } else {
2396 printf(" '%s'...\n", fileName);
2397 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2398 }
2399 }
2400 if (result != NO_ERROR) {
2401 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2402 if (result == NAME_NOT_FOUND) {
2403 fprintf(stderr, ": file not found\n");
2404 } else if (result == ALREADY_EXISTS) {
2405 fprintf(stderr, ": already exists in archive\n");
2406 } else {
2407 fprintf(stderr, "\n");
2408 }
2409 goto bail;
2410 }
2411 }
2412
2413 result = NO_ERROR;
2414
2415bail:
2416 delete zip;
2417 return (result != NO_ERROR);
2418}
2419
2420
2421/*
2422 * Delete files from an existing archive.
2423 */
2424int doRemove(Bundle* bundle)
2425{
2426 ZipFile* zip = NULL;
2427 status_t result = UNKNOWN_ERROR;
2428 const char* zipFileName;
2429
2430 if (bundle->getFileSpecCount() < 1) {
2431 fprintf(stderr, "ERROR: must specify zip file name\n");
2432 goto bail;
2433 }
2434 zipFileName = bundle->getFileSpecEntry(0);
2435
2436 if (bundle->getFileSpecCount() < 2) {
2437 fprintf(stderr, "NOTE: nothing to do\n");
2438 goto bail;
2439 }
2440
2441 zip = openReadWrite(zipFileName, false);
2442 if (zip == NULL) {
2443 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2444 zipFileName);
2445 goto bail;
2446 }
2447
2448 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2449 const char* fileName = bundle->getFileSpecEntry(i);
2450 ZipEntry* entry;
2451
2452 entry = zip->getEntryByName(fileName);
2453 if (entry == NULL) {
2454 printf(" '%s' NOT FOUND\n", fileName);
2455 continue;
2456 }
2457
2458 result = zip->remove(entry);
2459
2460 if (result != NO_ERROR) {
2461 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2462 bundle->getFileSpecEntry(i), zipFileName);
2463 goto bail;
2464 }
2465 }
2466
2467 /* update the archive */
2468 zip->flush();
2469
2470bail:
2471 delete zip;
2472 return (result != NO_ERROR);
2473}
2474
Adam Lesinski3921e872014-05-13 10:56:25 -07002475static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002476 const size_t numDirs = dir->getDirs().size();
2477 for (size_t i = 0; i < numDirs; i++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002478 bool ignore = ignoreConfig;
2479 const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2480 const char* dirStr = subDir->getLeaf().string();
2481 if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2482 ignore = true;
2483 }
2484 status_t err = addResourcesToBuilder(subDir, builder, ignore);
Adam Lesinskifab50872014-04-16 14:40:42 -07002485 if (err != NO_ERROR) {
2486 return err;
2487 }
2488 }
2489
2490 const size_t numFiles = dir->getFiles().size();
2491 for (size_t i = 0; i < numFiles; i++) {
2492 sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2493 const size_t numConfigs = gp->getFiles().size();
2494 for (size_t j = 0; j < numConfigs; j++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002495 status_t err = NO_ERROR;
Adam Lesinskic7614e52017-03-16 16:54:23 -07002496 if (ignoreConfig) {
Guang Zhu8c2df712017-03-21 03:53:43 +00002497 err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
Adam Lesinskic7614e52017-03-16 16:54:23 -07002498 } else {
Guang Zhu8c2df712017-03-21 03:53:43 +00002499 err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
Adam Lesinskic7614e52017-03-16 16:54:23 -07002500 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002501 if (err != NO_ERROR) {
2502 fprintf(stderr, "Failed to add %s (%s) to builder.\n",
Guang Zhu8c2df712017-03-21 03:53:43 +00002503 gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
Adam Lesinskifab50872014-04-16 14:40:42 -07002504 return err;
2505 }
2506 }
2507 }
2508 return NO_ERROR;
2509}
2510
2511static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2512 if (split->isBase()) {
2513 return original;
2514 }
2515
2516 String8 ext(original.getPathExtension());
2517 if (ext == String8(".apk")) {
2518 return String8::format("%s_%s%s",
2519 original.getBasePath().string(),
2520 split->getDirectorySafeName().string(),
2521 ext.string());
2522 }
2523
2524 return String8::format("%s_%s", original.string(),
2525 split->getDirectorySafeName().string());
2526}
Adam Lesinski282e1812014-01-23 18:17:42 -08002527
2528/*
2529 * Package up an asset directory and associated application files.
2530 */
2531int doPackage(Bundle* bundle)
2532{
2533 const char* outputAPKFile;
2534 int retVal = 1;
2535 status_t err;
2536 sp<AaptAssets> assets;
2537 int N;
2538 FILE* fp;
2539 String8 dependencyFile;
Adam Lesinskifab50872014-04-16 14:40:42 -07002540 sp<ApkBuilder> builder;
Adam Lesinski282e1812014-01-23 18:17:42 -08002541
Anton Krumina2ef5c02014-03-12 14:46:44 -07002542 // -c en_XA or/and ar_XB means do pseudolocalization
Adam Lesinskifab50872014-04-16 14:40:42 -07002543 sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2544 err = configFilter->parse(bundle->getConfigurations());
Adam Lesinski282e1812014-01-23 18:17:42 -08002545 if (err != NO_ERROR) {
2546 goto bail;
2547 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002548 if (configFilter->containsPseudo()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002549 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2550 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002551 if (configFilter->containsPseudoBidi()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002552 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
Adam Lesinski282e1812014-01-23 18:17:42 -08002553 }
2554
2555 N = bundle->getFileSpecCount();
2556 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
Adam Lesinski09384302014-01-22 16:07:42 -08002557 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002558 fprintf(stderr, "ERROR: no input files\n");
2559 goto bail;
2560 }
2561
2562 outputAPKFile = bundle->getOutputAPKFile();
2563
2564 // Make sure the filenames provided exist and are of the appropriate type.
2565 if (outputAPKFile) {
2566 FileType type;
2567 type = getFileType(outputAPKFile);
2568 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2569 fprintf(stderr,
2570 "ERROR: output file '%s' exists but is not regular file\n",
2571 outputAPKFile);
2572 goto bail;
2573 }
2574 }
2575
2576 // Load the assets.
2577 assets = new AaptAssets();
2578
2579 // Set up the resource gathering in assets if we're going to generate
2580 // dependency files. Every time we encounter a resource while slurping
2581 // the tree, we'll add it to these stores so we have full resource paths
2582 // to write to a dependency file.
2583 if (bundle->getGenDependencies()) {
2584 sp<FilePathStore> resPathStore = new FilePathStore;
2585 assets->setFullResPaths(resPathStore);
2586 sp<FilePathStore> assetPathStore = new FilePathStore;
2587 assets->setFullAssetPaths(assetPathStore);
2588 }
2589
2590 err = assets->slurpFromArgs(bundle);
2591 if (err < 0) {
2592 goto bail;
2593 }
2594
2595 if (bundle->getVerbose()) {
2596 assets->print(String8());
2597 }
2598
Adam Lesinskifab50872014-04-16 14:40:42 -07002599 // Create the ApkBuilder, which will collect the compiled files
2600 // to write to the final APK (or sets of APKs if we are building
2601 // a Split APK.
2602 builder = new ApkBuilder(configFilter);
2603
2604 // If we are generating a Split APK, find out which configurations to split on.
2605 if (bundle->getSplitConfigurations().size() > 0) {
2606 const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2607 const size_t numSplits = splitStrs.size();
2608 for (size_t i = 0; i < numSplits; i++) {
2609 std::set<ConfigDescription> configs;
2610 if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2611 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2612 goto bail;
2613 }
2614
2615 err = builder->createSplitForConfigs(configs);
2616 if (err != NO_ERROR) {
2617 goto bail;
2618 }
2619 }
2620 }
2621
Adam Lesinski282e1812014-01-23 18:17:42 -08002622 // If they asked for any fileAs that need to be compiled, do so.
2623 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002624 err = buildResources(bundle, assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002625 if (err != 0) {
2626 goto bail;
2627 }
2628 }
2629
2630 // At this point we've read everything and processed everything. From here
2631 // on out it's just writing output files.
2632 if (SourcePos::hasErrors()) {
2633 goto bail;
2634 }
2635
2636 // Update symbols with information about which ones are needed as Java symbols.
2637 assets->applyJavaSymbols();
2638 if (SourcePos::hasErrors()) {
2639 goto bail;
2640 }
2641
2642 // If we've been asked to generate a dependency file, do that here
2643 if (bundle->getGenDependencies()) {
2644 // If this is the packaging step, generate the dependency file next to
2645 // the output apk (e.g. bin/resources.ap_.d)
2646 if (outputAPKFile) {
2647 dependencyFile = String8(outputAPKFile);
2648 // Add the .d extension to the dependency file.
2649 dependencyFile.append(".d");
2650 } else {
2651 // Else if this is the R.java dependency generation step,
2652 // generate the dependency file in the R.java package subdirectory
2653 // e.g. gen/com/foo/app/R.java.d
2654 dependencyFile = String8(bundle->getRClassDir());
2655 dependencyFile.appendPath("R.java.d");
2656 }
2657 // Make sure we have a clean dependency file to start with
2658 fp = fopen(dependencyFile, "w");
2659 fclose(fp);
2660 }
2661
2662 // Write out R.java constants
2663 if (!assets->havePrivateSymbols()) {
2664 if (bundle->getCustomPackage() == NULL) {
2665 // Write the R.java file into the appropriate class directory
2666 // e.g. gen/com/foo/app/R.java
Adam Lesinski1e4663852014-08-15 14:47:28 -07002667 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002668 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002669 } else {
2670 const String8 customPkg(bundle->getCustomPackage());
Adam Lesinski1e4663852014-08-15 14:47:28 -07002671 err = writeResourceSymbols(bundle, assets, customPkg, true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002672 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002673 }
2674 if (err < 0) {
2675 goto bail;
2676 }
2677 // If we have library files, we're going to write our R.java file into
2678 // the appropriate class directory for those libraries as well.
2679 // e.g. gen/com/foo/app/lib/R.java
2680 if (bundle->getExtraPackages() != NULL) {
2681 // Split on colon
2682 String8 libs(bundle->getExtraPackages());
2683 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2684 while (packageString != NULL) {
2685 // Write the R.java file out with the correct package name
Marcin Kosiba0f3a5a62014-09-11 13:48:48 +01002686 err = writeResourceSymbols(bundle, assets, String8(packageString), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002687 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002688 if (err < 0) {
2689 goto bail;
2690 }
2691 packageString = strtok(NULL, ":");
2692 }
2693 libs.unlockBuffer();
2694 }
2695 } else {
Adam Lesinski1e4663852014-08-15 14:47:28 -07002696 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002697 if (err < 0) {
2698 goto bail;
2699 }
Adam Lesinski1e4663852014-08-15 14:47:28 -07002700 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002701 if (err < 0) {
2702 goto bail;
2703 }
2704 }
2705
2706 // Write out the ProGuard file
2707 err = writeProguardFile(bundle, assets);
2708 if (err < 0) {
2709 goto bail;
2710 }
2711
Rohit Agrawal86229cb2016-04-21 16:29:58 -07002712 // Write out the Main Dex ProGuard file
2713 err = writeMainDexProguardFile(bundle, assets);
2714 if (err < 0) {
2715 goto bail;
2716 }
2717
Adam Lesinski282e1812014-01-23 18:17:42 -08002718 // Write the apk
2719 if (outputAPKFile) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002720 // Gather all resources and add them to the APK Builder. The builder will then
2721 // figure out which Split they belong in.
2722 err = addResourcesToBuilder(assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002723 if (err != NO_ERROR) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002724 goto bail;
2725 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002726
2727 const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2728 const size_t numSplits = splits.size();
2729 for (size_t i = 0; i < numSplits; i++) {
2730 const sp<ApkSplit>& split = splits[i];
2731 String8 outputPath = buildApkName(String8(outputAPKFile), split);
2732 err = writeAPK(bundle, outputPath, split);
2733 if (err != NO_ERROR) {
2734 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2735 goto bail;
2736 }
2737 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002738 }
2739
2740 // If we've been asked to generate a dependency file, we need to finish up here.
2741 // the writeResourceSymbols and writeAPK functions have already written the target
2742 // half of the dependency file, now we need to write the prerequisites. (files that
2743 // the R.java file or .ap_ file depend on)
2744 if (bundle->getGenDependencies()) {
2745 // Now that writeResourceSymbols or writeAPK has taken care of writing
2746 // the targets to our dependency file, we'll write the prereqs
2747 fp = fopen(dependencyFile, "a+");
2748 fprintf(fp, " : ");
2749 bool includeRaw = (outputAPKFile != NULL);
2750 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2751 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2752 // and therefore was not added to our pathstores during slurping
2753 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2754 fclose(fp);
2755 }
2756
2757 retVal = 0;
2758bail:
2759 if (SourcePos::hasErrors()) {
2760 SourcePos::printErrors(stderr);
2761 }
2762 return retVal;
2763}
2764
2765/*
2766 * Do PNG Crunching
2767 * PRECONDITIONS
2768 * -S flag points to a source directory containing drawable* folders
2769 * -C flag points to destination directory. The folder structure in the
2770 * source directory will be mirrored to the destination (cache) directory
2771 *
2772 * POSTCONDITIONS
2773 * Destination directory will be updated to match the PNG files in
Maurice Chu2675f762013-10-22 17:33:11 -07002774 * the source directory.
Adam Lesinski282e1812014-01-23 18:17:42 -08002775 */
2776int doCrunch(Bundle* bundle)
2777{
2778 fprintf(stdout, "Crunching PNG Files in ");
2779 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2780 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2781
2782 updatePreProcessedCache(bundle);
2783
2784 return NO_ERROR;
2785}
2786
2787/*
2788 * Do PNG Crunching on a single flag
2789 * -i points to a single png file
2790 * -o points to a single png output file
2791 */
2792int doSingleCrunch(Bundle* bundle)
2793{
2794 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2795 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2796
2797 String8 input(bundle->getSingleCrunchInputFile());
2798 String8 output(bundle->getSingleCrunchOutputFile());
2799
2800 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2801 // we can't return the status_t as it gets truncate to the lower 8 bits.
2802 return 42;
2803 }
2804
2805 return NO_ERROR;
2806}
2807
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002808int runInDaemonMode(Bundle* bundle) {
2809 std::cout << "Ready" << std::endl;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002810 for (std::string cmd; std::getline(std::cin, cmd);) {
2811 if (cmd == "quit") {
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002812 return NO_ERROR;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002813 } else if (cmd == "s") {
2814 // Two argument crunch
2815 std::string inputFile, outputFile;
2816 std::getline(std::cin, inputFile);
2817 std::getline(std::cin, outputFile);
2818 bundle->setSingleCrunchInputFile(inputFile.c_str());
2819 bundle->setSingleCrunchOutputFile(outputFile.c_str());
2820 std::cout << "Crunching " << inputFile << std::endl;
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002821 if (doSingleCrunch(bundle) != NO_ERROR) {
2822 std::cout << "Error" << std::endl;
2823 }
2824 std::cout << "Done" << std::endl;
2825 } else {
2826 // in case of invalid command, just bail out.
2827 std::cerr << "Unknown command" << std::endl;
2828 return -1;
2829 }
2830 }
2831 return -1;
2832}
2833
Adam Lesinski282e1812014-01-23 18:17:42 -08002834char CONSOLE_DATA[2925] = {
2835 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2836 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2837 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2838 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2839 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2840 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2841 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2842 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2843 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2844 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2845 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2846 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2847 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2848 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2849 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2850 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2851 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2852 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2853 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2854 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2855 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2856 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2857 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2858 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2859 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2860 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2861 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2862 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2863 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2864 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2865 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2866 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2867 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2868 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2869 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2870 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2871 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2872 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2873 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2874 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2875 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2876 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2877 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2878 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2879 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2880 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2881 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2882 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2883 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2884 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2885 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2886 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2887 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2888 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2889 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2890 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2891 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2892 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2893 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2894 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2895 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2896 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2897 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2898 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2899 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2900 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2901 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2902 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2903 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2904 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2905 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2906 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2907 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2908 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2909 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2910 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2911 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2912 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2913 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2914 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2915 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2916 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2917 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2918 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2919 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2920 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2921 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2922 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2923 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2924 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2925 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2926 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2927 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2928 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2929 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2930 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2931 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2932 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2933 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2934 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2935 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2936 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2937 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2938 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2939 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2940 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2941 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2942 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2943 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2944 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2945 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2946 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2947 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2948 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2949 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2950 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2951 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2952 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2953 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2954 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2955 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2956 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2957 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2958 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2959 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2960 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2961 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2962 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2963 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2964 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2965 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2966 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2967 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2968 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2969 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2970 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2971 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2972 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2973 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2974 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2975 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2976 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2977 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2978 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2979 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2980 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2981 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2982 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2983 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2984 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2985 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2986 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2987 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2988 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2989 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2990 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2991 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2992 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2993 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2994 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2995 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2996 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2997 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2998 };