blob: 21386b88ce2c139efc8dc69667eed9bfcc099a12 [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,
Alan Viverette11be9312017-11-09 15:41:44 -0500296 COMPILE_SDK_VERSION_ATTR = 0x01010572, // NOT FINALIZED
297 COMPILE_SDK_VERSION_CODENAME_ATTR = 0x01010573, // NOT FINALIZED
Adam Lesinski282e1812014-01-23 18:17:42 -0800298};
299
Maurice Chu2675f762013-10-22 17:33:11 -0700300String8 getComponentName(String8 &pkgName, String8 &componentName) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800301 ssize_t idx = componentName.find(".");
302 String8 retStr(pkgName);
303 if (idx == 0) {
304 retStr += componentName;
305 } else if (idx < 0) {
306 retStr += ".";
307 retStr += componentName;
308 } else {
Maurice Chu2675f762013-10-22 17:33:11 -0700309 return componentName;
Adam Lesinski282e1812014-01-23 18:17:42 -0800310 }
Maurice Chu2675f762013-10-22 17:33:11 -0700311 return retStr;
Adam Lesinski282e1812014-01-23 18:17:42 -0800312}
313
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700314static void printCompatibleScreens(ResXMLTree& tree, String8* outError) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800315 size_t len;
316 ResXMLTree::event_code_t code;
317 int depth = 0;
318 bool first = true;
319 printf("compatible-screens:");
320 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
321 if (code == ResXMLTree::END_TAG) {
322 depth--;
323 if (depth < 0) {
324 break;
325 }
326 continue;
327 }
328 if (code != ResXMLTree::START_TAG) {
329 continue;
330 }
331 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700332 const char16_t* ctag16 = tree.getElementName(&len);
333 if (ctag16 == NULL) {
334 *outError = "failed to get XML element name (bad string pool)";
335 return;
336 }
337 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800338 if (tag == "screen") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700339 int32_t screenSize = AaptXml::getIntegerAttribute(tree,
340 SCREEN_SIZE_ATTR);
341 int32_t screenDensity = AaptXml::getIntegerAttribute(tree,
342 SCREEN_DENSITY_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -0800343 if (screenSize > 0 && screenDensity > 0) {
344 if (!first) {
345 printf(",");
346 }
347 first = false;
348 printf("'%d/%d'", screenSize, screenDensity);
349 }
350 }
351 }
352 printf("\n");
353}
354
Dianne Hackborncd154e92017-02-28 17:37:35 -0800355static void printUsesPermission(const String8& name, bool optional=false, int maxSdkVersion=-1,
356 const String8& requiredFeature = String8::empty(),
357 const String8& requiredNotFeature = String8::empty()) {
Adam Lesinski58f1f362013-11-12 12:59:08 -0800358 printf("uses-permission: name='%s'", ResTable::normalizeForOutput(name.string()).string());
359 if (maxSdkVersion != -1) {
360 printf(" maxSdkVersion='%d'", maxSdkVersion);
361 }
Dianne Hackborncd154e92017-02-28 17:37:35 -0800362 if (requiredFeature.length() > 0) {
363 printf(" requiredFeature='%s'", requiredFeature.string());
364 }
365 if (requiredNotFeature.length() > 0) {
366 printf(" requiredNotFeature='%s'", requiredNotFeature.string());
367 }
Adam Lesinski58f1f362013-11-12 12:59:08 -0800368 printf("\n");
369
370 if (optional) {
371 printf("optional-permission: name='%s'",
372 ResTable::normalizeForOutput(name.string()).string());
373 if (maxSdkVersion != -1) {
374 printf(" maxSdkVersion='%d'", maxSdkVersion);
375 }
376 printf("\n");
377 }
378}
379
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800380static void printUsesPermissionSdk23(const String8& name, int maxSdkVersion=-1) {
381 printf("uses-permission-sdk-23: ");
382
383 printf("name='%s'", ResTable::normalizeForOutput(name.string()).string());
384 if (maxSdkVersion != -1) {
385 printf(" maxSdkVersion='%d'", maxSdkVersion);
386 }
387 printf("\n");
388}
389
Adam Lesinski2386df22016-12-28 15:08:58 -0500390static void printUsesImpliedPermission(const String8& name, const String8& reason,
391 const int32_t maxSdkVersion = -1) {
392 printf("uses-implied-permission: name='%s'",
393 ResTable::normalizeForOutput(name.string()).string());
394 if (maxSdkVersion != -1) {
395 printf(" maxSdkVersion='%d'", maxSdkVersion);
396 }
397 printf(" reason='%s'\n", ResTable::normalizeForOutput(reason.string()).string());
Adam Lesinski58f1f362013-11-12 12:59:08 -0800398}
399
Chih-Hung Hsieh9b8528f2016-08-10 14:15:30 -0700400Vector<String8> getNfcAidCategories(AssetManager& assets, const String8& xmlPath, bool offHost,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700401 String8 *outError = NULL)
402{
403 Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER);
404 if (aidAsset == NULL) {
405 if (outError != NULL) *outError = "xml resource does not exist";
406 return Vector<String8>();
407 }
408
409 const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service");
410
411 bool withinApduService = false;
412 Vector<String8> categories;
413
414 String8 error;
415 ResXMLTree tree;
416 tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength());
417
418 size_t len;
419 int depth = 0;
420 ResXMLTree::event_code_t code;
421 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
422 if (code == ResXMLTree::END_TAG) {
423 depth--;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700424 const char16_t* ctag16 = tree.getElementName(&len);
425 if (ctag16 == NULL) {
426 *outError = "failed to get XML element name (bad string pool)";
427 return Vector<String8>();
428 }
429 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700430
431 if (depth == 0 && tag == serviceTagName) {
432 withinApduService = false;
433 }
434
435 } else if (code == ResXMLTree::START_TAG) {
436 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700437 const char16_t* ctag16 = tree.getElementName(&len);
438 if (ctag16 == NULL) {
439 *outError = "failed to get XML element name (bad string pool)";
440 return Vector<String8>();
441 }
442 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700443
444 if (depth == 1) {
445 if (tag == serviceTagName) {
446 withinApduService = true;
447 }
448 } else if (depth == 2 && withinApduService) {
449 if (tag == "aid-group") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700450 String8 category = AaptXml::getAttribute(tree, CATEGORY_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700451 if (error != "") {
452 if (outError != NULL) *outError = error;
453 return Vector<String8>();
454 }
455
456 categories.add(category);
457 }
458 }
459 }
460 }
461 aidAsset->close();
462 return categories;
463}
464
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700465static void printComponentPresence(const char* componentName) {
466 printf("provides-component:'%s'\n", componentName);
467}
468
Adam Lesinski2c72b682014-06-24 09:56:01 -0700469/**
470 * Represents a feature that has been automatically added due to
471 * a pre-requisite or some other reason.
472 */
473struct ImpliedFeature {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800474 ImpliedFeature() : impliedBySdk23(false) {}
475 ImpliedFeature(const String8& n, bool sdk23) : name(n), impliedBySdk23(sdk23) {}
476
Adam Lesinski2c72b682014-06-24 09:56:01 -0700477 /**
478 * Name of the implied feature.
479 */
480 String8 name;
481
482 /**
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800483 * Was this implied by a permission from SDK 23 (<uses-permission-sdk-23 />)?
484 */
485 bool impliedBySdk23;
486
487 /**
Adam Lesinski2c72b682014-06-24 09:56:01 -0700488 * List of human-readable reasons for why this feature was implied.
489 */
490 SortedVector<String8> reasons;
491};
492
Adam Lesinski694d0a72016-04-06 16:12:04 -0700493struct Feature {
494 Feature() : required(false), version(-1) {}
Chih-Hung Hsiehd53e3be2016-05-03 10:02:51 -0700495 explicit Feature(bool required, int32_t version = -1) : required(required), version(version) {}
Adam Lesinski694d0a72016-04-06 16:12:04 -0700496
497 /**
498 * Whether the feature is required.
499 */
500 bool required;
501
502 /**
503 * What version of the feature is requested.
504 */
505 int32_t version;
506};
507
Adam Lesinski2c72b682014-06-24 09:56:01 -0700508/**
509 * Represents a <feature-group> tag in the AndroidManifest.xml
510 */
511struct FeatureGroup {
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700512 FeatureGroup() : openGLESVersion(-1) {}
513
Adam Lesinski2c72b682014-06-24 09:56:01 -0700514 /**
515 * Human readable label
516 */
517 String8 label;
518
519 /**
520 * Explicit features defined in the group
521 */
Adam Lesinski694d0a72016-04-06 16:12:04 -0700522 KeyedVector<String8, Feature> features;
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700523
524 /**
525 * OpenGL ES version required
526 */
527 int openGLESVersion;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700528};
529
Adam Lesinskica955a42016-08-01 16:44:29 -0700530static bool hasFeature(const char* name, const FeatureGroup& grp,
531 const KeyedVector<String8, ImpliedFeature>& implied) {
532 String8 name8(name);
533 ssize_t idx = grp.features.indexOfKey(name8);
534 if (idx < 0) {
535 idx = implied.indexOfKey(name8);
536 }
537 return idx >= 0;
538}
539
Adam Lesinski2c72b682014-06-24 09:56:01 -0700540static void addImpliedFeature(KeyedVector<String8, ImpliedFeature>* impliedFeatures,
Adam Lesinski43158772015-11-11 15:13:55 -0800541 const char* name, const String8& reason, bool sdk23) {
Adam Lesinski2c72b682014-06-24 09:56:01 -0700542 String8 name8(name);
543 ssize_t idx = impliedFeatures->indexOfKey(name8);
544 if (idx < 0) {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800545 idx = impliedFeatures->add(name8, ImpliedFeature(name8, sdk23));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700546 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800547
548 ImpliedFeature* feature = &impliedFeatures->editValueAt(idx);
549
550 // A non-sdk 23 implied feature takes precedence.
551 if (feature->impliedBySdk23 && !sdk23) {
552 feature->impliedBySdk23 = false;
553 }
Adam Lesinski43158772015-11-11 15:13:55 -0800554 feature->reasons.add(reason);
Adam Lesinski2c72b682014-06-24 09:56:01 -0700555}
556
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800557static void printFeatureGroupImpl(const FeatureGroup& grp,
558 const KeyedVector<String8, ImpliedFeature>* impliedFeatures) {
Adam Lesinski2c72b682014-06-24 09:56:01 -0700559 printf("feature-group: label='%s'\n", grp.label.string());
560
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700561 if (grp.openGLESVersion > 0) {
562 printf(" uses-gl-es: '0x%x'\n", grp.openGLESVersion);
563 }
564
Adam Lesinski2c72b682014-06-24 09:56:01 -0700565 const size_t numFeatures = grp.features.size();
566 for (size_t i = 0; i < numFeatures; i++) {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700567 const Feature& feature = grp.features[i];
568 const bool required = feature.required;
569 const int32_t version = feature.version;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700570
571 const String8& featureName = grp.features.keyAt(i);
Adam Lesinski694d0a72016-04-06 16:12:04 -0700572 printf(" uses-feature%s: name='%s'", (required ? "" : "-not-required"),
Adam Lesinski2c72b682014-06-24 09:56:01 -0700573 ResTable::normalizeForOutput(featureName.string()).string());
Adam Lesinski694d0a72016-04-06 16:12:04 -0700574
575 if (version > 0) {
576 printf(" version='%d'", version);
577 }
578 printf("\n");
Adam Lesinski2c72b682014-06-24 09:56:01 -0700579 }
580
581 const size_t numImpliedFeatures =
582 (impliedFeatures != NULL) ? impliedFeatures->size() : 0;
583 for (size_t i = 0; i < numImpliedFeatures; i++) {
584 const ImpliedFeature& impliedFeature = impliedFeatures->valueAt(i);
585 if (grp.features.indexOfKey(impliedFeature.name) >= 0) {
586 // The feature is explicitly set, no need to use implied
587 // definition.
588 continue;
589 }
590
591 String8 printableFeatureName(ResTable::normalizeForOutput(
592 impliedFeature.name.string()));
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800593 const char* sdk23Suffix = impliedFeature.impliedBySdk23 ? "-sdk-23" : "";
594
595 printf(" uses-feature%s: name='%s'\n", sdk23Suffix, printableFeatureName.string());
596 printf(" uses-implied-feature%s: name='%s' reason='", sdk23Suffix,
597 printableFeatureName.string());
Adam Lesinski2c72b682014-06-24 09:56:01 -0700598 const size_t numReasons = impliedFeature.reasons.size();
599 for (size_t j = 0; j < numReasons; j++) {
600 printf("%s", impliedFeature.reasons[j].string());
601 if (j + 2 < numReasons) {
602 printf(", ");
603 } else if (j + 1 < numReasons) {
604 printf(", and ");
605 }
606 }
607 printf("'\n");
608 }
609}
610
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800611static void printFeatureGroup(const FeatureGroup& grp) {
612 printFeatureGroupImpl(grp, NULL);
613}
614
615static void printDefaultFeatureGroup(const FeatureGroup& grp,
616 const KeyedVector<String8, ImpliedFeature>& impliedFeatures) {
617 printFeatureGroupImpl(grp, &impliedFeatures);
618}
619
Adam Lesinski2c72b682014-06-24 09:56:01 -0700620static void addParentFeatures(FeatureGroup* grp, const String8& name) {
621 if (name == "android.hardware.camera.autofocus" ||
622 name == "android.hardware.camera.flash") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700623 grp->features.add(String8("android.hardware.camera"), Feature(true));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700624 } else if (name == "android.hardware.location.gps" ||
625 name == "android.hardware.location.network") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700626 grp->features.add(String8("android.hardware.location"), Feature(true));
Adam Lesinskica955a42016-08-01 16:44:29 -0700627 } else if (name == "android.hardware.faketouch.multitouch") {
628 grp->features.add(String8("android.hardware.faketouch"), Feature(true));
629 } else if (name == "android.hardware.faketouch.multitouch.distinct" ||
630 name == "android.hardware.faketouch.multitouch.jazzhands") {
631 grp->features.add(String8("android.hardware.faketouch.multitouch"), Feature(true));
632 grp->features.add(String8("android.hardware.faketouch"), Feature(true));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700633 } else if (name == "android.hardware.touchscreen.multitouch") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700634 grp->features.add(String8("android.hardware.touchscreen"), Feature(true));
Adam Lesinskica955a42016-08-01 16:44:29 -0700635 } else if (name == "android.hardware.touchscreen.multitouch.distinct" ||
636 name == "android.hardware.touchscreen.multitouch.jazzhands") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700637 grp->features.add(String8("android.hardware.touchscreen.multitouch"), Feature(true));
638 grp->features.add(String8("android.hardware.touchscreen"), Feature(true));
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700639 } else if (name == "android.hardware.opengles.aep") {
640 const int openGLESVersion31 = 0x00030001;
641 if (openGLESVersion31 > grp->openGLESVersion) {
642 grp->openGLESVersion = openGLESVersion31;
643 }
Adam Lesinski2c72b682014-06-24 09:56:01 -0700644 }
645}
646
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800647static void addImpliedFeaturesForPermission(const int targetSdk, const String8& name,
648 KeyedVector<String8, ImpliedFeature>* impliedFeatures,
649 bool impliedBySdk23Permission) {
650 if (name == "android.permission.CAMERA") {
651 addImpliedFeature(impliedFeatures, "android.hardware.camera",
Adam Lesinski43158772015-11-11 15:13:55 -0800652 String8::format("requested %s permission", name.string()),
653 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800654 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
Adam Lesinski43158772015-11-11 15:13:55 -0800655 if (targetSdk < SDK_LOLLIPOP) {
656 addImpliedFeature(impliedFeatures, "android.hardware.location.gps",
657 String8::format("requested %s permission", name.string()),
658 impliedBySdk23Permission);
659 addImpliedFeature(impliedFeatures, "android.hardware.location.gps",
660 String8::format("targetSdkVersion < %d", SDK_LOLLIPOP),
661 impliedBySdk23Permission);
662 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800663 addImpliedFeature(impliedFeatures, "android.hardware.location",
Adam Lesinski43158772015-11-11 15:13:55 -0800664 String8::format("requested %s permission", name.string()),
665 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800666 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
Adam Lesinski43158772015-11-11 15:13:55 -0800667 if (targetSdk < SDK_LOLLIPOP) {
668 addImpliedFeature(impliedFeatures, "android.hardware.location.network",
669 String8::format("requested %s permission", name.string()),
670 impliedBySdk23Permission);
671 addImpliedFeature(impliedFeatures, "android.hardware.location.network",
672 String8::format("targetSdkVersion < %d", SDK_LOLLIPOP),
673 impliedBySdk23Permission);
674 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800675 addImpliedFeature(impliedFeatures, "android.hardware.location",
Adam Lesinski43158772015-11-11 15:13:55 -0800676 String8::format("requested %s permission", name.string()),
677 impliedBySdk23Permission);
678 } else if (name == "android.permission.ACCESS_MOCK_LOCATION" ||
679 name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800680 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
681 addImpliedFeature(impliedFeatures, "android.hardware.location",
Adam Lesinski43158772015-11-11 15:13:55 -0800682 String8::format("requested %s permission", name.string()),
683 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800684 } else if (name == "android.permission.BLUETOOTH" ||
685 name == "android.permission.BLUETOOTH_ADMIN") {
Adam Lesinski43158772015-11-11 15:13:55 -0800686 if (targetSdk > SDK_DONUT) {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800687 addImpliedFeature(impliedFeatures, "android.hardware.bluetooth",
Adam Lesinski43158772015-11-11 15:13:55 -0800688 String8::format("requested %s permission", name.string()),
689 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800690 addImpliedFeature(impliedFeatures, "android.hardware.bluetooth",
Adam Lesinski43158772015-11-11 15:13:55 -0800691 String8::format("targetSdkVersion > %d", SDK_DONUT),
692 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800693 }
694 } else if (name == "android.permission.RECORD_AUDIO") {
695 addImpliedFeature(impliedFeatures, "android.hardware.microphone",
Adam Lesinski43158772015-11-11 15:13:55 -0800696 String8::format("requested %s permission", name.string()),
697 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800698 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
699 name == "android.permission.CHANGE_WIFI_STATE" ||
700 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
701 addImpliedFeature(impliedFeatures, "android.hardware.wifi",
Adam Lesinski43158772015-11-11 15:13:55 -0800702 String8::format("requested %s permission", name.string()),
703 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800704 } else if (name == "android.permission.CALL_PHONE" ||
705 name == "android.permission.CALL_PRIVILEGED" ||
706 name == "android.permission.MODIFY_PHONE_STATE" ||
707 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
708 name == "android.permission.READ_SMS" ||
709 name == "android.permission.RECEIVE_SMS" ||
710 name == "android.permission.RECEIVE_MMS" ||
711 name == "android.permission.RECEIVE_WAP_PUSH" ||
712 name == "android.permission.SEND_SMS" ||
713 name == "android.permission.WRITE_APN_SETTINGS" ||
714 name == "android.permission.WRITE_SMS") {
715 addImpliedFeature(impliedFeatures, "android.hardware.telephony",
Adam Lesinski43158772015-11-11 15:13:55 -0800716 String8("requested a telephony permission"),
717 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800718 }
719}
720
Adam Lesinski282e1812014-01-23 18:17:42 -0800721/*
722 * Handle the "dump" command, to extract select data from an archive.
723 */
724extern char CONSOLE_DATA[2925]; // see EOF
725int doDump(Bundle* bundle)
726{
727 status_t result = UNKNOWN_ERROR;
Adam Lesinski282e1812014-01-23 18:17:42 -0800728
729 if (bundle->getFileSpecCount() < 1) {
730 fprintf(stderr, "ERROR: no dump option specified\n");
731 return 1;
732 }
733
734 if (bundle->getFileSpecCount() < 2) {
735 fprintf(stderr, "ERROR: no dump file specified\n");
736 return 1;
737 }
738
739 const char* option = bundle->getFileSpecEntry(0);
740 const char* filename = bundle->getFileSpecEntry(1);
741
742 AssetManager assets;
Narayan Kamathf85e41f2014-01-24 14:14:30 +0000743 int32_t assetsCookie;
Adam Lesinski282e1812014-01-23 18:17:42 -0800744
Donald Chaid1ac6e12017-10-12 21:00:45 -0700745 // Add any dependencies passed in.
Adam Lesinski57fe4832017-05-10 15:42:22 -0700746 for (size_t i = 0; i < bundle->getPackageIncludes().size(); i++) {
747 const String8& assetPath = bundle->getPackageIncludes()[i];
748 if (!assets.addAssetPath(assetPath, NULL)) {
749 fprintf(stderr, "ERROR: included asset path %s could not be loaded\n", assetPath.string());
750 return 1;
751 }
752 }
753
Donald Chaid1ac6e12017-10-12 21:00:45 -0700754 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
755 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
756 return 1;
757 }
758
Adam Lesinski282e1812014-01-23 18:17:42 -0800759 // Make a dummy config for retrieving resources... we need to supply
760 // non-default values for some configs so that we can retrieve resources
761 // in the app that don't have a default. The most important of these is
762 // the API version because key resources like icons will have an implicit
763 // version if they are using newer config types like density.
764 ResTable_config config;
Narayan Kamath91447d82014-01-21 15:32:36 +0000765 memset(&config, 0, sizeof(ResTable_config));
Adam Lesinski282e1812014-01-23 18:17:42 -0800766 config.language[0] = 'e';
767 config.language[1] = 'n';
768 config.country[0] = 'U';
769 config.country[1] = 'S';
770 config.orientation = ResTable_config::ORIENTATION_PORT;
771 config.density = ResTable_config::DENSITY_MEDIUM;
772 config.sdkVersion = 10000; // Very high.
773 config.screenWidthDp = 320;
774 config.screenHeightDp = 480;
775 config.smallestScreenWidthDp = 320;
Adam Lesinskic2dea8d2014-08-04 16:40:41 -0700776 config.screenLayout |= ResTable_config::SCREENSIZE_NORMAL;
Adam Lesinski282e1812014-01-23 18:17:42 -0800777 assets.setConfiguration(config);
778
779 const ResTable& res = assets.getResources(false);
Dan Albert68001652014-09-09 09:51:01 -0700780 if (res.getError() != NO_ERROR) {
Adam Lesinski25e9d552014-05-19 15:01:43 -0700781 fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
Adam Lesinski63e646e2014-07-30 11:40:39 -0700782 return 1;
Adam Lesinski282e1812014-01-23 18:17:42 -0800783 }
784
Adam Lesinski694d0a72016-04-06 16:12:04 -0700785 // Source for AndroidManifest.xml
Adam Lesinski10de3af12016-07-13 10:14:03 -0700786 const String8 manifestFile("AndroidManifest.xml");
Adam Lesinski694d0a72016-04-06 16:12:04 -0700787
Adam Lesinski2cb761e2014-08-15 13:59:02 -0700788 // The dynamicRefTable can be null if there are no resources for this asset cookie.
789 // This fine.
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700790 auto noop_destructor = [](const DynamicRefTable* /*ref_table */) { };
791 auto dynamicRefTable = std::shared_ptr<const DynamicRefTable>(
792 res.getDynamicRefTableForCookie(assetsCookie), noop_destructor);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700793
794 Asset* asset = NULL;
795
Adam Lesinski282e1812014-01-23 18:17:42 -0800796 if (strcmp("resources", option) == 0) {
Elliott Hughesba3fe562015-08-12 14:49:53 -0700797#ifndef __ANDROID__
Adam Lesinski282e1812014-01-23 18:17:42 -0800798 res.print(bundle->getValues());
799#endif
800
801 } else if (strcmp("strings", option) == 0) {
802 const ResStringPool* pool = res.getTableStringBlock(0);
803 printStringPool(pool);
804
805 } else if (strcmp("xmltree", option) == 0) {
806 if (bundle->getFileSpecCount() < 3) {
807 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
808 goto bail;
809 }
810
811 for (int i=2; i<bundle->getFileSpecCount(); i++) {
812 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700813 ResXMLTree tree(dynamicRefTable);
814 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800815 if (asset == NULL) {
Adam Lesinskifcb5f7b2016-11-02 13:17:10 -0700816 fprintf(stderr, "ERROR: dump failed because resource %s not found\n", resname);
Adam Lesinski282e1812014-01-23 18:17:42 -0800817 goto bail;
818 }
819
820 if (tree.setTo(asset->getBuffer(true),
821 asset->getLength()) != NO_ERROR) {
822 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
823 goto bail;
824 }
825 tree.restart();
826 printXMLBlock(&tree);
827 tree.uninit();
828 delete asset;
829 asset = NULL;
830 }
831
832 } else if (strcmp("xmlstrings", option) == 0) {
833 if (bundle->getFileSpecCount() < 3) {
834 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
835 goto bail;
836 }
837
838 for (int i=2; i<bundle->getFileSpecCount(); i++) {
839 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700840 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800841 if (asset == NULL) {
842 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
843 goto bail;
844 }
845
Adam Lesinski63e646e2014-07-30 11:40:39 -0700846 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800847 if (tree.setTo(asset->getBuffer(true),
848 asset->getLength()) != NO_ERROR) {
849 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
850 goto bail;
851 }
852 printStringPool(&tree.getStrings());
853 delete asset;
854 asset = NULL;
855 }
856
857 } else {
Adam Lesinski63e646e2014-07-30 11:40:39 -0700858 asset = assets.openNonAsset(assetsCookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800859 if (asset == NULL) {
860 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
861 goto bail;
862 }
863
Adam Lesinski63e646e2014-07-30 11:40:39 -0700864 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800865 if (tree.setTo(asset->getBuffer(true),
866 asset->getLength()) != NO_ERROR) {
867 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
868 goto bail;
869 }
870 tree.restart();
871
872 if (strcmp("permissions", option) == 0) {
873 size_t len;
874 ResXMLTree::event_code_t code;
875 int depth = 0;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800876 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
877 code != ResXMLTree::BAD_DOCUMENT) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800878 if (code == ResXMLTree::END_TAG) {
879 depth--;
880 continue;
881 }
882 if (code != ResXMLTree::START_TAG) {
883 continue;
884 }
885 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700886 const char16_t* ctag16 = tree.getElementName(&len);
887 if (ctag16 == NULL) {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700888 SourcePos(manifestFile, tree.getLineNumber()).error(
889 "ERROR: failed to get XML element name (bad string pool)");
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700890 goto bail;
891 }
892 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800893 //printf("Depth %d tag %s\n", depth, tag.string());
894 if (depth == 1) {
895 if (tag != "manifest") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700896 SourcePos(manifestFile, tree.getLineNumber()).error(
897 "ERROR: manifest does not start with <manifest> tag");
Adam Lesinski282e1812014-01-23 18:17:42 -0800898 goto bail;
899 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700900 String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -0700901 printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800902 } else if (depth == 2) {
903 if (tag == "permission") {
904 String8 error;
905 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
906 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700907 SourcePos(manifestFile, tree.getLineNumber()).error(
908 "ERROR getting 'android:name': %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800909 goto bail;
910 }
911
912 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700913 SourcePos(manifestFile, tree.getLineNumber()).error(
914 "ERROR: missing 'android:name' for permission");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800915 goto bail;
916 }
917 printf("permission: %s\n",
918 ResTable::normalizeForOutput(name.string()).string());
919 } else if (tag == "uses-permission") {
920 String8 error;
921 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
922 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700923 SourcePos(manifestFile, tree.getLineNumber()).error(
924 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800925 goto bail;
926 }
927
928 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700929 SourcePos(manifestFile, tree.getLineNumber()).error(
930 "ERROR: missing 'android:name' for uses-permission");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800931 goto bail;
932 }
933 printUsesPermission(name,
934 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
935 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
936 } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
937 String8 error;
938 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
939 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700940 SourcePos(manifestFile, tree.getLineNumber()).error(
941 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800942 goto bail;
943 }
944
945 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700946 SourcePos(manifestFile, tree.getLineNumber()).error(
947 "ERROR: missing 'android:name' for uses-permission-sdk-23");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800948 goto bail;
949 }
950 printUsesPermissionSdk23(
951 name,
952 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
Adam Lesinski282e1812014-01-23 18:17:42 -0800953 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800954 }
955 }
956 } else if (strcmp("badging", option) == 0) {
957 Vector<String8> locales;
958 res.getLocales(&locales);
959
960 Vector<ResTable_config> configs;
961 res.getConfigurations(&configs);
962 SortedVector<int> densities;
963 const size_t NC = configs.size();
964 for (size_t i=0; i<NC; i++) {
965 int dens = configs[i].density;
966 if (dens == 0) {
967 dens = 160;
968 }
969 densities.add(dens);
970 }
971
972 size_t len;
973 ResXMLTree::event_code_t code;
974 int depth = 0;
975 String8 error;
976 bool withinActivity = false;
977 bool isMainActivity = false;
978 bool isLauncherActivity = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800979 bool isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800980 bool isSearchable = false;
981 bool withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700982 bool withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700983 bool withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800984 bool withinReceiver = false;
985 bool withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700986 bool withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800987 bool withinIntentFilter = false;
988 bool hasMainActivity = false;
989 bool hasOtherActivities = false;
990 bool hasOtherReceivers = false;
991 bool hasOtherServices = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700992 bool hasIntentFilter = false;
993
Adam Lesinski282e1812014-01-23 18:17:42 -0800994 bool hasWallpaperService = false;
995 bool hasImeService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700996 bool hasAccessibilityService = false;
997 bool hasPrintService = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800998 bool hasWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700999 bool hasDeviceAdminReceiver = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001000 bool hasPaymentService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001001 bool hasDocumentsProvider = false;
1002 bool hasCameraActivity = false;
1003 bool hasCameraSecureActivity = false;
1004 bool hasLauncher = false;
1005 bool hasNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001006 bool hasDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001007
Adam Lesinski282e1812014-01-23 18:17:42 -08001008 bool actMainActivity = false;
1009 bool actWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001010 bool actDeviceAdminEnabled = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001011 bool actImeService = false;
1012 bool actWallpaperService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001013 bool actAccessibilityService = false;
1014 bool actPrintService = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001015 bool actHostApduService = false;
1016 bool actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001017 bool actDocumentsProvider = false;
1018 bool actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001019 bool actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001020 bool actCamera = false;
1021 bool actCameraSecure = false;
1022 bool catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001023 bool hasMetaHostPaymentCategory = false;
1024 bool hasMetaOffHostPaymentCategory = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001025
1026 // These permissions are required by services implementing services
1027 // the system binds to (IME, Accessibility, PrintServices, etc.)
1028 bool hasBindDeviceAdminPermission = false;
1029 bool hasBindInputMethodPermission = false;
1030 bool hasBindAccessibilityServicePermission = false;
1031 bool hasBindPrintServicePermission = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001032 bool hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001033 bool hasRequiredSafAttributes = false;
1034 bool hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001035 bool hasBindDreamServicePermission = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001036
1037 // These two implement the implicit permissions that are granted
1038 // to pre-1.6 applications.
1039 bool hasWriteExternalStoragePermission = false;
Adam Lesinski2386df22016-12-28 15:08:58 -05001040 int32_t writeExternalStoragePermissionMaxSdkVersion = -1;
Adam Lesinski282e1812014-01-23 18:17:42 -08001041 bool hasReadPhoneStatePermission = false;
1042
1043 // If an app requests write storage, they will also get read storage.
1044 bool hasReadExternalStoragePermission = false;
1045
1046 // Implement transition to read and write call log.
1047 bool hasReadContactsPermission = false;
1048 bool hasWriteContactsPermission = false;
1049 bool hasReadCallLogPermission = false;
1050 bool hasWriteCallLogPermission = false;
1051
Adam Lesinskie47fd122014-08-15 22:25:36 -07001052 // If an app declares itself as multiArch, we report the
1053 // native libraries differently.
1054 bool hasMultiArch = false;
1055
Adam Lesinski282e1812014-01-23 18:17:42 -08001056 // This next group of variables is used to implement a group of
1057 // backward-compatibility heuristics necessitated by the addition of
1058 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
1059 // heuristic is "if an app requests a permission but doesn't explicitly
1060 // request the corresponding <uses-feature>, presume it's there anyway".
Adam Lesinski2c72b682014-06-24 09:56:01 -07001061
Adam Lesinski282e1812014-01-23 18:17:42 -08001062 // 2.2 also added some other features that apps can request, but that
1063 // have no corresponding permission, so we cannot implement any
1064 // back-compatibility heuristic for them. The below are thus unnecessary
1065 // (but are retained here for documentary purposes.)
1066 //bool specCompassFeature = false;
1067 //bool specAccelerometerFeature = false;
1068 //bool specProximityFeature = false;
1069 //bool specAmbientLightFeature = false;
1070 //bool specLiveWallpaperFeature = false;
1071
1072 int targetSdk = 0;
1073 int smallScreen = 1;
1074 int normalScreen = 1;
1075 int largeScreen = 1;
1076 int xlargeScreen = 1;
1077 int anyDensity = 1;
1078 int requiresSmallestWidthDp = 0;
1079 int compatibleWidthLimitDp = 0;
1080 int largestWidthLimitDp = 0;
1081 String8 pkg;
1082 String8 activityName;
1083 String8 activityLabel;
1084 String8 activityIcon;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001085 String8 activityBanner;
Adam Lesinski282e1812014-01-23 18:17:42 -08001086 String8 receiverName;
1087 String8 serviceName;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001088 Vector<String8> supportedInput;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001089
1090 FeatureGroup commonFeatures;
1091 Vector<FeatureGroup> featureGroups;
1092 KeyedVector<String8, ImpliedFeature> impliedFeatures;
1093
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001094 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
1095 code != ResXMLTree::BAD_DOCUMENT) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001096 if (code == ResXMLTree::END_TAG) {
1097 depth--;
1098 if (depth < 2) {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001099 if (withinSupportsInput && !supportedInput.isEmpty()) {
1100 printf("supports-input: '");
1101 const size_t N = supportedInput.size();
1102 for (size_t i=0; i<N; i++) {
Maurice Chu2675f762013-10-22 17:33:11 -07001103 printf("%s", ResTable::normalizeForOutput(
1104 supportedInput[i].string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001105 if (i != N - 1) {
1106 printf("' '");
1107 } else {
1108 printf("'\n");
1109 }
1110 }
1111 supportedInput.clear();
1112 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001113 withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001114 withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001115 withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001116 } else if (depth < 3) {
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001117 if (withinActivity && isMainActivity) {
Maurice Chu2675f762013-10-22 17:33:11 -07001118 String8 aName(getComponentName(pkg, activityName));
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001119 if (isLauncherActivity) {
1120 printf("launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001121 if (aName.length() > 0) {
1122 printf(" name='%s' ",
1123 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001124 }
1125 printf(" label='%s' icon='%s'\n",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001126 ResTable::normalizeForOutput(activityLabel.string())
1127 .string(),
1128 ResTable::normalizeForOutput(activityIcon.string())
1129 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001130 }
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001131 if (isLeanbackLauncherActivity) {
1132 printf("leanback-launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001133 if (aName.length() > 0) {
1134 printf(" name='%s' ",
1135 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001136 }
1137 printf(" label='%s' icon='%s' banner='%s'\n",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001138 ResTable::normalizeForOutput(activityLabel.string())
1139 .string(),
1140 ResTable::normalizeForOutput(activityIcon.string())
1141 .string(),
1142 ResTable::normalizeForOutput(activityBanner.string())
1143 .string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001144 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001145 }
1146 if (!hasIntentFilter) {
1147 hasOtherActivities |= withinActivity;
1148 hasOtherReceivers |= withinReceiver;
1149 hasOtherServices |= withinService;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001150 } else {
1151 if (withinService) {
1152 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
1153 hasBindNfcServicePermission);
1154 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
1155 hasBindNfcServicePermission);
1156 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001157 }
1158 withinActivity = false;
1159 withinService = false;
1160 withinReceiver = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001161 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001162 hasIntentFilter = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001163 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001164 } else if (depth < 4) {
1165 if (withinIntentFilter) {
1166 if (withinActivity) {
1167 hasMainActivity |= actMainActivity;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001168 hasLauncher |= catLauncher;
1169 hasCameraActivity |= actCamera;
1170 hasCameraSecureActivity |= actCameraSecure;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001171 hasOtherActivities |=
1172 !actMainActivity && !actCamera && !actCameraSecure;
Adam Lesinski282e1812014-01-23 18:17:42 -08001173 } else if (withinReceiver) {
1174 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001175 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
1176 hasBindDeviceAdminPermission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001177 hasOtherReceivers |=
1178 (!actWidgetReceivers && !actDeviceAdminEnabled);
Adam Lesinski282e1812014-01-23 18:17:42 -08001179 } else if (withinService) {
1180 hasImeService |= actImeService;
1181 hasWallpaperService |= actWallpaperService;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001182 hasAccessibilityService |= (actAccessibilityService &&
1183 hasBindAccessibilityServicePermission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001184 hasPrintService |=
1185 (actPrintService && hasBindPrintServicePermission);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001186 hasNotificationListenerService |= actNotificationListenerService &&
1187 hasBindNotificationListenerServicePermission;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001188 hasDreamService |= actDreamService && hasBindDreamServicePermission;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001189 hasOtherServices |= (!actImeService && !actWallpaperService &&
Adam Lesinski94fc9122013-09-30 17:16:09 -07001190 !actAccessibilityService && !actPrintService &&
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001191 !actHostApduService && !actOffHostApduService &&
1192 !actNotificationListenerService);
1193 } else if (withinProvider) {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001194 hasDocumentsProvider |=
1195 actDocumentsProvider && hasRequiredSafAttributes;
Adam Lesinski282e1812014-01-23 18:17:42 -08001196 }
1197 }
1198 withinIntentFilter = false;
1199 }
1200 continue;
1201 }
1202 if (code != ResXMLTree::START_TAG) {
1203 continue;
1204 }
1205 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001206
1207 const char16_t* ctag16 = tree.getElementName(&len);
1208 if (ctag16 == NULL) {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001209 SourcePos(manifestFile, tree.getLineNumber()).error(
1210 "ERROR: failed to get XML element name (bad string pool)");
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001211 goto bail;
1212 }
1213 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -08001214 //printf("Depth %d, %s\n", depth, tag.string());
1215 if (depth == 1) {
1216 if (tag != "manifest") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001217 SourcePos(manifestFile, tree.getLineNumber()).error(
1218 "ERROR: manifest does not start with <manifest> tag");
Adam Lesinski282e1812014-01-23 18:17:42 -08001219 goto bail;
1220 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001221 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -07001222 printf("package: name='%s' ",
1223 ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001224 int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR,
1225 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001226 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001227 SourcePos(manifestFile, tree.getLineNumber()).error(
1228 "ERROR getting 'android:versionCode' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001229 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001230 goto bail;
1231 }
1232 if (versionCode > 0) {
1233 printf("versionCode='%d' ", versionCode);
1234 } else {
1235 printf("versionCode='' ");
1236 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001237 String8 versionName = AaptXml::getResolvedAttribute(res, tree,
1238 VERSION_NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001239 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001240 SourcePos(manifestFile, tree.getLineNumber()).error(
1241 "ERROR getting 'android:versionName' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001242 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001243 goto bail;
1244 }
Adam Lesinski25d35a92014-08-11 09:41:56 -07001245 printf("versionName='%s'",
Maurice Chu2675f762013-10-22 17:33:11 -07001246 ResTable::normalizeForOutput(versionName.string()).string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001247
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001248 String8 splitName = AaptXml::getAttribute(tree, NULL, "split");
Adam Lesinski25d35a92014-08-11 09:41:56 -07001249 if (!splitName.isEmpty()) {
1250 printf(" split='%s'", ResTable::normalizeForOutput(
1251 splitName.string()).string());
1252 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001253
Alan Viverette11be9312017-11-09 15:41:44 -05001254 String8 platformBuildVersionName = AaptXml::getAttribute(tree, NULL,
Adam Lesinski5283fab2014-08-29 11:23:55 -07001255 "platformBuildVersionName");
Alan Viverette11be9312017-11-09 15:41:44 -05001256 if (platformBuildVersionName != "") {
1257 printf(" platformBuildVersionName='%s'", platformBuildVersionName.string());
1258 }
1259
1260 String8 platformBuildVersionCode = AaptXml::getAttribute(tree, NULL,
1261 "platformBuildVersionCode");
1262 if (platformBuildVersionCode != "") {
1263 printf(" platformBuildVersionCode='%s'", platformBuildVersionCode.string());
1264 }
1265
1266 int32_t compileSdkVersion = AaptXml::getIntegerAttribute(tree,
1267 COMPILE_SDK_VERSION_ATTR, &error);
1268 if (error != "") {
1269 SourcePos(manifestFile, tree.getLineNumber()).error(
1270 "ERROR getting 'android:compileSdkVersion' attribute: %s",
1271 error.string());
1272 goto bail;
1273 }
1274 if (compileSdkVersion > 0) {
1275 printf(" compileSdkVersion='%d'", compileSdkVersion);
1276 }
1277
1278 String8 compileSdkVersionCodename = AaptXml::getResolvedAttribute(res, tree,
1279 COMPILE_SDK_VERSION_CODENAME_ATTR, &error);
1280 if (compileSdkVersionCodename != "") {
1281 printf(" compileSdkVersionCodename='%s'", ResTable::normalizeForOutput(
1282 compileSdkVersionCodename.string()).string());
1283 }
1284
Adam Lesinski25d35a92014-08-11 09:41:56 -07001285 printf("\n");
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001286
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001287 int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
1288 INSTALL_LOCATION_ATTR, &error);
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001289 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001290 SourcePos(manifestFile, tree.getLineNumber()).error(
1291 "ERROR getting 'android:installLocation' attribute: %s",
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001292 error.string());
1293 goto bail;
1294 }
1295
1296 if (installLocation >= 0) {
1297 printf("install-location:'");
1298 switch (installLocation) {
1299 case 0:
1300 printf("auto");
1301 break;
1302 case 1:
1303 printf("internalOnly");
1304 break;
1305 case 2:
1306 printf("preferExternal");
1307 break;
1308 default:
1309 fprintf(stderr, "Invalid installLocation %d\n", installLocation);
1310 goto bail;
1311 }
1312 printf("'\n");
1313 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001314 } else if (depth == 2) {
1315 withinApplication = false;
1316 if (tag == "application") {
1317 withinApplication = true;
1318
1319 String8 label;
1320 const size_t NL = locales.size();
1321 for (size_t i=0; i<NL; i++) {
1322 const char* localeStr = locales[i].string();
Adam Lesinskia77685f2016-10-03 16:26:28 -07001323 assets.setConfiguration(config, localeStr != NULL ? localeStr : "");
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001324 String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1325 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001326 if (llabel != "") {
1327 if (localeStr == NULL || strlen(localeStr) == 0) {
1328 label = llabel;
Maurice Chu2675f762013-10-22 17:33:11 -07001329 printf("application-label:'%s'\n",
1330 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001331 } else {
1332 if (label == "") {
1333 label = llabel;
1334 }
1335 printf("application-label-%s:'%s'\n", localeStr,
Maurice Chu2675f762013-10-22 17:33:11 -07001336 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001337 }
1338 }
1339 }
1340
1341 ResTable_config tmpConfig = config;
1342 const size_t ND = densities.size();
1343 for (size_t i=0; i<ND; i++) {
1344 tmpConfig.density = densities[i];
1345 assets.setConfiguration(tmpConfig);
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001346 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1347 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001348 if (icon != "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001349 printf("application-icon-%d:'%s'\n", densities[i],
1350 ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001351 }
1352 }
1353 assets.setConfiguration(config);
1354
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001355 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001356 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001357 SourcePos(manifestFile, tree.getLineNumber()).error(
1358 "ERROR getting 'android:icon' attribute: %s", error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001359 goto bail;
1360 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001361 int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0,
1362 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001363 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001364 SourcePos(manifestFile, tree.getLineNumber()).error(
1365 "ERROR getting 'android:testOnly' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001366 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001367 goto bail;
1368 }
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001369
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001370 String8 banner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1371 &error);
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001372 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001373 SourcePos(manifestFile, tree.getLineNumber()).error(
1374 "ERROR getting 'android:banner' attribute: %s", error.string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001375 goto bail;
1376 }
Maurice Chu2675f762013-10-22 17:33:11 -07001377 printf("application: label='%s' ",
1378 ResTable::normalizeForOutput(label.string()).string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001379 printf("icon='%s'", ResTable::normalizeForOutput(icon.string()).string());
1380 if (banner != "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001381 printf(" banner='%s'",
1382 ResTable::normalizeForOutput(banner.string()).string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001383 }
1384 printf("\n");
Adam Lesinski282e1812014-01-23 18:17:42 -08001385 if (testOnly != 0) {
1386 printf("testOnly='%d'\n", testOnly);
1387 }
1388
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001389 int32_t isGame = AaptXml::getResolvedIntegerAttribute(res, tree,
1390 ISGAME_ATTR, 0, &error);
1391 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001392 SourcePos(manifestFile, tree.getLineNumber()).error(
1393 "ERROR getting 'android:isGame' attribute: %s", error.string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001394 goto bail;
1395 }
1396 if (isGame != 0) {
1397 printf("application-isGame\n");
1398 }
1399
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001400 int32_t debuggable = AaptXml::getResolvedIntegerAttribute(res, tree,
1401 DEBUGGABLE_ATTR, 0, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001402 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001403 SourcePos(manifestFile, tree.getLineNumber()).error(
1404 "ERROR getting 'android:debuggable' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001405 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001406 goto bail;
1407 }
1408 if (debuggable != 0) {
1409 printf("application-debuggable\n");
1410 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07001411
1412 // We must search by name because the multiArch flag hasn't been API
1413 // frozen yet.
1414 int32_t multiArchIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
1415 "multiArch");
1416 if (multiArchIndex >= 0) {
1417 Res_value value;
1418 if (tree.getAttributeValue(multiArchIndex, &value) != NO_ERROR) {
1419 if (value.dataType >= Res_value::TYPE_FIRST_INT &&
1420 value.dataType <= Res_value::TYPE_LAST_INT) {
1421 hasMultiArch = value.data;
1422 }
1423 }
1424 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001425 } else if (tag == "uses-sdk") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001426 int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR,
1427 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001428 if (error != "") {
1429 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001430 String8 name = AaptXml::getResolvedAttribute(res, tree,
1431 MIN_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001432 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001433 SourcePos(manifestFile, tree.getLineNumber()).error(
1434 "ERROR getting 'android:minSdkVersion' attribute: %s",
Adam Lesinski282e1812014-01-23 18:17:42 -08001435 error.string());
1436 goto bail;
1437 }
1438 if (name == "Donut") targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001439 printf("sdkVersion:'%s'\n",
1440 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001441 } else if (code != -1) {
1442 targetSdk = code;
1443 printf("sdkVersion:'%d'\n", code);
1444 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001445 code = AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -08001446 if (code != -1) {
1447 printf("maxSdkVersion:'%d'\n", code);
1448 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001449 code = AaptXml::getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001450 if (error != "") {
1451 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001452 String8 name = AaptXml::getResolvedAttribute(res, tree,
1453 TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001454 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001455 SourcePos(manifestFile, tree.getLineNumber()).error(
1456 "ERROR getting 'android:targetSdkVersion' attribute: %s",
Adam Lesinski282e1812014-01-23 18:17:42 -08001457 error.string());
1458 goto bail;
1459 }
1460 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001461 printf("targetSdkVersion:'%s'\n",
1462 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001463 } else if (code != -1) {
1464 if (targetSdk < code) {
1465 targetSdk = code;
1466 }
1467 printf("targetSdkVersion:'%d'\n", code);
1468 }
1469 } else if (tag == "uses-configuration") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001470 int32_t reqTouchScreen = AaptXml::getIntegerAttribute(tree,
1471 REQ_TOUCH_SCREEN_ATTR, 0);
1472 int32_t reqKeyboardType = AaptXml::getIntegerAttribute(tree,
1473 REQ_KEYBOARD_TYPE_ATTR, 0);
1474 int32_t reqHardKeyboard = AaptXml::getIntegerAttribute(tree,
1475 REQ_HARD_KEYBOARD_ATTR, 0);
1476 int32_t reqNavigation = AaptXml::getIntegerAttribute(tree,
1477 REQ_NAVIGATION_ATTR, 0);
1478 int32_t reqFiveWayNav = AaptXml::getIntegerAttribute(tree,
1479 REQ_FIVE_WAY_NAV_ATTR, 0);
Adam Lesinski282e1812014-01-23 18:17:42 -08001480 printf("uses-configuration:");
1481 if (reqTouchScreen != 0) {
1482 printf(" reqTouchScreen='%d'", reqTouchScreen);
1483 }
1484 if (reqKeyboardType != 0) {
1485 printf(" reqKeyboardType='%d'", reqKeyboardType);
1486 }
1487 if (reqHardKeyboard != 0) {
1488 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1489 }
1490 if (reqNavigation != 0) {
1491 printf(" reqNavigation='%d'", reqNavigation);
1492 }
1493 if (reqFiveWayNav != 0) {
1494 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1495 }
1496 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001497 } else if (tag == "supports-input") {
1498 withinSupportsInput = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001499 } else if (tag == "supports-screens") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001500 smallScreen = AaptXml::getIntegerAttribute(tree,
1501 SMALL_SCREEN_ATTR, 1);
1502 normalScreen = AaptXml::getIntegerAttribute(tree,
1503 NORMAL_SCREEN_ATTR, 1);
1504 largeScreen = AaptXml::getIntegerAttribute(tree,
1505 LARGE_SCREEN_ATTR, 1);
1506 xlargeScreen = AaptXml::getIntegerAttribute(tree,
1507 XLARGE_SCREEN_ATTR, 1);
1508 anyDensity = AaptXml::getIntegerAttribute(tree,
1509 ANY_DENSITY_ATTR, 1);
1510 requiresSmallestWidthDp = AaptXml::getIntegerAttribute(tree,
1511 REQUIRES_SMALLEST_WIDTH_DP_ATTR, 0);
1512 compatibleWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1513 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, 0);
1514 largestWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1515 LARGEST_WIDTH_LIMIT_DP_ATTR, 0);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001516 } else if (tag == "feature-group") {
1517 withinFeatureGroup = true;
1518 FeatureGroup group;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001519 group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001520 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001521 SourcePos(manifestFile, tree.getLineNumber()).error(
1522 "ERROR getting 'android:label' attribute: %s", error.string());
Adam Lesinski2c72b682014-06-24 09:56:01 -07001523 goto bail;
1524 }
1525 featureGroups.add(group);
1526
Adam Lesinski282e1812014-01-23 18:17:42 -08001527 } else if (tag == "uses-feature") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001528 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001529 if (name != "" && error == "") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001530 const char* androidSchema =
1531 "http://schemas.android.com/apk/res/android";
Adam Lesinski282e1812014-01-23 18:17:42 -08001532
Adam Lesinski694d0a72016-04-06 16:12:04 -07001533 int32_t req = AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1,
1534 &error);
1535 if (error != "") {
1536 SourcePos(manifestFile, tree.getLineNumber()).error(
1537 "failed to read attribute 'android:required': %s",
1538 error.string());
1539 goto bail;
1540 }
1541
1542 int32_t version = AaptXml::getIntegerAttribute(tree, androidSchema,
1543 "version", 0, &error);
1544 if (error != "") {
1545 SourcePos(manifestFile, tree.getLineNumber()).error(
1546 "failed to read attribute 'android:version': %s",
1547 error.string());
1548 goto bail;
1549 }
1550
1551 commonFeatures.features.add(name, Feature(req != 0, version));
Adam Lesinski2c72b682014-06-24 09:56:01 -07001552 if (req) {
1553 addParentFeatures(&commonFeatures, name);
Adam Lesinski282e1812014-01-23 18:17:42 -08001554 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001555 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001556 int vers = AaptXml::getIntegerAttribute(tree,
Adam Lesinski282e1812014-01-23 18:17:42 -08001557 GL_ES_VERSION_ATTR, &error);
1558 if (error == "") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001559 if (vers > commonFeatures.openGLESVersion) {
1560 commonFeatures.openGLESVersion = vers;
1561 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001562 }
1563 }
1564 } else if (tag == "uses-permission") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001565 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001566 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001567 SourcePos(manifestFile, tree.getLineNumber()).error(
1568 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001569 goto bail;
1570 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001571
1572 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001573 SourcePos(manifestFile, tree.getLineNumber()).error(
1574 "ERROR: missing 'android:name' for uses-permission");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001575 goto bail;
1576 }
1577
1578 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, false);
1579
Adam Lesinski2386df22016-12-28 15:08:58 -05001580 const int32_t maxSdkVersion =
1581 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, -1);
Dianne Hackborncd154e92017-02-28 17:37:35 -08001582 const String8 requiredFeature = AaptXml::getAttribute(tree,
1583 REQUIRED_FEATURE_ATTR, &error);
1584 const String8 requiredNotFeature = AaptXml::getAttribute(tree,
1585 REQUIRED_NOT_FEATURE_ATTR, &error);
Adam Lesinski2386df22016-12-28 15:08:58 -05001586
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001587 if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1588 hasWriteExternalStoragePermission = true;
Adam Lesinski2386df22016-12-28 15:08:58 -05001589 writeExternalStoragePermissionMaxSdkVersion = maxSdkVersion;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001590 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1591 hasReadExternalStoragePermission = true;
1592 } else if (name == "android.permission.READ_PHONE_STATE") {
1593 hasReadPhoneStatePermission = true;
1594 } else if (name == "android.permission.READ_CONTACTS") {
1595 hasReadContactsPermission = true;
1596 } else if (name == "android.permission.WRITE_CONTACTS") {
1597 hasWriteContactsPermission = true;
1598 } else if (name == "android.permission.READ_CALL_LOG") {
1599 hasReadCallLogPermission = true;
1600 } else if (name == "android.permission.WRITE_CALL_LOG") {
1601 hasWriteCallLogPermission = true;
1602 }
1603
1604 printUsesPermission(name,
1605 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
Dianne Hackborncd154e92017-02-28 17:37:35 -08001606 maxSdkVersion, requiredFeature, requiredNotFeature);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001607
1608 } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
1609 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1610 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001611 SourcePos(manifestFile, tree.getLineNumber()).error(
1612 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001613 goto bail;
1614 }
1615
1616 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001617 SourcePos(manifestFile, tree.getLineNumber()).error(
1618 "ERROR: missing 'android:name' for uses-permission-sdk-23");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001619 goto bail;
1620 }
1621
1622 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, true);
1623
1624 printUsesPermissionSdk23(
1625 name, AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
1626
Adam Lesinski282e1812014-01-23 18:17:42 -08001627 } else if (tag == "uses-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001628 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001629 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001630 printf("uses-package:'%s'\n",
1631 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001632 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001633 SourcePos(manifestFile, tree.getLineNumber()).error(
1634 "ERROR getting 'android:name' attribute: %s", error.string());
1635 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001636 }
1637 } else if (tag == "original-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001638 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001639 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001640 printf("original-package:'%s'\n",
1641 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001642 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001643 SourcePos(manifestFile, tree.getLineNumber()).error(
1644 "ERROR getting 'android:name' attribute: %s", error.string());
1645 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001646 }
1647 } else if (tag == "supports-gl-texture") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001648 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001649 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001650 printf("supports-gl-texture:'%s'\n",
1651 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001652 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001653 SourcePos(manifestFile, tree.getLineNumber()).error(
1654 "ERROR getting 'android:name' attribute: %s", error.string());
1655 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001656 }
1657 } else if (tag == "compatible-screens") {
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001658 printCompatibleScreens(tree, &error);
1659 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001660 SourcePos(manifestFile, tree.getLineNumber()).error(
1661 "ERROR getting compatible screens: %s", error.string());
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001662 goto bail;
1663 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001664 depth--;
1665 } else if (tag == "package-verifier") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001666 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001667 if (name != "" && error == "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001668 String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR,
1669 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001670 if (publicKey != "" && error == "") {
1671 printf("package-verifier: name='%s' publicKey='%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001672 ResTable::normalizeForOutput(name.string()).string(),
1673 ResTable::normalizeForOutput(publicKey.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001674 }
1675 }
1676 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001677 } else if (depth == 3) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001678 withinActivity = false;
1679 withinReceiver = false;
1680 withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001681 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001682 hasIntentFilter = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001683 hasMetaHostPaymentCategory = false;
1684 hasMetaOffHostPaymentCategory = false;
1685 hasBindDeviceAdminPermission = false;
1686 hasBindInputMethodPermission = false;
1687 hasBindAccessibilityServicePermission = false;
1688 hasBindPrintServicePermission = false;
1689 hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001690 hasRequiredSafAttributes = false;
1691 hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001692 hasBindDreamServicePermission = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001693 if (withinApplication) {
1694 if(tag == "activity") {
1695 withinActivity = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001696 activityName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001697 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001698 SourcePos(manifestFile, tree.getLineNumber()).error(
1699 "ERROR getting 'android:name' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001700 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001701 goto bail;
1702 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001703
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001704 activityLabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1705 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001706 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001707 SourcePos(manifestFile, tree.getLineNumber()).error(
1708 "ERROR getting 'android:label' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001709 error.string());
1710 goto bail;
1711 }
1712
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001713 activityIcon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1714 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001715 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001716 SourcePos(manifestFile, tree.getLineNumber()).error(
1717 "ERROR getting 'android:icon' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001718 error.string());
1719 goto bail;
1720 }
1721
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001722 activityBanner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1723 &error);
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001724 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001725 SourcePos(manifestFile, tree.getLineNumber()).error(
1726 "ERROR getting 'android:banner' attribute: %s",
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001727 error.string());
1728 goto bail;
1729 }
1730
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001731 int32_t orien = AaptXml::getResolvedIntegerAttribute(res, tree,
Michael Wrightec4fdec2013-09-06 16:50:52 -07001732 SCREEN_ORIENTATION_ATTR, &error);
1733 if (error == "") {
1734 if (orien == 0 || orien == 6 || orien == 8) {
1735 // Requests landscape, sensorLandscape, or reverseLandscape.
Adam Lesinski43158772015-11-11 15:13:55 -08001736 addImpliedFeature(
1737 &impliedFeatures, "android.hardware.screen.landscape",
1738 String8("one or more activities have specified a "
1739 "landscape orientation"),
1740 false);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001741 } else if (orien == 1 || orien == 7 || orien == 9) {
1742 // Requests portrait, sensorPortrait, or reversePortrait.
Adam Lesinski43158772015-11-11 15:13:55 -08001743 addImpliedFeature(
1744 &impliedFeatures, "android.hardware.screen.portrait",
1745 String8("one or more activities have specified a "
1746 "portrait orientation"),
1747 false);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001748 }
1749 }
1750 } else if (tag == "uses-library") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001751 String8 libraryName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001752 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001753 SourcePos(manifestFile, tree.getLineNumber()).error(
Michael Wrightec4fdec2013-09-06 16:50:52 -07001754 "ERROR getting 'android:name' attribute for uses-library"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001755 " %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001756 goto bail;
1757 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001758 int req = AaptXml::getIntegerAttribute(tree,
1759 REQUIRED_ATTR, 1);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001760 printf("uses-library%s:'%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001761 req ? "" : "-not-required", ResTable::normalizeForOutput(
1762 libraryName.string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001763 } else if (tag == "receiver") {
1764 withinReceiver = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001765 receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001766
1767 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001768 SourcePos(manifestFile, tree.getLineNumber()).error(
Michael Wrightec4fdec2013-09-06 16:50:52 -07001769 "ERROR getting 'android:name' attribute for receiver:"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001770 " %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001771 goto bail;
1772 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001773
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001774 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1775 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001776 if (error == "") {
1777 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1778 hasBindDeviceAdminPermission = true;
1779 }
1780 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001781 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001782 "ERROR getting 'android:permission' attribute for"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001783 " receiver '%s': %s",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001784 receiverName.string(), error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001785 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001786 } else if (tag == "service") {
1787 withinService = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001788 serviceName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001789
1790 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001791 SourcePos(manifestFile, tree.getLineNumber()).error(
1792 "ERROR getting 'android:name' attribute for "
1793 "service:%s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001794 goto bail;
1795 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001796
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001797 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1798 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001799 if (error == "") {
1800 if (permission == "android.permission.BIND_INPUT_METHOD") {
1801 hasBindInputMethodPermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001802 } else if (permission ==
1803 "android.permission.BIND_ACCESSIBILITY_SERVICE") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001804 hasBindAccessibilityServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001805 } else if (permission ==
1806 "android.permission.BIND_PRINT_SERVICE") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001807 hasBindPrintServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001808 } else if (permission ==
1809 "android.permission.BIND_NFC_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07001810 hasBindNfcServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001811 } else if (permission ==
1812 "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001813 hasBindNotificationListenerServicePermission = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001814 } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
1815 hasBindDreamServicePermission = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001816 }
1817 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001818 SourcePos(manifestFile, tree.getLineNumber()).error(
1819 "ERROR getting 'android:permission' attribute for "
1820 "service '%s': %s", serviceName.string(), error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001821 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001822 } else if (tag == "provider") {
1823 withinProvider = true;
1824
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001825 bool exported = AaptXml::getResolvedIntegerAttribute(res, tree,
1826 EXPORTED_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001827 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001828 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001829 "ERROR getting 'android:exported' attribute for provider:"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001830 " %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001831 goto bail;
1832 }
1833
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001834 bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute(
1835 res, tree, GRANT_URI_PERMISSIONS_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001836 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001837 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001838 "ERROR getting 'android:grantUriPermissions' attribute for "
Adam Lesinski10de3af12016-07-13 10:14:03 -07001839 "provider: %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001840 goto bail;
1841 }
1842
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001843 String8 permission = AaptXml::getResolvedAttribute(res, tree,
1844 PERMISSION_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001845 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001846 SourcePos(manifestFile, tree.getLineNumber()).error(
1847 "ERROR getting 'android:permission' attribute for "
1848 "provider: %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001849 goto bail;
1850 }
1851
1852 hasRequiredSafAttributes |= exported && grantUriPermissions &&
1853 permission == "android.permission.MANAGE_DOCUMENTS";
1854
Michael Wrightec4fdec2013-09-06 16:50:52 -07001855 } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001856 String8 metaDataName = AaptXml::getResolvedAttribute(res, tree,
1857 NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001858 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001859 SourcePos(manifestFile, tree.getLineNumber()).error(
1860 "ERROR getting 'android:name' attribute for "
1861 "meta-data: %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001862 goto bail;
1863 }
Maurice Chu2675f762013-10-22 17:33:11 -07001864 printf("meta-data: name='%s' ",
1865 ResTable::normalizeForOutput(metaDataName.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001866 printResolvedResourceAttribute(res, tree, VALUE_ATTR, String8("value"),
Maurice Chu76327312013-10-16 18:28:46 -07001867 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001868 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001869 // Try looking for a RESOURCE_ATTR
1870 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001871 printResolvedResourceAttribute(res, tree, RESOURCE_ATTR,
Maurice Chu76327312013-10-16 18:28:46 -07001872 String8("resource"), &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001873 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001874 SourcePos(manifestFile, tree.getLineNumber()).error(
1875 "ERROR getting 'android:value' or "
Maurice Chu76327312013-10-16 18:28:46 -07001876 "'android:resource' attribute for "
Adam Lesinski10de3af12016-07-13 10:14:03 -07001877 "meta-data: %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001878 goto bail;
1879 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001880 }
Maurice Chu76327312013-10-16 18:28:46 -07001881 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001882 } else if (withinSupportsInput && tag == "input-type") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001883 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001884 if (name != "" && error == "") {
1885 supportedInput.add(name);
1886 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001887 SourcePos(manifestFile, tree.getLineNumber()).error(
1888 "ERROR getting 'android:name' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001889 error.string());
1890 goto bail;
1891 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001892 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001893 } else if (withinFeatureGroup && tag == "uses-feature") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001894 const String8 androidSchema("http://schemas.android.com/apk/res/android");
Adam Lesinski2c72b682014-06-24 09:56:01 -07001895 FeatureGroup& top = featureGroups.editTop();
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001896
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001897 String8 name = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001898 if (name != "" && error == "") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001899 Feature feature(true);
1900
1901 int32_t featureVers = AaptXml::getIntegerAttribute(
1902 tree, androidSchema.string(), "version", 0, &error);
1903 if (error == "") {
1904 feature.version = featureVers;
1905 } else {
1906 SourcePos(manifestFile, tree.getLineNumber()).error(
1907 "failed to read attribute 'android:version': %s",
1908 error.string());
1909 goto bail;
1910 }
1911
1912 top.features.add(name, feature);
Adam Lesinskid3edfde2014-08-08 17:32:44 -07001913 addParentFeatures(&top, name);
Adam Lesinski694d0a72016-04-06 16:12:04 -07001914
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001915 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001916 int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR,
1917 &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001918 if (error == "") {
1919 if (vers > top.openGLESVersion) {
1920 top.openGLESVersion = vers;
1921 }
1922 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001923 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001924 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001925 } else if (depth == 4) {
1926 if (tag == "intent-filter") {
1927 hasIntentFilter = true;
1928 withinIntentFilter = true;
1929 actMainActivity = false;
1930 actWidgetReceivers = false;
1931 actImeService = false;
1932 actWallpaperService = false;
1933 actAccessibilityService = false;
1934 actPrintService = false;
1935 actDeviceAdminEnabled = false;
1936 actHostApduService = false;
1937 actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001938 actDocumentsProvider = false;
1939 actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001940 actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001941 actCamera = false;
1942 actCameraSecure = false;
1943 catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001944 } else if (withinService && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001945 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001946 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001947 SourcePos(manifestFile, tree.getLineNumber()).error(
1948 "ERROR getting 'android:name' attribute for "
1949 "meta-data tag in service '%s': %s", serviceName.string(),
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001950 error.string());
Adam Lesinski94fc9122013-09-30 17:16:09 -07001951 goto bail;
1952 }
1953
1954 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1955 name == "android.nfc.cardemulation.off_host_apdu_service") {
1956 bool offHost = true;
1957 if (name == "android.nfc.cardemulation.host_apdu_service") {
1958 offHost = false;
1959 }
1960
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001961 String8 xmlPath = AaptXml::getResolvedAttribute(res, tree,
1962 RESOURCE_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001963 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001964 SourcePos(manifestFile, tree.getLineNumber()).error(
1965 "ERROR getting 'android:resource' attribute for "
1966 "meta-data tag in service '%s': %s",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001967 serviceName.string(), error.string());
Adam Lesinski94fc9122013-09-30 17:16:09 -07001968 goto bail;
1969 }
1970
1971 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1972 offHost, &error);
1973 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001974 SourcePos(manifestFile, tree.getLineNumber()).error(
1975 "ERROR getting AID category for service '%s'",
Adam Lesinski94fc9122013-09-30 17:16:09 -07001976 serviceName.string());
1977 goto bail;
1978 }
1979
1980 const size_t catLen = categories.size();
1981 for (size_t i = 0; i < catLen; i++) {
1982 bool paymentCategory = (categories[i] == "payment");
1983 if (offHost) {
1984 hasMetaOffHostPaymentCategory |= paymentCategory;
1985 } else {
1986 hasMetaHostPaymentCategory |= paymentCategory;
1987 }
1988 }
1989 }
1990 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001991 } else if ((depth == 5) && withinIntentFilter) {
1992 String8 action;
1993 if (tag == "action") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001994 action = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001995 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001996 SourcePos(manifestFile, tree.getLineNumber()).error(
1997 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001998 goto bail;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001999 }
2000
Adam Lesinskia5018c92013-09-30 16:23:15 -07002001 if (withinActivity) {
2002 if (action == "android.intent.action.MAIN") {
2003 isMainActivity = true;
2004 actMainActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002005 } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
2006 action == "android.media.action.VIDEO_CAMERA") {
2007 actCamera = true;
2008 } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
2009 actCameraSecure = true;
Michael Wrightec4fdec2013-09-06 16:50:52 -07002010 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002011 } else if (withinReceiver) {
2012 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
2013 actWidgetReceivers = true;
2014 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
2015 actDeviceAdminEnabled = true;
2016 }
2017 } else if (withinService) {
2018 if (action == "android.view.InputMethod") {
2019 actImeService = true;
2020 } else if (action == "android.service.wallpaper.WallpaperService") {
2021 actWallpaperService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002022 } else if (action ==
2023 "android.accessibilityservice.AccessibilityService") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07002024 actAccessibilityService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002025 } else if (action =="android.printservice.PrintService") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07002026 actPrintService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002027 } else if (action ==
2028 "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07002029 actHostApduService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002030 } else if (action ==
2031 "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07002032 actOffHostApduService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002033 } else if (action ==
2034 "android.service.notification.NotificationListenerService") {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002035 actNotificationListenerService = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04002036 } else if (action == "android.service.dreams.DreamService") {
2037 actDreamService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002038 }
2039 } else if (withinProvider) {
2040 if (action == "android.content.action.DOCUMENTS_PROVIDER") {
2041 actDocumentsProvider = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07002042 }
2043 }
2044 if (action == "android.intent.action.SEARCH") {
2045 isSearchable = true;
2046 }
2047 }
2048
2049 if (tag == "category") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07002050 String8 category = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07002051 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07002052 SourcePos(manifestFile, tree.getLineNumber()).error(
2053 "ERROR getting 'name' attribute: %s", error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07002054 goto bail;
2055 }
2056 if (withinActivity) {
2057 if (category == "android.intent.category.LAUNCHER") {
2058 isLauncherActivity = true;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08002059 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
2060 isLeanbackLauncherActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002061 } else if (category == "android.intent.category.HOME") {
2062 catLauncher = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08002063 }
2064 }
2065 }
2066 }
2067 }
2068
2069 // Pre-1.6 implicitly granted permission compatibility logic
2070 if (targetSdk < 4) {
2071 if (!hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002072 printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
2073 printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
2074 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002075 hasWriteExternalStoragePermission = true;
2076 }
2077 if (!hasReadPhoneStatePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002078 printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
2079 printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
2080 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002081 }
2082 }
2083
2084 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
2085 // force them to always take READ_EXTERNAL_STORAGE as well. We always
2086 // do this (regardless of target API version) because we can't have
2087 // an app with write permission but not read permission.
2088 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
Adam Lesinski2386df22016-12-28 15:08:58 -05002089 printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
2090 false /* optional */, writeExternalStoragePermissionMaxSdkVersion);
Adam Lesinski58f1f362013-11-12 12:59:08 -08002091 printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
Adam Lesinski2386df22016-12-28 15:08:58 -05002092 String8("requested WRITE_EXTERNAL_STORAGE"),
2093 writeExternalStoragePermissionMaxSdkVersion);
Adam Lesinski282e1812014-01-23 18:17:42 -08002094 }
2095
2096 // Pre-JellyBean call log permission compatibility.
2097 if (targetSdk < 16) {
2098 if (!hasReadCallLogPermission && hasReadContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002099 printUsesPermission(String8("android.permission.READ_CALL_LOG"));
2100 printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
2101 String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002102 }
2103 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002104 printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
2105 printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
2106 String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002107 }
2108 }
2109
Adam Lesinskica955a42016-08-01 16:44:29 -07002110 // If the app hasn't declared the touchscreen as a feature requirement (either
2111 // directly or implied, required or not), then the faketouch feature is implied.
2112 if (!hasFeature("android.hardware.touchscreen", commonFeatures, impliedFeatures)) {
2113 addImpliedFeature(&impliedFeatures, "android.hardware.faketouch",
Adam Lesinski43158772015-11-11 15:13:55 -08002114 String8("default feature for all apps"), false);
Adam Lesinskica955a42016-08-01 16:44:29 -07002115 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07002116
2117 const size_t numFeatureGroups = featureGroups.size();
2118 if (numFeatureGroups == 0) {
2119 // If no <feature-group> tags were defined, apply auto-implied features.
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002120 printDefaultFeatureGroup(commonFeatures, impliedFeatures);
Adam Lesinski2c72b682014-06-24 09:56:01 -07002121
2122 } else {
2123 // <feature-group> tags are defined, so we ignore implied features and
2124 for (size_t i = 0; i < numFeatureGroups; i++) {
2125 FeatureGroup& grp = featureGroups.editItemAt(i);
2126
Adam Lesinskid7a94da2014-07-25 14:38:54 -07002127 if (commonFeatures.openGLESVersion > grp.openGLESVersion) {
2128 grp.openGLESVersion = commonFeatures.openGLESVersion;
2129 }
2130
Adam Lesinski2c72b682014-06-24 09:56:01 -07002131 // Merge the features defined in the top level (not inside a <feature-group>)
2132 // with this feature group.
2133 const size_t numCommonFeatures = commonFeatures.features.size();
2134 for (size_t j = 0; j < numCommonFeatures; j++) {
2135 if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07002136 grp.features.add(commonFeatures.features.keyAt(j),
2137 commonFeatures.features[j]);
Adam Lesinski2c72b682014-06-24 09:56:01 -07002138 }
2139 }
2140
Adam Lesinski73a05112014-12-08 12:53:17 -08002141 if (!grp.features.isEmpty()) {
Adam Lesinski2c72b682014-06-24 09:56:01 -07002142 printFeatureGroup(grp);
Adam Lesinski282e1812014-01-23 18:17:42 -08002143 }
2144 }
2145 }
2146
Adam Lesinski282e1812014-01-23 18:17:42 -08002147
Adam Lesinski282e1812014-01-23 18:17:42 -08002148 if (hasWidgetReceivers) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002149 printComponentPresence("app-widget");
Adam Lesinski282e1812014-01-23 18:17:42 -08002150 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002151 if (hasDeviceAdminReceiver) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002152 printComponentPresence("device-admin");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002153 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002154 if (hasImeService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002155 printComponentPresence("ime");
Adam Lesinski282e1812014-01-23 18:17:42 -08002156 }
2157 if (hasWallpaperService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002158 printComponentPresence("wallpaper");
Adam Lesinski282e1812014-01-23 18:17:42 -08002159 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002160 if (hasAccessibilityService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002161 printComponentPresence("accessibility");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002162 }
2163 if (hasPrintService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002164 printComponentPresence("print-service");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002165 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07002166 if (hasPaymentService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002167 printComponentPresence("payment");
2168 }
2169 if (isSearchable) {
2170 printComponentPresence("search");
2171 }
2172 if (hasDocumentsProvider) {
2173 printComponentPresence("document-provider");
2174 }
2175 if (hasLauncher) {
2176 printComponentPresence("launcher");
2177 }
2178 if (hasNotificationListenerService) {
2179 printComponentPresence("notification-listener");
2180 }
John Spurlockeb8d1be2014-06-25 17:46:15 -04002181 if (hasDreamService) {
2182 printComponentPresence("dream");
2183 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002184 if (hasCameraActivity) {
2185 printComponentPresence("camera");
2186 }
2187 if (hasCameraSecureActivity) {
2188 printComponentPresence("camera-secure");
2189 }
2190
2191 if (hasMainActivity) {
2192 printf("main\n");
Adam Lesinski94fc9122013-09-30 17:16:09 -07002193 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002194 if (hasOtherActivities) {
2195 printf("other-activities\n");
2196 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002197 if (hasOtherReceivers) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002198 printf("other-receivers\n");
2199 }
2200 if (hasOtherServices) {
2201 printf("other-services\n");
2202 }
2203
2204 // For modern apps, if screen size buckets haven't been specified
2205 // but the new width ranges have, then infer the buckets from them.
2206 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
2207 && requiresSmallestWidthDp > 0) {
2208 int compatWidth = compatibleWidthLimitDp;
2209 if (compatWidth <= 0) {
2210 compatWidth = requiresSmallestWidthDp;
2211 }
2212 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
2213 smallScreen = -1;
2214 } else {
2215 smallScreen = 0;
2216 }
2217 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
2218 normalScreen = -1;
2219 } else {
2220 normalScreen = 0;
2221 }
2222 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
2223 largeScreen = -1;
2224 } else {
2225 largeScreen = 0;
2226 }
2227 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
2228 xlargeScreen = -1;
2229 } else {
2230 xlargeScreen = 0;
2231 }
2232 }
2233
2234 // Determine default values for any unspecified screen sizes,
2235 // based on the target SDK of the package. As of 4 (donut)
2236 // the screen size support was introduced, so all default to
2237 // enabled.
2238 if (smallScreen > 0) {
2239 smallScreen = targetSdk >= 4 ? -1 : 0;
2240 }
2241 if (normalScreen > 0) {
2242 normalScreen = -1;
2243 }
2244 if (largeScreen > 0) {
2245 largeScreen = targetSdk >= 4 ? -1 : 0;
2246 }
2247 if (xlargeScreen > 0) {
2248 // Introduced in Gingerbread.
2249 xlargeScreen = targetSdk >= 9 ? -1 : 0;
2250 }
2251 if (anyDensity > 0) {
2252 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
2253 || compatibleWidthLimitDp > 0) ? -1 : 0;
2254 }
2255 printf("supports-screens:");
2256 if (smallScreen != 0) {
2257 printf(" 'small'");
2258 }
2259 if (normalScreen != 0) {
2260 printf(" 'normal'");
2261 }
2262 if (largeScreen != 0) {
2263 printf(" 'large'");
2264 }
2265 if (xlargeScreen != 0) {
2266 printf(" 'xlarge'");
2267 }
2268 printf("\n");
2269 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
2270 if (requiresSmallestWidthDp > 0) {
2271 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
2272 }
2273 if (compatibleWidthLimitDp > 0) {
2274 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
2275 }
2276 if (largestWidthLimitDp > 0) {
2277 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
2278 }
2279
2280 printf("locales:");
2281 const size_t NL = locales.size();
2282 for (size_t i=0; i<NL; i++) {
2283 const char* localeStr = locales[i].string();
2284 if (localeStr == NULL || strlen(localeStr) == 0) {
2285 localeStr = "--_--";
2286 }
2287 printf(" '%s'", localeStr);
2288 }
2289 printf("\n");
2290
2291 printf("densities:");
2292 const size_t ND = densities.size();
2293 for (size_t i=0; i<ND; i++) {
2294 printf(" '%d'", densities[i]);
2295 }
2296 printf("\n");
2297
2298 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
2299 if (dir != NULL) {
2300 if (dir->getFileCount() > 0) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002301 SortedVector<String8> architectures;
Adam Lesinski282e1812014-01-23 18:17:42 -08002302 for (size_t i=0; i<dir->getFileCount(); i++) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002303 architectures.add(ResTable::normalizeForOutput(
2304 dir->getFileName(i).string()));
Adam Lesinski282e1812014-01-23 18:17:42 -08002305 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07002306
2307 bool outputAltNativeCode = false;
2308 // A multiArch package is one that contains 64-bit and
2309 // 32-bit versions of native code and expects 3rd-party
2310 // apps to load these native code libraries. Since most
2311 // 64-bit systems also support 32-bit apps, the apps
2312 // loading this multiArch package's code may be either
2313 // 32-bit or 64-bit.
2314 if (hasMultiArch) {
2315 // If this is a multiArch package, report the 64-bit
2316 // version only. Then as a separate entry, report the
2317 // rest.
2318 //
2319 // If we report the 32-bit architecture, this APK will
2320 // be installed on a 32-bit device, causing a large waste
2321 // of bandwidth and disk space. This assumes that
2322 // the developer of the multiArch package has also
2323 // made a version that is 32-bit only.
2324 String8 intel64("x86_64");
2325 String8 arm64("arm64-v8a");
2326 ssize_t index = architectures.indexOf(intel64);
2327 if (index < 0) {
2328 index = architectures.indexOf(arm64);
2329 }
2330
2331 if (index >= 0) {
2332 printf("native-code: '%s'\n", architectures[index].string());
2333 architectures.removeAt(index);
2334 outputAltNativeCode = true;
2335 }
2336 }
2337
2338 const size_t archCount = architectures.size();
2339 if (archCount > 0) {
2340 if (outputAltNativeCode) {
2341 printf("alt-");
2342 }
2343 printf("native-code:");
2344 for (size_t i = 0; i < archCount; i++) {
2345 printf(" '%s'", architectures[i].string());
2346 }
2347 printf("\n");
2348 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002349 }
2350 delete dir;
2351 }
2352 } else if (strcmp("badger", option) == 0) {
2353 printf("%s", CONSOLE_DATA);
2354 } else if (strcmp("configurations", option) == 0) {
2355 Vector<ResTable_config> configs;
2356 res.getConfigurations(&configs);
2357 const size_t N = configs.size();
2358 for (size_t i=0; i<N; i++) {
2359 printf("%s\n", configs[i].toString().string());
2360 }
2361 } else {
2362 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2363 goto bail;
2364 }
2365 }
2366
2367 result = NO_ERROR;
2368
2369bail:
Adam Lesinski10de3af12016-07-13 10:14:03 -07002370 if (SourcePos::hasErrors()) {
2371 SourcePos::printErrors(stderr);
2372 }
2373
Adam Lesinski282e1812014-01-23 18:17:42 -08002374 if (asset) {
2375 delete asset;
2376 }
2377 return (result != NO_ERROR);
2378}
2379
2380
2381/*
2382 * Handle the "add" command, which wants to add files to a new or
2383 * pre-existing archive.
2384 */
2385int doAdd(Bundle* bundle)
2386{
2387 ZipFile* zip = NULL;
2388 status_t result = UNKNOWN_ERROR;
2389 const char* zipFileName;
2390
2391 if (bundle->getUpdate()) {
2392 /* avoid confusion */
2393 fprintf(stderr, "ERROR: can't use '-u' with add\n");
2394 goto bail;
2395 }
2396
2397 if (bundle->getFileSpecCount() < 1) {
2398 fprintf(stderr, "ERROR: must specify zip file name\n");
2399 goto bail;
2400 }
2401 zipFileName = bundle->getFileSpecEntry(0);
2402
2403 if (bundle->getFileSpecCount() < 2) {
2404 fprintf(stderr, "NOTE: nothing to do\n");
2405 goto bail;
2406 }
2407
2408 zip = openReadWrite(zipFileName, true);
2409 if (zip == NULL) {
2410 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2411 goto bail;
2412 }
2413
2414 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2415 const char* fileName = bundle->getFileSpecEntry(i);
2416
2417 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
2418 printf(" '%s'... (from gzip)\n", fileName);
2419 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
2420 } else {
2421 if (bundle->getJunkPath()) {
2422 String8 storageName = String8(fileName).getPathLeaf();
Maurice Chu2675f762013-10-22 17:33:11 -07002423 printf(" '%s' as '%s'...\n", fileName,
2424 ResTable::normalizeForOutput(storageName.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08002425 result = zip->add(fileName, storageName.string(),
2426 bundle->getCompressionMethod(), NULL);
2427 } else {
2428 printf(" '%s'...\n", fileName);
2429 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2430 }
2431 }
2432 if (result != NO_ERROR) {
2433 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2434 if (result == NAME_NOT_FOUND) {
2435 fprintf(stderr, ": file not found\n");
2436 } else if (result == ALREADY_EXISTS) {
2437 fprintf(stderr, ": already exists in archive\n");
2438 } else {
2439 fprintf(stderr, "\n");
2440 }
2441 goto bail;
2442 }
2443 }
2444
2445 result = NO_ERROR;
2446
2447bail:
2448 delete zip;
2449 return (result != NO_ERROR);
2450}
2451
2452
2453/*
2454 * Delete files from an existing archive.
2455 */
2456int doRemove(Bundle* bundle)
2457{
2458 ZipFile* zip = NULL;
2459 status_t result = UNKNOWN_ERROR;
2460 const char* zipFileName;
2461
2462 if (bundle->getFileSpecCount() < 1) {
2463 fprintf(stderr, "ERROR: must specify zip file name\n");
2464 goto bail;
2465 }
2466 zipFileName = bundle->getFileSpecEntry(0);
2467
2468 if (bundle->getFileSpecCount() < 2) {
2469 fprintf(stderr, "NOTE: nothing to do\n");
2470 goto bail;
2471 }
2472
2473 zip = openReadWrite(zipFileName, false);
2474 if (zip == NULL) {
2475 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2476 zipFileName);
2477 goto bail;
2478 }
2479
2480 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2481 const char* fileName = bundle->getFileSpecEntry(i);
2482 ZipEntry* entry;
2483
2484 entry = zip->getEntryByName(fileName);
2485 if (entry == NULL) {
2486 printf(" '%s' NOT FOUND\n", fileName);
2487 continue;
2488 }
2489
2490 result = zip->remove(entry);
2491
2492 if (result != NO_ERROR) {
2493 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2494 bundle->getFileSpecEntry(i), zipFileName);
2495 goto bail;
2496 }
2497 }
2498
2499 /* update the archive */
2500 zip->flush();
2501
2502bail:
2503 delete zip;
2504 return (result != NO_ERROR);
2505}
2506
Adam Lesinski3921e872014-05-13 10:56:25 -07002507static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002508 const size_t numDirs = dir->getDirs().size();
2509 for (size_t i = 0; i < numDirs; i++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002510 bool ignore = ignoreConfig;
2511 const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2512 const char* dirStr = subDir->getLeaf().string();
2513 if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2514 ignore = true;
2515 }
2516 status_t err = addResourcesToBuilder(subDir, builder, ignore);
Adam Lesinskifab50872014-04-16 14:40:42 -07002517 if (err != NO_ERROR) {
2518 return err;
2519 }
2520 }
2521
2522 const size_t numFiles = dir->getFiles().size();
2523 for (size_t i = 0; i < numFiles; i++) {
2524 sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2525 const size_t numConfigs = gp->getFiles().size();
2526 for (size_t j = 0; j < numConfigs; j++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002527 status_t err = NO_ERROR;
Adam Lesinskic7614e52017-03-16 16:54:23 -07002528 if (ignoreConfig) {
Guang Zhu8c2df712017-03-21 03:53:43 +00002529 err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
Adam Lesinskic7614e52017-03-16 16:54:23 -07002530 } else {
Guang Zhu8c2df712017-03-21 03:53:43 +00002531 err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
Adam Lesinskic7614e52017-03-16 16:54:23 -07002532 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002533 if (err != NO_ERROR) {
2534 fprintf(stderr, "Failed to add %s (%s) to builder.\n",
Guang Zhu8c2df712017-03-21 03:53:43 +00002535 gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
Adam Lesinskifab50872014-04-16 14:40:42 -07002536 return err;
2537 }
2538 }
2539 }
2540 return NO_ERROR;
2541}
2542
2543static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2544 if (split->isBase()) {
2545 return original;
2546 }
2547
2548 String8 ext(original.getPathExtension());
2549 if (ext == String8(".apk")) {
2550 return String8::format("%s_%s%s",
2551 original.getBasePath().string(),
2552 split->getDirectorySafeName().string(),
2553 ext.string());
2554 }
2555
2556 return String8::format("%s_%s", original.string(),
2557 split->getDirectorySafeName().string());
2558}
Adam Lesinski282e1812014-01-23 18:17:42 -08002559
2560/*
2561 * Package up an asset directory and associated application files.
2562 */
2563int doPackage(Bundle* bundle)
2564{
2565 const char* outputAPKFile;
2566 int retVal = 1;
2567 status_t err;
2568 sp<AaptAssets> assets;
2569 int N;
2570 FILE* fp;
2571 String8 dependencyFile;
Adam Lesinskifab50872014-04-16 14:40:42 -07002572 sp<ApkBuilder> builder;
Adam Lesinski282e1812014-01-23 18:17:42 -08002573
Anton Krumina2ef5c02014-03-12 14:46:44 -07002574 // -c en_XA or/and ar_XB means do pseudolocalization
Adam Lesinskifab50872014-04-16 14:40:42 -07002575 sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2576 err = configFilter->parse(bundle->getConfigurations());
Adam Lesinski282e1812014-01-23 18:17:42 -08002577 if (err != NO_ERROR) {
2578 goto bail;
2579 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002580 if (configFilter->containsPseudo()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002581 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2582 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002583 if (configFilter->containsPseudoBidi()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002584 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
Adam Lesinski282e1812014-01-23 18:17:42 -08002585 }
2586
2587 N = bundle->getFileSpecCount();
2588 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
Adam Lesinski09384302014-01-22 16:07:42 -08002589 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002590 fprintf(stderr, "ERROR: no input files\n");
2591 goto bail;
2592 }
2593
2594 outputAPKFile = bundle->getOutputAPKFile();
2595
2596 // Make sure the filenames provided exist and are of the appropriate type.
2597 if (outputAPKFile) {
2598 FileType type;
2599 type = getFileType(outputAPKFile);
2600 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2601 fprintf(stderr,
2602 "ERROR: output file '%s' exists but is not regular file\n",
2603 outputAPKFile);
2604 goto bail;
2605 }
2606 }
2607
2608 // Load the assets.
2609 assets = new AaptAssets();
2610
2611 // Set up the resource gathering in assets if we're going to generate
2612 // dependency files. Every time we encounter a resource while slurping
2613 // the tree, we'll add it to these stores so we have full resource paths
2614 // to write to a dependency file.
2615 if (bundle->getGenDependencies()) {
2616 sp<FilePathStore> resPathStore = new FilePathStore;
2617 assets->setFullResPaths(resPathStore);
2618 sp<FilePathStore> assetPathStore = new FilePathStore;
2619 assets->setFullAssetPaths(assetPathStore);
2620 }
2621
2622 err = assets->slurpFromArgs(bundle);
2623 if (err < 0) {
2624 goto bail;
2625 }
2626
2627 if (bundle->getVerbose()) {
2628 assets->print(String8());
2629 }
2630
Adam Lesinskifab50872014-04-16 14:40:42 -07002631 // Create the ApkBuilder, which will collect the compiled files
2632 // to write to the final APK (or sets of APKs if we are building
2633 // a Split APK.
2634 builder = new ApkBuilder(configFilter);
2635
2636 // If we are generating a Split APK, find out which configurations to split on.
2637 if (bundle->getSplitConfigurations().size() > 0) {
2638 const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2639 const size_t numSplits = splitStrs.size();
2640 for (size_t i = 0; i < numSplits; i++) {
2641 std::set<ConfigDescription> configs;
2642 if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2643 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2644 goto bail;
2645 }
2646
2647 err = builder->createSplitForConfigs(configs);
2648 if (err != NO_ERROR) {
2649 goto bail;
2650 }
2651 }
2652 }
2653
Adam Lesinski282e1812014-01-23 18:17:42 -08002654 // If they asked for any fileAs that need to be compiled, do so.
2655 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002656 err = buildResources(bundle, assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002657 if (err != 0) {
2658 goto bail;
2659 }
2660 }
2661
2662 // At this point we've read everything and processed everything. From here
2663 // on out it's just writing output files.
2664 if (SourcePos::hasErrors()) {
2665 goto bail;
2666 }
2667
2668 // Update symbols with information about which ones are needed as Java symbols.
2669 assets->applyJavaSymbols();
2670 if (SourcePos::hasErrors()) {
2671 goto bail;
2672 }
2673
2674 // If we've been asked to generate a dependency file, do that here
2675 if (bundle->getGenDependencies()) {
2676 // If this is the packaging step, generate the dependency file next to
2677 // the output apk (e.g. bin/resources.ap_.d)
2678 if (outputAPKFile) {
2679 dependencyFile = String8(outputAPKFile);
2680 // Add the .d extension to the dependency file.
2681 dependencyFile.append(".d");
2682 } else {
2683 // Else if this is the R.java dependency generation step,
2684 // generate the dependency file in the R.java package subdirectory
2685 // e.g. gen/com/foo/app/R.java.d
2686 dependencyFile = String8(bundle->getRClassDir());
2687 dependencyFile.appendPath("R.java.d");
2688 }
2689 // Make sure we have a clean dependency file to start with
2690 fp = fopen(dependencyFile, "w");
2691 fclose(fp);
2692 }
2693
2694 // Write out R.java constants
2695 if (!assets->havePrivateSymbols()) {
2696 if (bundle->getCustomPackage() == NULL) {
2697 // Write the R.java file into the appropriate class directory
2698 // e.g. gen/com/foo/app/R.java
Adam Lesinski1e4663852014-08-15 14:47:28 -07002699 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002700 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002701 } else {
2702 const String8 customPkg(bundle->getCustomPackage());
Adam Lesinski1e4663852014-08-15 14:47:28 -07002703 err = writeResourceSymbols(bundle, assets, customPkg, true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002704 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002705 }
2706 if (err < 0) {
2707 goto bail;
2708 }
2709 // If we have library files, we're going to write our R.java file into
2710 // the appropriate class directory for those libraries as well.
2711 // e.g. gen/com/foo/app/lib/R.java
2712 if (bundle->getExtraPackages() != NULL) {
2713 // Split on colon
2714 String8 libs(bundle->getExtraPackages());
2715 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2716 while (packageString != NULL) {
2717 // Write the R.java file out with the correct package name
Marcin Kosiba0f3a5a62014-09-11 13:48:48 +01002718 err = writeResourceSymbols(bundle, assets, String8(packageString), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002719 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002720 if (err < 0) {
2721 goto bail;
2722 }
2723 packageString = strtok(NULL, ":");
2724 }
2725 libs.unlockBuffer();
2726 }
2727 } else {
Adam Lesinski1e4663852014-08-15 14:47:28 -07002728 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002729 if (err < 0) {
2730 goto bail;
2731 }
Adam Lesinski1e4663852014-08-15 14:47:28 -07002732 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002733 if (err < 0) {
2734 goto bail;
2735 }
2736 }
2737
2738 // Write out the ProGuard file
2739 err = writeProguardFile(bundle, assets);
2740 if (err < 0) {
2741 goto bail;
2742 }
2743
Rohit Agrawal86229cb2016-04-21 16:29:58 -07002744 // Write out the Main Dex ProGuard file
2745 err = writeMainDexProguardFile(bundle, assets);
2746 if (err < 0) {
2747 goto bail;
2748 }
2749
Adam Lesinski282e1812014-01-23 18:17:42 -08002750 // Write the apk
2751 if (outputAPKFile) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002752 // Gather all resources and add them to the APK Builder. The builder will then
2753 // figure out which Split they belong in.
2754 err = addResourcesToBuilder(assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002755 if (err != NO_ERROR) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002756 goto bail;
2757 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002758
2759 const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2760 const size_t numSplits = splits.size();
2761 for (size_t i = 0; i < numSplits; i++) {
2762 const sp<ApkSplit>& split = splits[i];
2763 String8 outputPath = buildApkName(String8(outputAPKFile), split);
2764 err = writeAPK(bundle, outputPath, split);
2765 if (err != NO_ERROR) {
2766 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2767 goto bail;
2768 }
2769 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002770 }
2771
2772 // If we've been asked to generate a dependency file, we need to finish up here.
2773 // the writeResourceSymbols and writeAPK functions have already written the target
2774 // half of the dependency file, now we need to write the prerequisites. (files that
2775 // the R.java file or .ap_ file depend on)
2776 if (bundle->getGenDependencies()) {
2777 // Now that writeResourceSymbols or writeAPK has taken care of writing
2778 // the targets to our dependency file, we'll write the prereqs
2779 fp = fopen(dependencyFile, "a+");
2780 fprintf(fp, " : ");
2781 bool includeRaw = (outputAPKFile != NULL);
2782 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2783 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2784 // and therefore was not added to our pathstores during slurping
2785 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2786 fclose(fp);
2787 }
2788
2789 retVal = 0;
2790bail:
2791 if (SourcePos::hasErrors()) {
2792 SourcePos::printErrors(stderr);
2793 }
2794 return retVal;
2795}
2796
2797/*
2798 * Do PNG Crunching
2799 * PRECONDITIONS
2800 * -S flag points to a source directory containing drawable* folders
2801 * -C flag points to destination directory. The folder structure in the
2802 * source directory will be mirrored to the destination (cache) directory
2803 *
2804 * POSTCONDITIONS
2805 * Destination directory will be updated to match the PNG files in
Maurice Chu2675f762013-10-22 17:33:11 -07002806 * the source directory.
Adam Lesinski282e1812014-01-23 18:17:42 -08002807 */
2808int doCrunch(Bundle* bundle)
2809{
2810 fprintf(stdout, "Crunching PNG Files in ");
2811 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2812 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2813
2814 updatePreProcessedCache(bundle);
2815
2816 return NO_ERROR;
2817}
2818
2819/*
2820 * Do PNG Crunching on a single flag
2821 * -i points to a single png file
2822 * -o points to a single png output file
2823 */
2824int doSingleCrunch(Bundle* bundle)
2825{
2826 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2827 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2828
2829 String8 input(bundle->getSingleCrunchInputFile());
2830 String8 output(bundle->getSingleCrunchOutputFile());
2831
2832 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2833 // we can't return the status_t as it gets truncate to the lower 8 bits.
2834 return 42;
2835 }
2836
2837 return NO_ERROR;
2838}
2839
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002840int runInDaemonMode(Bundle* bundle) {
2841 std::cout << "Ready" << std::endl;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002842 for (std::string cmd; std::getline(std::cin, cmd);) {
2843 if (cmd == "quit") {
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002844 return NO_ERROR;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002845 } else if (cmd == "s") {
2846 // Two argument crunch
2847 std::string inputFile, outputFile;
2848 std::getline(std::cin, inputFile);
2849 std::getline(std::cin, outputFile);
2850 bundle->setSingleCrunchInputFile(inputFile.c_str());
2851 bundle->setSingleCrunchOutputFile(outputFile.c_str());
2852 std::cout << "Crunching " << inputFile << std::endl;
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002853 if (doSingleCrunch(bundle) != NO_ERROR) {
2854 std::cout << "Error" << std::endl;
2855 }
2856 std::cout << "Done" << std::endl;
2857 } else {
2858 // in case of invalid command, just bail out.
2859 std::cerr << "Unknown command" << std::endl;
2860 return -1;
2861 }
2862 }
2863 return -1;
2864}
2865
Adam Lesinski282e1812014-01-23 18:17:42 -08002866char CONSOLE_DATA[2925] = {
2867 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2868 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2869 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2870 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2871 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2872 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2873 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2874 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2875 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2876 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2877 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2878 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2879 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2880 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2881 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2882 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2883 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2884 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2885 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2886 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2887 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2888 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2889 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2890 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2891 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2892 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2893 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2894 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2895 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2896 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2897 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2898 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2899 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2900 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2901 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2902 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2903 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2904 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2905 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2906 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2907 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2908 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2909 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2910 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2911 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2912 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2913 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2914 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2915 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2916 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2917 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2918 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2919 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2920 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2921 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2922 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2923 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2924 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2925 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2926 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2927 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2928 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2929 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2930 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2931 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2932 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2933 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2934 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2935 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2936 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2937 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2938 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2939 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2940 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2941 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2942 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2943 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2944 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2945 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2946 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2947 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2948 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2949 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2950 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2951 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2952 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2953 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2954 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2955 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2956 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2957 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2958 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2959 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2960 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2961 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2962 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2963 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2964 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2965 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2966 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2967 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2968 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2969 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2970 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2971 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2972 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2973 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2974 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2975 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2976 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2977 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2978 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2979 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2980 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2981 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2982 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2983 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2984 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2985 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2986 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2987 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2988 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2989 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2990 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2991 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2992 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2993 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2994 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2995 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2996 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2997 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2998 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2999 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
3000 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
3001 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
3002 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
3003 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3004 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3005 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
3006 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
3007 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
3008 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3009 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
3010 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3011 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
3012 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
3013 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
3014 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3015 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3016 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
3017 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
3018 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
3019 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
3020 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
3021 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3022 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3023 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
3024 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
3025 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3026 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3027 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3028 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3029 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
3030 };