blob: 05375b0cb8717861988bdbdfe6af826442413d95 [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.
Adam Lesinski63e646e2014-07-30 11:40:39 -0700790 const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700791
792 Asset* asset = NULL;
793
Adam Lesinski282e1812014-01-23 18:17:42 -0800794 if (strcmp("resources", option) == 0) {
Elliott Hughesba3fe562015-08-12 14:49:53 -0700795#ifndef __ANDROID__
Adam Lesinski282e1812014-01-23 18:17:42 -0800796 res.print(bundle->getValues());
797#endif
798
799 } else if (strcmp("strings", option) == 0) {
800 const ResStringPool* pool = res.getTableStringBlock(0);
801 printStringPool(pool);
802
803 } else if (strcmp("xmltree", option) == 0) {
804 if (bundle->getFileSpecCount() < 3) {
805 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
806 goto bail;
807 }
808
809 for (int i=2; i<bundle->getFileSpecCount(); i++) {
810 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700811 ResXMLTree tree(dynamicRefTable);
812 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800813 if (asset == NULL) {
Adam Lesinskifcb5f7b2016-11-02 13:17:10 -0700814 fprintf(stderr, "ERROR: dump failed because resource %s not found\n", resname);
Adam Lesinski282e1812014-01-23 18:17:42 -0800815 goto bail;
816 }
817
818 if (tree.setTo(asset->getBuffer(true),
819 asset->getLength()) != NO_ERROR) {
820 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
821 goto bail;
822 }
823 tree.restart();
824 printXMLBlock(&tree);
825 tree.uninit();
826 delete asset;
827 asset = NULL;
828 }
829
830 } else if (strcmp("xmlstrings", option) == 0) {
831 if (bundle->getFileSpecCount() < 3) {
832 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
833 goto bail;
834 }
835
836 for (int i=2; i<bundle->getFileSpecCount(); i++) {
837 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700838 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800839 if (asset == NULL) {
840 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
841 goto bail;
842 }
843
Adam Lesinski63e646e2014-07-30 11:40:39 -0700844 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800845 if (tree.setTo(asset->getBuffer(true),
846 asset->getLength()) != NO_ERROR) {
847 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
848 goto bail;
849 }
850 printStringPool(&tree.getStrings());
851 delete asset;
852 asset = NULL;
853 }
854
855 } else {
Adam Lesinski63e646e2014-07-30 11:40:39 -0700856 asset = assets.openNonAsset(assetsCookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800857 if (asset == NULL) {
858 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
859 goto bail;
860 }
861
Adam Lesinski63e646e2014-07-30 11:40:39 -0700862 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800863 if (tree.setTo(asset->getBuffer(true),
864 asset->getLength()) != NO_ERROR) {
865 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
866 goto bail;
867 }
868 tree.restart();
869
870 if (strcmp("permissions", option) == 0) {
871 size_t len;
872 ResXMLTree::event_code_t code;
873 int depth = 0;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800874 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
875 code != ResXMLTree::BAD_DOCUMENT) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800876 if (code == ResXMLTree::END_TAG) {
877 depth--;
878 continue;
879 }
880 if (code != ResXMLTree::START_TAG) {
881 continue;
882 }
883 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700884 const char16_t* ctag16 = tree.getElementName(&len);
885 if (ctag16 == NULL) {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700886 SourcePos(manifestFile, tree.getLineNumber()).error(
887 "ERROR: failed to get XML element name (bad string pool)");
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700888 goto bail;
889 }
890 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800891 //printf("Depth %d tag %s\n", depth, tag.string());
892 if (depth == 1) {
893 if (tag != "manifest") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700894 SourcePos(manifestFile, tree.getLineNumber()).error(
895 "ERROR: manifest does not start with <manifest> tag");
Adam Lesinski282e1812014-01-23 18:17:42 -0800896 goto bail;
897 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700898 String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -0700899 printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800900 } else if (depth == 2) {
901 if (tag == "permission") {
902 String8 error;
903 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
904 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700905 SourcePos(manifestFile, tree.getLineNumber()).error(
906 "ERROR getting 'android:name': %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800907 goto bail;
908 }
909
910 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700911 SourcePos(manifestFile, tree.getLineNumber()).error(
912 "ERROR: missing 'android:name' for permission");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800913 goto bail;
914 }
915 printf("permission: %s\n",
916 ResTable::normalizeForOutput(name.string()).string());
917 } else if (tag == "uses-permission") {
918 String8 error;
919 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
920 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700921 SourcePos(manifestFile, tree.getLineNumber()).error(
922 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800923 goto bail;
924 }
925
926 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700927 SourcePos(manifestFile, tree.getLineNumber()).error(
928 "ERROR: missing 'android:name' for uses-permission");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800929 goto bail;
930 }
931 printUsesPermission(name,
932 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
933 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
934 } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
935 String8 error;
936 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
937 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700938 SourcePos(manifestFile, tree.getLineNumber()).error(
939 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800940 goto bail;
941 }
942
943 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700944 SourcePos(manifestFile, tree.getLineNumber()).error(
945 "ERROR: missing 'android:name' for uses-permission-sdk-23");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800946 goto bail;
947 }
948 printUsesPermissionSdk23(
949 name,
950 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
Adam Lesinski282e1812014-01-23 18:17:42 -0800951 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800952 }
953 }
954 } else if (strcmp("badging", option) == 0) {
955 Vector<String8> locales;
956 res.getLocales(&locales);
957
958 Vector<ResTable_config> configs;
959 res.getConfigurations(&configs);
960 SortedVector<int> densities;
961 const size_t NC = configs.size();
962 for (size_t i=0; i<NC; i++) {
963 int dens = configs[i].density;
964 if (dens == 0) {
965 dens = 160;
966 }
967 densities.add(dens);
968 }
969
970 size_t len;
971 ResXMLTree::event_code_t code;
972 int depth = 0;
973 String8 error;
974 bool withinActivity = false;
975 bool isMainActivity = false;
976 bool isLauncherActivity = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800977 bool isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800978 bool isSearchable = false;
979 bool withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700980 bool withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700981 bool withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800982 bool withinReceiver = false;
983 bool withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700984 bool withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800985 bool withinIntentFilter = false;
986 bool hasMainActivity = false;
987 bool hasOtherActivities = false;
988 bool hasOtherReceivers = false;
989 bool hasOtherServices = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700990 bool hasIntentFilter = false;
991
Adam Lesinski282e1812014-01-23 18:17:42 -0800992 bool hasWallpaperService = false;
993 bool hasImeService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700994 bool hasAccessibilityService = false;
995 bool hasPrintService = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800996 bool hasWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700997 bool hasDeviceAdminReceiver = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700998 bool hasPaymentService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700999 bool hasDocumentsProvider = false;
1000 bool hasCameraActivity = false;
1001 bool hasCameraSecureActivity = false;
1002 bool hasLauncher = false;
1003 bool hasNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001004 bool hasDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001005
Adam Lesinski282e1812014-01-23 18:17:42 -08001006 bool actMainActivity = false;
1007 bool actWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001008 bool actDeviceAdminEnabled = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001009 bool actImeService = false;
1010 bool actWallpaperService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001011 bool actAccessibilityService = false;
1012 bool actPrintService = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001013 bool actHostApduService = false;
1014 bool actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001015 bool actDocumentsProvider = false;
1016 bool actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001017 bool actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001018 bool actCamera = false;
1019 bool actCameraSecure = false;
1020 bool catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001021 bool hasMetaHostPaymentCategory = false;
1022 bool hasMetaOffHostPaymentCategory = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001023
1024 // These permissions are required by services implementing services
1025 // the system binds to (IME, Accessibility, PrintServices, etc.)
1026 bool hasBindDeviceAdminPermission = false;
1027 bool hasBindInputMethodPermission = false;
1028 bool hasBindAccessibilityServicePermission = false;
1029 bool hasBindPrintServicePermission = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001030 bool hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001031 bool hasRequiredSafAttributes = false;
1032 bool hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001033 bool hasBindDreamServicePermission = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001034
1035 // These two implement the implicit permissions that are granted
1036 // to pre-1.6 applications.
1037 bool hasWriteExternalStoragePermission = false;
Adam Lesinski2386df22016-12-28 15:08:58 -05001038 int32_t writeExternalStoragePermissionMaxSdkVersion = -1;
Adam Lesinski282e1812014-01-23 18:17:42 -08001039 bool hasReadPhoneStatePermission = false;
1040
1041 // If an app requests write storage, they will also get read storage.
1042 bool hasReadExternalStoragePermission = false;
1043
1044 // Implement transition to read and write call log.
1045 bool hasReadContactsPermission = false;
1046 bool hasWriteContactsPermission = false;
1047 bool hasReadCallLogPermission = false;
1048 bool hasWriteCallLogPermission = false;
1049
Adam Lesinskie47fd122014-08-15 22:25:36 -07001050 // If an app declares itself as multiArch, we report the
1051 // native libraries differently.
1052 bool hasMultiArch = false;
1053
Adam Lesinski282e1812014-01-23 18:17:42 -08001054 // This next group of variables is used to implement a group of
1055 // backward-compatibility heuristics necessitated by the addition of
1056 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
1057 // heuristic is "if an app requests a permission but doesn't explicitly
1058 // request the corresponding <uses-feature>, presume it's there anyway".
Adam Lesinski2c72b682014-06-24 09:56:01 -07001059
Adam Lesinski282e1812014-01-23 18:17:42 -08001060 // 2.2 also added some other features that apps can request, but that
1061 // have no corresponding permission, so we cannot implement any
1062 // back-compatibility heuristic for them. The below are thus unnecessary
1063 // (but are retained here for documentary purposes.)
1064 //bool specCompassFeature = false;
1065 //bool specAccelerometerFeature = false;
1066 //bool specProximityFeature = false;
1067 //bool specAmbientLightFeature = false;
1068 //bool specLiveWallpaperFeature = false;
1069
1070 int targetSdk = 0;
1071 int smallScreen = 1;
1072 int normalScreen = 1;
1073 int largeScreen = 1;
1074 int xlargeScreen = 1;
1075 int anyDensity = 1;
1076 int requiresSmallestWidthDp = 0;
1077 int compatibleWidthLimitDp = 0;
1078 int largestWidthLimitDp = 0;
1079 String8 pkg;
1080 String8 activityName;
1081 String8 activityLabel;
1082 String8 activityIcon;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001083 String8 activityBanner;
Adam Lesinski282e1812014-01-23 18:17:42 -08001084 String8 receiverName;
1085 String8 serviceName;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001086 Vector<String8> supportedInput;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001087
1088 FeatureGroup commonFeatures;
1089 Vector<FeatureGroup> featureGroups;
1090 KeyedVector<String8, ImpliedFeature> impliedFeatures;
1091
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001092 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
1093 code != ResXMLTree::BAD_DOCUMENT) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001094 if (code == ResXMLTree::END_TAG) {
1095 depth--;
1096 if (depth < 2) {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001097 if (withinSupportsInput && !supportedInput.isEmpty()) {
1098 printf("supports-input: '");
1099 const size_t N = supportedInput.size();
1100 for (size_t i=0; i<N; i++) {
Maurice Chu2675f762013-10-22 17:33:11 -07001101 printf("%s", ResTable::normalizeForOutput(
1102 supportedInput[i].string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001103 if (i != N - 1) {
1104 printf("' '");
1105 } else {
1106 printf("'\n");
1107 }
1108 }
1109 supportedInput.clear();
1110 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001111 withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001112 withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001113 withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001114 } else if (depth < 3) {
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001115 if (withinActivity && isMainActivity) {
Maurice Chu2675f762013-10-22 17:33:11 -07001116 String8 aName(getComponentName(pkg, activityName));
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001117 if (isLauncherActivity) {
1118 printf("launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001119 if (aName.length() > 0) {
1120 printf(" name='%s' ",
1121 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001122 }
1123 printf(" label='%s' icon='%s'\n",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001124 ResTable::normalizeForOutput(activityLabel.string())
1125 .string(),
1126 ResTable::normalizeForOutput(activityIcon.string())
1127 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001128 }
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001129 if (isLeanbackLauncherActivity) {
1130 printf("leanback-launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001131 if (aName.length() > 0) {
1132 printf(" name='%s' ",
1133 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001134 }
1135 printf(" label='%s' icon='%s' banner='%s'\n",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001136 ResTable::normalizeForOutput(activityLabel.string())
1137 .string(),
1138 ResTable::normalizeForOutput(activityIcon.string())
1139 .string(),
1140 ResTable::normalizeForOutput(activityBanner.string())
1141 .string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001142 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001143 }
1144 if (!hasIntentFilter) {
1145 hasOtherActivities |= withinActivity;
1146 hasOtherReceivers |= withinReceiver;
1147 hasOtherServices |= withinService;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001148 } else {
1149 if (withinService) {
1150 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
1151 hasBindNfcServicePermission);
1152 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
1153 hasBindNfcServicePermission);
1154 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001155 }
1156 withinActivity = false;
1157 withinService = false;
1158 withinReceiver = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001159 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001160 hasIntentFilter = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001161 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001162 } else if (depth < 4) {
1163 if (withinIntentFilter) {
1164 if (withinActivity) {
1165 hasMainActivity |= actMainActivity;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001166 hasLauncher |= catLauncher;
1167 hasCameraActivity |= actCamera;
1168 hasCameraSecureActivity |= actCameraSecure;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001169 hasOtherActivities |=
1170 !actMainActivity && !actCamera && !actCameraSecure;
Adam Lesinski282e1812014-01-23 18:17:42 -08001171 } else if (withinReceiver) {
1172 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001173 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
1174 hasBindDeviceAdminPermission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001175 hasOtherReceivers |=
1176 (!actWidgetReceivers && !actDeviceAdminEnabled);
Adam Lesinski282e1812014-01-23 18:17:42 -08001177 } else if (withinService) {
1178 hasImeService |= actImeService;
1179 hasWallpaperService |= actWallpaperService;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001180 hasAccessibilityService |= (actAccessibilityService &&
1181 hasBindAccessibilityServicePermission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001182 hasPrintService |=
1183 (actPrintService && hasBindPrintServicePermission);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001184 hasNotificationListenerService |= actNotificationListenerService &&
1185 hasBindNotificationListenerServicePermission;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001186 hasDreamService |= actDreamService && hasBindDreamServicePermission;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001187 hasOtherServices |= (!actImeService && !actWallpaperService &&
Adam Lesinski94fc9122013-09-30 17:16:09 -07001188 !actAccessibilityService && !actPrintService &&
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001189 !actHostApduService && !actOffHostApduService &&
1190 !actNotificationListenerService);
1191 } else if (withinProvider) {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001192 hasDocumentsProvider |=
1193 actDocumentsProvider && hasRequiredSafAttributes;
Adam Lesinski282e1812014-01-23 18:17:42 -08001194 }
1195 }
1196 withinIntentFilter = false;
1197 }
1198 continue;
1199 }
1200 if (code != ResXMLTree::START_TAG) {
1201 continue;
1202 }
1203 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001204
1205 const char16_t* ctag16 = tree.getElementName(&len);
1206 if (ctag16 == NULL) {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001207 SourcePos(manifestFile, tree.getLineNumber()).error(
1208 "ERROR: failed to get XML element name (bad string pool)");
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001209 goto bail;
1210 }
1211 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -08001212 //printf("Depth %d, %s\n", depth, tag.string());
1213 if (depth == 1) {
1214 if (tag != "manifest") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001215 SourcePos(manifestFile, tree.getLineNumber()).error(
1216 "ERROR: manifest does not start with <manifest> tag");
Adam Lesinski282e1812014-01-23 18:17:42 -08001217 goto bail;
1218 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001219 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -07001220 printf("package: name='%s' ",
1221 ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001222 int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR,
1223 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001224 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001225 SourcePos(manifestFile, tree.getLineNumber()).error(
1226 "ERROR getting 'android:versionCode' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001227 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001228 goto bail;
1229 }
1230 if (versionCode > 0) {
1231 printf("versionCode='%d' ", versionCode);
1232 } else {
1233 printf("versionCode='' ");
1234 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001235 String8 versionName = AaptXml::getResolvedAttribute(res, tree,
1236 VERSION_NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001237 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001238 SourcePos(manifestFile, tree.getLineNumber()).error(
1239 "ERROR getting 'android:versionName' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001240 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001241 goto bail;
1242 }
Adam Lesinski25d35a92014-08-11 09:41:56 -07001243 printf("versionName='%s'",
Maurice Chu2675f762013-10-22 17:33:11 -07001244 ResTable::normalizeForOutput(versionName.string()).string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001245
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001246 String8 splitName = AaptXml::getAttribute(tree, NULL, "split");
Adam Lesinski25d35a92014-08-11 09:41:56 -07001247 if (!splitName.isEmpty()) {
1248 printf(" split='%s'", ResTable::normalizeForOutput(
1249 splitName.string()).string());
1250 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001251
Alan Viverette11be9312017-11-09 15:41:44 -05001252 String8 platformBuildVersionName = AaptXml::getAttribute(tree, NULL,
Adam Lesinski5283fab2014-08-29 11:23:55 -07001253 "platformBuildVersionName");
Alan Viverette11be9312017-11-09 15:41:44 -05001254 if (platformBuildVersionName != "") {
1255 printf(" platformBuildVersionName='%s'", platformBuildVersionName.string());
1256 }
1257
1258 String8 platformBuildVersionCode = AaptXml::getAttribute(tree, NULL,
1259 "platformBuildVersionCode");
1260 if (platformBuildVersionCode != "") {
1261 printf(" platformBuildVersionCode='%s'", platformBuildVersionCode.string());
1262 }
1263
1264 int32_t compileSdkVersion = AaptXml::getIntegerAttribute(tree,
1265 COMPILE_SDK_VERSION_ATTR, &error);
1266 if (error != "") {
1267 SourcePos(manifestFile, tree.getLineNumber()).error(
1268 "ERROR getting 'android:compileSdkVersion' attribute: %s",
1269 error.string());
1270 goto bail;
1271 }
1272 if (compileSdkVersion > 0) {
1273 printf(" compileSdkVersion='%d'", compileSdkVersion);
1274 }
1275
1276 String8 compileSdkVersionCodename = AaptXml::getResolvedAttribute(res, tree,
1277 COMPILE_SDK_VERSION_CODENAME_ATTR, &error);
1278 if (compileSdkVersionCodename != "") {
1279 printf(" compileSdkVersionCodename='%s'", ResTable::normalizeForOutput(
1280 compileSdkVersionCodename.string()).string());
1281 }
1282
Adam Lesinski25d35a92014-08-11 09:41:56 -07001283 printf("\n");
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001284
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001285 int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
1286 INSTALL_LOCATION_ATTR, &error);
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001287 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001288 SourcePos(manifestFile, tree.getLineNumber()).error(
1289 "ERROR getting 'android:installLocation' attribute: %s",
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001290 error.string());
1291 goto bail;
1292 }
1293
1294 if (installLocation >= 0) {
1295 printf("install-location:'");
1296 switch (installLocation) {
1297 case 0:
1298 printf("auto");
1299 break;
1300 case 1:
1301 printf("internalOnly");
1302 break;
1303 case 2:
1304 printf("preferExternal");
1305 break;
1306 default:
1307 fprintf(stderr, "Invalid installLocation %d\n", installLocation);
1308 goto bail;
1309 }
1310 printf("'\n");
1311 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001312 } else if (depth == 2) {
1313 withinApplication = false;
1314 if (tag == "application") {
1315 withinApplication = true;
1316
1317 String8 label;
1318 const size_t NL = locales.size();
1319 for (size_t i=0; i<NL; i++) {
1320 const char* localeStr = locales[i].string();
Adam Lesinskia77685f2016-10-03 16:26:28 -07001321 assets.setConfiguration(config, localeStr != NULL ? localeStr : "");
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001322 String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1323 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001324 if (llabel != "") {
1325 if (localeStr == NULL || strlen(localeStr) == 0) {
1326 label = llabel;
Maurice Chu2675f762013-10-22 17:33:11 -07001327 printf("application-label:'%s'\n",
1328 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001329 } else {
1330 if (label == "") {
1331 label = llabel;
1332 }
1333 printf("application-label-%s:'%s'\n", localeStr,
Maurice Chu2675f762013-10-22 17:33:11 -07001334 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001335 }
1336 }
1337 }
1338
1339 ResTable_config tmpConfig = config;
1340 const size_t ND = densities.size();
1341 for (size_t i=0; i<ND; i++) {
1342 tmpConfig.density = densities[i];
1343 assets.setConfiguration(tmpConfig);
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001344 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1345 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001346 if (icon != "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001347 printf("application-icon-%d:'%s'\n", densities[i],
1348 ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001349 }
1350 }
1351 assets.setConfiguration(config);
1352
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001353 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001354 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001355 SourcePos(manifestFile, tree.getLineNumber()).error(
1356 "ERROR getting 'android:icon' attribute: %s", error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001357 goto bail;
1358 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001359 int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0,
1360 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001361 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001362 SourcePos(manifestFile, tree.getLineNumber()).error(
1363 "ERROR getting 'android:testOnly' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001364 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001365 goto bail;
1366 }
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001367
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001368 String8 banner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1369 &error);
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001370 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001371 SourcePos(manifestFile, tree.getLineNumber()).error(
1372 "ERROR getting 'android:banner' attribute: %s", error.string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001373 goto bail;
1374 }
Maurice Chu2675f762013-10-22 17:33:11 -07001375 printf("application: label='%s' ",
1376 ResTable::normalizeForOutput(label.string()).string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001377 printf("icon='%s'", ResTable::normalizeForOutput(icon.string()).string());
1378 if (banner != "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001379 printf(" banner='%s'",
1380 ResTable::normalizeForOutput(banner.string()).string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001381 }
1382 printf("\n");
Adam Lesinski282e1812014-01-23 18:17:42 -08001383 if (testOnly != 0) {
1384 printf("testOnly='%d'\n", testOnly);
1385 }
1386
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001387 int32_t isGame = AaptXml::getResolvedIntegerAttribute(res, tree,
1388 ISGAME_ATTR, 0, &error);
1389 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001390 SourcePos(manifestFile, tree.getLineNumber()).error(
1391 "ERROR getting 'android:isGame' attribute: %s", error.string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001392 goto bail;
1393 }
1394 if (isGame != 0) {
1395 printf("application-isGame\n");
1396 }
1397
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001398 int32_t debuggable = AaptXml::getResolvedIntegerAttribute(res, tree,
1399 DEBUGGABLE_ATTR, 0, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001400 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001401 SourcePos(manifestFile, tree.getLineNumber()).error(
1402 "ERROR getting 'android:debuggable' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001403 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001404 goto bail;
1405 }
1406 if (debuggable != 0) {
1407 printf("application-debuggable\n");
1408 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07001409
1410 // We must search by name because the multiArch flag hasn't been API
1411 // frozen yet.
1412 int32_t multiArchIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
1413 "multiArch");
1414 if (multiArchIndex >= 0) {
1415 Res_value value;
1416 if (tree.getAttributeValue(multiArchIndex, &value) != NO_ERROR) {
1417 if (value.dataType >= Res_value::TYPE_FIRST_INT &&
1418 value.dataType <= Res_value::TYPE_LAST_INT) {
1419 hasMultiArch = value.data;
1420 }
1421 }
1422 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001423 } else if (tag == "uses-sdk") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001424 int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR,
1425 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001426 if (error != "") {
1427 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001428 String8 name = AaptXml::getResolvedAttribute(res, tree,
1429 MIN_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001430 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001431 SourcePos(manifestFile, tree.getLineNumber()).error(
1432 "ERROR getting 'android:minSdkVersion' attribute: %s",
Adam Lesinski282e1812014-01-23 18:17:42 -08001433 error.string());
1434 goto bail;
1435 }
1436 if (name == "Donut") targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001437 printf("sdkVersion:'%s'\n",
1438 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001439 } else if (code != -1) {
1440 targetSdk = code;
1441 printf("sdkVersion:'%d'\n", code);
1442 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001443 code = AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -08001444 if (code != -1) {
1445 printf("maxSdkVersion:'%d'\n", code);
1446 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001447 code = AaptXml::getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001448 if (error != "") {
1449 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001450 String8 name = AaptXml::getResolvedAttribute(res, tree,
1451 TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001452 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001453 SourcePos(manifestFile, tree.getLineNumber()).error(
1454 "ERROR getting 'android:targetSdkVersion' attribute: %s",
Adam Lesinski282e1812014-01-23 18:17:42 -08001455 error.string());
1456 goto bail;
1457 }
1458 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001459 printf("targetSdkVersion:'%s'\n",
1460 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001461 } else if (code != -1) {
1462 if (targetSdk < code) {
1463 targetSdk = code;
1464 }
1465 printf("targetSdkVersion:'%d'\n", code);
1466 }
1467 } else if (tag == "uses-configuration") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001468 int32_t reqTouchScreen = AaptXml::getIntegerAttribute(tree,
1469 REQ_TOUCH_SCREEN_ATTR, 0);
1470 int32_t reqKeyboardType = AaptXml::getIntegerAttribute(tree,
1471 REQ_KEYBOARD_TYPE_ATTR, 0);
1472 int32_t reqHardKeyboard = AaptXml::getIntegerAttribute(tree,
1473 REQ_HARD_KEYBOARD_ATTR, 0);
1474 int32_t reqNavigation = AaptXml::getIntegerAttribute(tree,
1475 REQ_NAVIGATION_ATTR, 0);
1476 int32_t reqFiveWayNav = AaptXml::getIntegerAttribute(tree,
1477 REQ_FIVE_WAY_NAV_ATTR, 0);
Adam Lesinski282e1812014-01-23 18:17:42 -08001478 printf("uses-configuration:");
1479 if (reqTouchScreen != 0) {
1480 printf(" reqTouchScreen='%d'", reqTouchScreen);
1481 }
1482 if (reqKeyboardType != 0) {
1483 printf(" reqKeyboardType='%d'", reqKeyboardType);
1484 }
1485 if (reqHardKeyboard != 0) {
1486 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1487 }
1488 if (reqNavigation != 0) {
1489 printf(" reqNavigation='%d'", reqNavigation);
1490 }
1491 if (reqFiveWayNav != 0) {
1492 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1493 }
1494 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001495 } else if (tag == "supports-input") {
1496 withinSupportsInput = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001497 } else if (tag == "supports-screens") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001498 smallScreen = AaptXml::getIntegerAttribute(tree,
1499 SMALL_SCREEN_ATTR, 1);
1500 normalScreen = AaptXml::getIntegerAttribute(tree,
1501 NORMAL_SCREEN_ATTR, 1);
1502 largeScreen = AaptXml::getIntegerAttribute(tree,
1503 LARGE_SCREEN_ATTR, 1);
1504 xlargeScreen = AaptXml::getIntegerAttribute(tree,
1505 XLARGE_SCREEN_ATTR, 1);
1506 anyDensity = AaptXml::getIntegerAttribute(tree,
1507 ANY_DENSITY_ATTR, 1);
1508 requiresSmallestWidthDp = AaptXml::getIntegerAttribute(tree,
1509 REQUIRES_SMALLEST_WIDTH_DP_ATTR, 0);
1510 compatibleWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1511 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, 0);
1512 largestWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1513 LARGEST_WIDTH_LIMIT_DP_ATTR, 0);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001514 } else if (tag == "feature-group") {
1515 withinFeatureGroup = true;
1516 FeatureGroup group;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001517 group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001518 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001519 SourcePos(manifestFile, tree.getLineNumber()).error(
1520 "ERROR getting 'android:label' attribute: %s", error.string());
Adam Lesinski2c72b682014-06-24 09:56:01 -07001521 goto bail;
1522 }
1523 featureGroups.add(group);
1524
Adam Lesinski282e1812014-01-23 18:17:42 -08001525 } else if (tag == "uses-feature") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001526 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001527 if (name != "" && error == "") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001528 const char* androidSchema =
1529 "http://schemas.android.com/apk/res/android";
Adam Lesinski282e1812014-01-23 18:17:42 -08001530
Adam Lesinski694d0a72016-04-06 16:12:04 -07001531 int32_t req = AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1,
1532 &error);
1533 if (error != "") {
1534 SourcePos(manifestFile, tree.getLineNumber()).error(
1535 "failed to read attribute 'android:required': %s",
1536 error.string());
1537 goto bail;
1538 }
1539
1540 int32_t version = AaptXml::getIntegerAttribute(tree, androidSchema,
1541 "version", 0, &error);
1542 if (error != "") {
1543 SourcePos(manifestFile, tree.getLineNumber()).error(
1544 "failed to read attribute 'android:version': %s",
1545 error.string());
1546 goto bail;
1547 }
1548
1549 commonFeatures.features.add(name, Feature(req != 0, version));
Adam Lesinski2c72b682014-06-24 09:56:01 -07001550 if (req) {
1551 addParentFeatures(&commonFeatures, name);
Adam Lesinski282e1812014-01-23 18:17:42 -08001552 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001553 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001554 int vers = AaptXml::getIntegerAttribute(tree,
Adam Lesinski282e1812014-01-23 18:17:42 -08001555 GL_ES_VERSION_ATTR, &error);
1556 if (error == "") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001557 if (vers > commonFeatures.openGLESVersion) {
1558 commonFeatures.openGLESVersion = vers;
1559 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001560 }
1561 }
1562 } else if (tag == "uses-permission") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001563 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001564 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001565 SourcePos(manifestFile, tree.getLineNumber()).error(
1566 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001567 goto bail;
1568 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001569
1570 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001571 SourcePos(manifestFile, tree.getLineNumber()).error(
1572 "ERROR: missing 'android:name' for uses-permission");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001573 goto bail;
1574 }
1575
1576 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, false);
1577
Adam Lesinski2386df22016-12-28 15:08:58 -05001578 const int32_t maxSdkVersion =
1579 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, -1);
Dianne Hackborncd154e92017-02-28 17:37:35 -08001580 const String8 requiredFeature = AaptXml::getAttribute(tree,
1581 REQUIRED_FEATURE_ATTR, &error);
1582 const String8 requiredNotFeature = AaptXml::getAttribute(tree,
1583 REQUIRED_NOT_FEATURE_ATTR, &error);
Adam Lesinski2386df22016-12-28 15:08:58 -05001584
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001585 if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1586 hasWriteExternalStoragePermission = true;
Adam Lesinski2386df22016-12-28 15:08:58 -05001587 writeExternalStoragePermissionMaxSdkVersion = maxSdkVersion;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001588 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1589 hasReadExternalStoragePermission = true;
1590 } else if (name == "android.permission.READ_PHONE_STATE") {
1591 hasReadPhoneStatePermission = true;
1592 } else if (name == "android.permission.READ_CONTACTS") {
1593 hasReadContactsPermission = true;
1594 } else if (name == "android.permission.WRITE_CONTACTS") {
1595 hasWriteContactsPermission = true;
1596 } else if (name == "android.permission.READ_CALL_LOG") {
1597 hasReadCallLogPermission = true;
1598 } else if (name == "android.permission.WRITE_CALL_LOG") {
1599 hasWriteCallLogPermission = true;
1600 }
1601
1602 printUsesPermission(name,
1603 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
Dianne Hackborncd154e92017-02-28 17:37:35 -08001604 maxSdkVersion, requiredFeature, requiredNotFeature);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001605
1606 } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
1607 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1608 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001609 SourcePos(manifestFile, tree.getLineNumber()).error(
1610 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001611 goto bail;
1612 }
1613
1614 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001615 SourcePos(manifestFile, tree.getLineNumber()).error(
1616 "ERROR: missing 'android:name' for uses-permission-sdk-23");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001617 goto bail;
1618 }
1619
1620 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, true);
1621
1622 printUsesPermissionSdk23(
1623 name, AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
1624
Adam Lesinski282e1812014-01-23 18:17:42 -08001625 } else if (tag == "uses-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001626 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001627 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001628 printf("uses-package:'%s'\n",
1629 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001630 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001631 SourcePos(manifestFile, tree.getLineNumber()).error(
1632 "ERROR getting 'android:name' attribute: %s", error.string());
1633 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001634 }
1635 } else if (tag == "original-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001636 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001637 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001638 printf("original-package:'%s'\n",
1639 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001640 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001641 SourcePos(manifestFile, tree.getLineNumber()).error(
1642 "ERROR getting 'android:name' attribute: %s", error.string());
1643 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001644 }
1645 } else if (tag == "supports-gl-texture") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001646 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001647 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001648 printf("supports-gl-texture:'%s'\n",
1649 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001650 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001651 SourcePos(manifestFile, tree.getLineNumber()).error(
1652 "ERROR getting 'android:name' attribute: %s", error.string());
1653 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001654 }
1655 } else if (tag == "compatible-screens") {
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001656 printCompatibleScreens(tree, &error);
1657 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001658 SourcePos(manifestFile, tree.getLineNumber()).error(
1659 "ERROR getting compatible screens: %s", error.string());
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001660 goto bail;
1661 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001662 depth--;
1663 } else if (tag == "package-verifier") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001664 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001665 if (name != "" && error == "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001666 String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR,
1667 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001668 if (publicKey != "" && error == "") {
1669 printf("package-verifier: name='%s' publicKey='%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001670 ResTable::normalizeForOutput(name.string()).string(),
1671 ResTable::normalizeForOutput(publicKey.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001672 }
1673 }
1674 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001675 } else if (depth == 3) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001676 withinActivity = false;
1677 withinReceiver = false;
1678 withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001679 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001680 hasIntentFilter = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001681 hasMetaHostPaymentCategory = false;
1682 hasMetaOffHostPaymentCategory = false;
1683 hasBindDeviceAdminPermission = false;
1684 hasBindInputMethodPermission = false;
1685 hasBindAccessibilityServicePermission = false;
1686 hasBindPrintServicePermission = false;
1687 hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001688 hasRequiredSafAttributes = false;
1689 hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001690 hasBindDreamServicePermission = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001691 if (withinApplication) {
1692 if(tag == "activity") {
1693 withinActivity = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001694 activityName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001695 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001696 SourcePos(manifestFile, tree.getLineNumber()).error(
1697 "ERROR getting 'android:name' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001698 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001699 goto bail;
1700 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001701
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001702 activityLabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1703 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001704 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001705 SourcePos(manifestFile, tree.getLineNumber()).error(
1706 "ERROR getting 'android:label' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001707 error.string());
1708 goto bail;
1709 }
1710
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001711 activityIcon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1712 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001713 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001714 SourcePos(manifestFile, tree.getLineNumber()).error(
1715 "ERROR getting 'android:icon' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001716 error.string());
1717 goto bail;
1718 }
1719
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001720 activityBanner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1721 &error);
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001722 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001723 SourcePos(manifestFile, tree.getLineNumber()).error(
1724 "ERROR getting 'android:banner' attribute: %s",
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001725 error.string());
1726 goto bail;
1727 }
1728
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001729 int32_t orien = AaptXml::getResolvedIntegerAttribute(res, tree,
Michael Wrightec4fdec2013-09-06 16:50:52 -07001730 SCREEN_ORIENTATION_ATTR, &error);
1731 if (error == "") {
1732 if (orien == 0 || orien == 6 || orien == 8) {
1733 // Requests landscape, sensorLandscape, or reverseLandscape.
Adam Lesinski43158772015-11-11 15:13:55 -08001734 addImpliedFeature(
1735 &impliedFeatures, "android.hardware.screen.landscape",
1736 String8("one or more activities have specified a "
1737 "landscape orientation"),
1738 false);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001739 } else if (orien == 1 || orien == 7 || orien == 9) {
1740 // Requests portrait, sensorPortrait, or reversePortrait.
Adam Lesinski43158772015-11-11 15:13:55 -08001741 addImpliedFeature(
1742 &impliedFeatures, "android.hardware.screen.portrait",
1743 String8("one or more activities have specified a "
1744 "portrait orientation"),
1745 false);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001746 }
1747 }
1748 } else if (tag == "uses-library") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001749 String8 libraryName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001750 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001751 SourcePos(manifestFile, tree.getLineNumber()).error(
Michael Wrightec4fdec2013-09-06 16:50:52 -07001752 "ERROR getting 'android:name' attribute for uses-library"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001753 " %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001754 goto bail;
1755 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001756 int req = AaptXml::getIntegerAttribute(tree,
1757 REQUIRED_ATTR, 1);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001758 printf("uses-library%s:'%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001759 req ? "" : "-not-required", ResTable::normalizeForOutput(
1760 libraryName.string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001761 } else if (tag == "receiver") {
1762 withinReceiver = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001763 receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001764
1765 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001766 SourcePos(manifestFile, tree.getLineNumber()).error(
Michael Wrightec4fdec2013-09-06 16:50:52 -07001767 "ERROR getting 'android:name' attribute for receiver:"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001768 " %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001769 goto bail;
1770 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001771
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001772 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1773 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001774 if (error == "") {
1775 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1776 hasBindDeviceAdminPermission = true;
1777 }
1778 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001779 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001780 "ERROR getting 'android:permission' attribute for"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001781 " receiver '%s': %s",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001782 receiverName.string(), error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001783 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001784 } else if (tag == "service") {
1785 withinService = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001786 serviceName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001787
1788 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001789 SourcePos(manifestFile, tree.getLineNumber()).error(
1790 "ERROR getting 'android:name' attribute for "
1791 "service:%s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001792 goto bail;
1793 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001794
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001795 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1796 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001797 if (error == "") {
1798 if (permission == "android.permission.BIND_INPUT_METHOD") {
1799 hasBindInputMethodPermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001800 } else if (permission ==
1801 "android.permission.BIND_ACCESSIBILITY_SERVICE") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001802 hasBindAccessibilityServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001803 } else if (permission ==
1804 "android.permission.BIND_PRINT_SERVICE") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001805 hasBindPrintServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001806 } else if (permission ==
1807 "android.permission.BIND_NFC_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07001808 hasBindNfcServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001809 } else if (permission ==
1810 "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001811 hasBindNotificationListenerServicePermission = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001812 } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
1813 hasBindDreamServicePermission = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001814 }
1815 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001816 SourcePos(manifestFile, tree.getLineNumber()).error(
1817 "ERROR getting 'android:permission' attribute for "
1818 "service '%s': %s", serviceName.string(), error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001819 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001820 } else if (tag == "provider") {
1821 withinProvider = true;
1822
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001823 bool exported = AaptXml::getResolvedIntegerAttribute(res, tree,
1824 EXPORTED_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001825 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001826 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001827 "ERROR getting 'android:exported' attribute for provider:"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001828 " %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001829 goto bail;
1830 }
1831
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001832 bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute(
1833 res, tree, GRANT_URI_PERMISSIONS_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001834 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001835 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001836 "ERROR getting 'android:grantUriPermissions' attribute for "
Adam Lesinski10de3af12016-07-13 10:14:03 -07001837 "provider: %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001838 goto bail;
1839 }
1840
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001841 String8 permission = AaptXml::getResolvedAttribute(res, tree,
1842 PERMISSION_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001843 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001844 SourcePos(manifestFile, tree.getLineNumber()).error(
1845 "ERROR getting 'android:permission' attribute for "
1846 "provider: %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001847 goto bail;
1848 }
1849
1850 hasRequiredSafAttributes |= exported && grantUriPermissions &&
1851 permission == "android.permission.MANAGE_DOCUMENTS";
1852
Michael Wrightec4fdec2013-09-06 16:50:52 -07001853 } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001854 String8 metaDataName = AaptXml::getResolvedAttribute(res, tree,
1855 NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001856 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001857 SourcePos(manifestFile, tree.getLineNumber()).error(
1858 "ERROR getting 'android:name' attribute for "
1859 "meta-data: %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001860 goto bail;
1861 }
Maurice Chu2675f762013-10-22 17:33:11 -07001862 printf("meta-data: name='%s' ",
1863 ResTable::normalizeForOutput(metaDataName.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001864 printResolvedResourceAttribute(res, tree, VALUE_ATTR, String8("value"),
Maurice Chu76327312013-10-16 18:28:46 -07001865 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001866 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001867 // Try looking for a RESOURCE_ATTR
1868 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001869 printResolvedResourceAttribute(res, tree, RESOURCE_ATTR,
Maurice Chu76327312013-10-16 18:28:46 -07001870 String8("resource"), &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001871 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001872 SourcePos(manifestFile, tree.getLineNumber()).error(
1873 "ERROR getting 'android:value' or "
Maurice Chu76327312013-10-16 18:28:46 -07001874 "'android:resource' attribute for "
Adam Lesinski10de3af12016-07-13 10:14:03 -07001875 "meta-data: %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001876 goto bail;
1877 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001878 }
Maurice Chu76327312013-10-16 18:28:46 -07001879 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001880 } else if (withinSupportsInput && tag == "input-type") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001881 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001882 if (name != "" && error == "") {
1883 supportedInput.add(name);
1884 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001885 SourcePos(manifestFile, tree.getLineNumber()).error(
1886 "ERROR getting 'android:name' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001887 error.string());
1888 goto bail;
1889 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001890 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001891 } else if (withinFeatureGroup && tag == "uses-feature") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001892 const String8 androidSchema("http://schemas.android.com/apk/res/android");
Adam Lesinski2c72b682014-06-24 09:56:01 -07001893 FeatureGroup& top = featureGroups.editTop();
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001894
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001895 String8 name = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001896 if (name != "" && error == "") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001897 Feature feature(true);
1898
1899 int32_t featureVers = AaptXml::getIntegerAttribute(
1900 tree, androidSchema.string(), "version", 0, &error);
1901 if (error == "") {
1902 feature.version = featureVers;
1903 } else {
1904 SourcePos(manifestFile, tree.getLineNumber()).error(
1905 "failed to read attribute 'android:version': %s",
1906 error.string());
1907 goto bail;
1908 }
1909
1910 top.features.add(name, feature);
Adam Lesinskid3edfde2014-08-08 17:32:44 -07001911 addParentFeatures(&top, name);
Adam Lesinski694d0a72016-04-06 16:12:04 -07001912
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001913 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001914 int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR,
1915 &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001916 if (error == "") {
1917 if (vers > top.openGLESVersion) {
1918 top.openGLESVersion = vers;
1919 }
1920 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001921 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001922 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001923 } else if (depth == 4) {
1924 if (tag == "intent-filter") {
1925 hasIntentFilter = true;
1926 withinIntentFilter = true;
1927 actMainActivity = false;
1928 actWidgetReceivers = false;
1929 actImeService = false;
1930 actWallpaperService = false;
1931 actAccessibilityService = false;
1932 actPrintService = false;
1933 actDeviceAdminEnabled = false;
1934 actHostApduService = false;
1935 actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001936 actDocumentsProvider = false;
1937 actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001938 actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001939 actCamera = false;
1940 actCameraSecure = false;
1941 catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001942 } else if (withinService && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001943 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001944 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001945 SourcePos(manifestFile, tree.getLineNumber()).error(
1946 "ERROR getting 'android:name' attribute for "
1947 "meta-data tag in service '%s': %s", serviceName.string(),
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001948 error.string());
Adam Lesinski94fc9122013-09-30 17:16:09 -07001949 goto bail;
1950 }
1951
1952 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1953 name == "android.nfc.cardemulation.off_host_apdu_service") {
1954 bool offHost = true;
1955 if (name == "android.nfc.cardemulation.host_apdu_service") {
1956 offHost = false;
1957 }
1958
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001959 String8 xmlPath = AaptXml::getResolvedAttribute(res, tree,
1960 RESOURCE_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001961 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001962 SourcePos(manifestFile, tree.getLineNumber()).error(
1963 "ERROR getting 'android:resource' attribute for "
1964 "meta-data tag in service '%s': %s",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001965 serviceName.string(), error.string());
Adam Lesinski94fc9122013-09-30 17:16:09 -07001966 goto bail;
1967 }
1968
1969 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1970 offHost, &error);
1971 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001972 SourcePos(manifestFile, tree.getLineNumber()).error(
1973 "ERROR getting AID category for service '%s'",
Adam Lesinski94fc9122013-09-30 17:16:09 -07001974 serviceName.string());
1975 goto bail;
1976 }
1977
1978 const size_t catLen = categories.size();
1979 for (size_t i = 0; i < catLen; i++) {
1980 bool paymentCategory = (categories[i] == "payment");
1981 if (offHost) {
1982 hasMetaOffHostPaymentCategory |= paymentCategory;
1983 } else {
1984 hasMetaHostPaymentCategory |= paymentCategory;
1985 }
1986 }
1987 }
1988 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001989 } else if ((depth == 5) && withinIntentFilter) {
1990 String8 action;
1991 if (tag == "action") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001992 action = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001993 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001994 SourcePos(manifestFile, tree.getLineNumber()).error(
1995 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001996 goto bail;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001997 }
1998
Adam Lesinskia5018c92013-09-30 16:23:15 -07001999 if (withinActivity) {
2000 if (action == "android.intent.action.MAIN") {
2001 isMainActivity = true;
2002 actMainActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002003 } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
2004 action == "android.media.action.VIDEO_CAMERA") {
2005 actCamera = true;
2006 } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
2007 actCameraSecure = true;
Michael Wrightec4fdec2013-09-06 16:50:52 -07002008 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002009 } else if (withinReceiver) {
2010 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
2011 actWidgetReceivers = true;
2012 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
2013 actDeviceAdminEnabled = true;
2014 }
2015 } else if (withinService) {
2016 if (action == "android.view.InputMethod") {
2017 actImeService = true;
2018 } else if (action == "android.service.wallpaper.WallpaperService") {
2019 actWallpaperService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002020 } else if (action ==
2021 "android.accessibilityservice.AccessibilityService") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07002022 actAccessibilityService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002023 } else if (action =="android.printservice.PrintService") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07002024 actPrintService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002025 } else if (action ==
2026 "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07002027 actHostApduService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002028 } else if (action ==
2029 "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07002030 actOffHostApduService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002031 } else if (action ==
2032 "android.service.notification.NotificationListenerService") {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002033 actNotificationListenerService = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04002034 } else if (action == "android.service.dreams.DreamService") {
2035 actDreamService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002036 }
2037 } else if (withinProvider) {
2038 if (action == "android.content.action.DOCUMENTS_PROVIDER") {
2039 actDocumentsProvider = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07002040 }
2041 }
2042 if (action == "android.intent.action.SEARCH") {
2043 isSearchable = true;
2044 }
2045 }
2046
2047 if (tag == "category") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07002048 String8 category = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07002049 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07002050 SourcePos(manifestFile, tree.getLineNumber()).error(
2051 "ERROR getting 'name' attribute: %s", error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07002052 goto bail;
2053 }
2054 if (withinActivity) {
2055 if (category == "android.intent.category.LAUNCHER") {
2056 isLauncherActivity = true;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08002057 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
2058 isLeanbackLauncherActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002059 } else if (category == "android.intent.category.HOME") {
2060 catLauncher = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08002061 }
2062 }
2063 }
2064 }
2065 }
2066
2067 // Pre-1.6 implicitly granted permission compatibility logic
2068 if (targetSdk < 4) {
2069 if (!hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002070 printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
2071 printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
2072 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002073 hasWriteExternalStoragePermission = true;
2074 }
2075 if (!hasReadPhoneStatePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002076 printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
2077 printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
2078 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002079 }
2080 }
2081
2082 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
2083 // force them to always take READ_EXTERNAL_STORAGE as well. We always
2084 // do this (regardless of target API version) because we can't have
2085 // an app with write permission but not read permission.
2086 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
Adam Lesinski2386df22016-12-28 15:08:58 -05002087 printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
2088 false /* optional */, writeExternalStoragePermissionMaxSdkVersion);
Adam Lesinski58f1f362013-11-12 12:59:08 -08002089 printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
Adam Lesinski2386df22016-12-28 15:08:58 -05002090 String8("requested WRITE_EXTERNAL_STORAGE"),
2091 writeExternalStoragePermissionMaxSdkVersion);
Adam Lesinski282e1812014-01-23 18:17:42 -08002092 }
2093
2094 // Pre-JellyBean call log permission compatibility.
2095 if (targetSdk < 16) {
2096 if (!hasReadCallLogPermission && hasReadContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002097 printUsesPermission(String8("android.permission.READ_CALL_LOG"));
2098 printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
2099 String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002100 }
2101 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002102 printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
2103 printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
2104 String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002105 }
2106 }
2107
Adam Lesinskica955a42016-08-01 16:44:29 -07002108 // If the app hasn't declared the touchscreen as a feature requirement (either
2109 // directly or implied, required or not), then the faketouch feature is implied.
2110 if (!hasFeature("android.hardware.touchscreen", commonFeatures, impliedFeatures)) {
2111 addImpliedFeature(&impliedFeatures, "android.hardware.faketouch",
Adam Lesinski43158772015-11-11 15:13:55 -08002112 String8("default feature for all apps"), false);
Adam Lesinskica955a42016-08-01 16:44:29 -07002113 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07002114
2115 const size_t numFeatureGroups = featureGroups.size();
2116 if (numFeatureGroups == 0) {
2117 // If no <feature-group> tags were defined, apply auto-implied features.
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002118 printDefaultFeatureGroup(commonFeatures, impliedFeatures);
Adam Lesinski2c72b682014-06-24 09:56:01 -07002119
2120 } else {
2121 // <feature-group> tags are defined, so we ignore implied features and
2122 for (size_t i = 0; i < numFeatureGroups; i++) {
2123 FeatureGroup& grp = featureGroups.editItemAt(i);
2124
Adam Lesinskid7a94da2014-07-25 14:38:54 -07002125 if (commonFeatures.openGLESVersion > grp.openGLESVersion) {
2126 grp.openGLESVersion = commonFeatures.openGLESVersion;
2127 }
2128
Adam Lesinski2c72b682014-06-24 09:56:01 -07002129 // Merge the features defined in the top level (not inside a <feature-group>)
2130 // with this feature group.
2131 const size_t numCommonFeatures = commonFeatures.features.size();
2132 for (size_t j = 0; j < numCommonFeatures; j++) {
2133 if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07002134 grp.features.add(commonFeatures.features.keyAt(j),
2135 commonFeatures.features[j]);
Adam Lesinski2c72b682014-06-24 09:56:01 -07002136 }
2137 }
2138
Adam Lesinski73a05112014-12-08 12:53:17 -08002139 if (!grp.features.isEmpty()) {
Adam Lesinski2c72b682014-06-24 09:56:01 -07002140 printFeatureGroup(grp);
Adam Lesinski282e1812014-01-23 18:17:42 -08002141 }
2142 }
2143 }
2144
Adam Lesinski282e1812014-01-23 18:17:42 -08002145
Adam Lesinski282e1812014-01-23 18:17:42 -08002146 if (hasWidgetReceivers) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002147 printComponentPresence("app-widget");
Adam Lesinski282e1812014-01-23 18:17:42 -08002148 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002149 if (hasDeviceAdminReceiver) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002150 printComponentPresence("device-admin");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002151 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002152 if (hasImeService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002153 printComponentPresence("ime");
Adam Lesinski282e1812014-01-23 18:17:42 -08002154 }
2155 if (hasWallpaperService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002156 printComponentPresence("wallpaper");
Adam Lesinski282e1812014-01-23 18:17:42 -08002157 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002158 if (hasAccessibilityService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002159 printComponentPresence("accessibility");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002160 }
2161 if (hasPrintService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002162 printComponentPresence("print-service");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002163 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07002164 if (hasPaymentService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002165 printComponentPresence("payment");
2166 }
2167 if (isSearchable) {
2168 printComponentPresence("search");
2169 }
2170 if (hasDocumentsProvider) {
2171 printComponentPresence("document-provider");
2172 }
2173 if (hasLauncher) {
2174 printComponentPresence("launcher");
2175 }
2176 if (hasNotificationListenerService) {
2177 printComponentPresence("notification-listener");
2178 }
John Spurlockeb8d1be2014-06-25 17:46:15 -04002179 if (hasDreamService) {
2180 printComponentPresence("dream");
2181 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002182 if (hasCameraActivity) {
2183 printComponentPresence("camera");
2184 }
2185 if (hasCameraSecureActivity) {
2186 printComponentPresence("camera-secure");
2187 }
2188
2189 if (hasMainActivity) {
2190 printf("main\n");
Adam Lesinski94fc9122013-09-30 17:16:09 -07002191 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002192 if (hasOtherActivities) {
2193 printf("other-activities\n");
2194 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002195 if (hasOtherReceivers) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002196 printf("other-receivers\n");
2197 }
2198 if (hasOtherServices) {
2199 printf("other-services\n");
2200 }
2201
2202 // For modern apps, if screen size buckets haven't been specified
2203 // but the new width ranges have, then infer the buckets from them.
2204 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
2205 && requiresSmallestWidthDp > 0) {
2206 int compatWidth = compatibleWidthLimitDp;
2207 if (compatWidth <= 0) {
2208 compatWidth = requiresSmallestWidthDp;
2209 }
2210 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
2211 smallScreen = -1;
2212 } else {
2213 smallScreen = 0;
2214 }
2215 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
2216 normalScreen = -1;
2217 } else {
2218 normalScreen = 0;
2219 }
2220 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
2221 largeScreen = -1;
2222 } else {
2223 largeScreen = 0;
2224 }
2225 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
2226 xlargeScreen = -1;
2227 } else {
2228 xlargeScreen = 0;
2229 }
2230 }
2231
2232 // Determine default values for any unspecified screen sizes,
2233 // based on the target SDK of the package. As of 4 (donut)
2234 // the screen size support was introduced, so all default to
2235 // enabled.
2236 if (smallScreen > 0) {
2237 smallScreen = targetSdk >= 4 ? -1 : 0;
2238 }
2239 if (normalScreen > 0) {
2240 normalScreen = -1;
2241 }
2242 if (largeScreen > 0) {
2243 largeScreen = targetSdk >= 4 ? -1 : 0;
2244 }
2245 if (xlargeScreen > 0) {
2246 // Introduced in Gingerbread.
2247 xlargeScreen = targetSdk >= 9 ? -1 : 0;
2248 }
2249 if (anyDensity > 0) {
2250 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
2251 || compatibleWidthLimitDp > 0) ? -1 : 0;
2252 }
2253 printf("supports-screens:");
2254 if (smallScreen != 0) {
2255 printf(" 'small'");
2256 }
2257 if (normalScreen != 0) {
2258 printf(" 'normal'");
2259 }
2260 if (largeScreen != 0) {
2261 printf(" 'large'");
2262 }
2263 if (xlargeScreen != 0) {
2264 printf(" 'xlarge'");
2265 }
2266 printf("\n");
2267 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
2268 if (requiresSmallestWidthDp > 0) {
2269 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
2270 }
2271 if (compatibleWidthLimitDp > 0) {
2272 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
2273 }
2274 if (largestWidthLimitDp > 0) {
2275 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
2276 }
2277
2278 printf("locales:");
2279 const size_t NL = locales.size();
2280 for (size_t i=0; i<NL; i++) {
2281 const char* localeStr = locales[i].string();
2282 if (localeStr == NULL || strlen(localeStr) == 0) {
2283 localeStr = "--_--";
2284 }
2285 printf(" '%s'", localeStr);
2286 }
2287 printf("\n");
2288
2289 printf("densities:");
2290 const size_t ND = densities.size();
2291 for (size_t i=0; i<ND; i++) {
2292 printf(" '%d'", densities[i]);
2293 }
2294 printf("\n");
2295
2296 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
2297 if (dir != NULL) {
2298 if (dir->getFileCount() > 0) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002299 SortedVector<String8> architectures;
Adam Lesinski282e1812014-01-23 18:17:42 -08002300 for (size_t i=0; i<dir->getFileCount(); i++) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002301 architectures.add(ResTable::normalizeForOutput(
2302 dir->getFileName(i).string()));
Adam Lesinski282e1812014-01-23 18:17:42 -08002303 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07002304
2305 bool outputAltNativeCode = false;
2306 // A multiArch package is one that contains 64-bit and
2307 // 32-bit versions of native code and expects 3rd-party
2308 // apps to load these native code libraries. Since most
2309 // 64-bit systems also support 32-bit apps, the apps
2310 // loading this multiArch package's code may be either
2311 // 32-bit or 64-bit.
2312 if (hasMultiArch) {
2313 // If this is a multiArch package, report the 64-bit
2314 // version only. Then as a separate entry, report the
2315 // rest.
2316 //
2317 // If we report the 32-bit architecture, this APK will
2318 // be installed on a 32-bit device, causing a large waste
2319 // of bandwidth and disk space. This assumes that
2320 // the developer of the multiArch package has also
2321 // made a version that is 32-bit only.
2322 String8 intel64("x86_64");
2323 String8 arm64("arm64-v8a");
2324 ssize_t index = architectures.indexOf(intel64);
2325 if (index < 0) {
2326 index = architectures.indexOf(arm64);
2327 }
2328
2329 if (index >= 0) {
2330 printf("native-code: '%s'\n", architectures[index].string());
2331 architectures.removeAt(index);
2332 outputAltNativeCode = true;
2333 }
2334 }
2335
2336 const size_t archCount = architectures.size();
2337 if (archCount > 0) {
2338 if (outputAltNativeCode) {
2339 printf("alt-");
2340 }
2341 printf("native-code:");
2342 for (size_t i = 0; i < archCount; i++) {
2343 printf(" '%s'", architectures[i].string());
2344 }
2345 printf("\n");
2346 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002347 }
2348 delete dir;
2349 }
2350 } else if (strcmp("badger", option) == 0) {
2351 printf("%s", CONSOLE_DATA);
2352 } else if (strcmp("configurations", option) == 0) {
2353 Vector<ResTable_config> configs;
2354 res.getConfigurations(&configs);
2355 const size_t N = configs.size();
2356 for (size_t i=0; i<N; i++) {
2357 printf("%s\n", configs[i].toString().string());
2358 }
2359 } else {
2360 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2361 goto bail;
2362 }
2363 }
2364
2365 result = NO_ERROR;
2366
2367bail:
Adam Lesinski10de3af12016-07-13 10:14:03 -07002368 if (SourcePos::hasErrors()) {
2369 SourcePos::printErrors(stderr);
2370 }
2371
Adam Lesinski282e1812014-01-23 18:17:42 -08002372 if (asset) {
2373 delete asset;
2374 }
2375 return (result != NO_ERROR);
2376}
2377
2378
2379/*
2380 * Handle the "add" command, which wants to add files to a new or
2381 * pre-existing archive.
2382 */
2383int doAdd(Bundle* bundle)
2384{
2385 ZipFile* zip = NULL;
2386 status_t result = UNKNOWN_ERROR;
2387 const char* zipFileName;
2388
2389 if (bundle->getUpdate()) {
2390 /* avoid confusion */
2391 fprintf(stderr, "ERROR: can't use '-u' with add\n");
2392 goto bail;
2393 }
2394
2395 if (bundle->getFileSpecCount() < 1) {
2396 fprintf(stderr, "ERROR: must specify zip file name\n");
2397 goto bail;
2398 }
2399 zipFileName = bundle->getFileSpecEntry(0);
2400
2401 if (bundle->getFileSpecCount() < 2) {
2402 fprintf(stderr, "NOTE: nothing to do\n");
2403 goto bail;
2404 }
2405
2406 zip = openReadWrite(zipFileName, true);
2407 if (zip == NULL) {
2408 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2409 goto bail;
2410 }
2411
2412 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2413 const char* fileName = bundle->getFileSpecEntry(i);
2414
2415 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
2416 printf(" '%s'... (from gzip)\n", fileName);
2417 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
2418 } else {
2419 if (bundle->getJunkPath()) {
2420 String8 storageName = String8(fileName).getPathLeaf();
Maurice Chu2675f762013-10-22 17:33:11 -07002421 printf(" '%s' as '%s'...\n", fileName,
2422 ResTable::normalizeForOutput(storageName.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08002423 result = zip->add(fileName, storageName.string(),
2424 bundle->getCompressionMethod(), NULL);
2425 } else {
2426 printf(" '%s'...\n", fileName);
2427 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2428 }
2429 }
2430 if (result != NO_ERROR) {
2431 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2432 if (result == NAME_NOT_FOUND) {
2433 fprintf(stderr, ": file not found\n");
2434 } else if (result == ALREADY_EXISTS) {
2435 fprintf(stderr, ": already exists in archive\n");
2436 } else {
2437 fprintf(stderr, "\n");
2438 }
2439 goto bail;
2440 }
2441 }
2442
2443 result = NO_ERROR;
2444
2445bail:
2446 delete zip;
2447 return (result != NO_ERROR);
2448}
2449
2450
2451/*
2452 * Delete files from an existing archive.
2453 */
2454int doRemove(Bundle* bundle)
2455{
2456 ZipFile* zip = NULL;
2457 status_t result = UNKNOWN_ERROR;
2458 const char* zipFileName;
2459
2460 if (bundle->getFileSpecCount() < 1) {
2461 fprintf(stderr, "ERROR: must specify zip file name\n");
2462 goto bail;
2463 }
2464 zipFileName = bundle->getFileSpecEntry(0);
2465
2466 if (bundle->getFileSpecCount() < 2) {
2467 fprintf(stderr, "NOTE: nothing to do\n");
2468 goto bail;
2469 }
2470
2471 zip = openReadWrite(zipFileName, false);
2472 if (zip == NULL) {
2473 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2474 zipFileName);
2475 goto bail;
2476 }
2477
2478 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2479 const char* fileName = bundle->getFileSpecEntry(i);
2480 ZipEntry* entry;
2481
2482 entry = zip->getEntryByName(fileName);
2483 if (entry == NULL) {
2484 printf(" '%s' NOT FOUND\n", fileName);
2485 continue;
2486 }
2487
2488 result = zip->remove(entry);
2489
2490 if (result != NO_ERROR) {
2491 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2492 bundle->getFileSpecEntry(i), zipFileName);
2493 goto bail;
2494 }
2495 }
2496
2497 /* update the archive */
2498 zip->flush();
2499
2500bail:
2501 delete zip;
2502 return (result != NO_ERROR);
2503}
2504
Adam Lesinski3921e872014-05-13 10:56:25 -07002505static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002506 const size_t numDirs = dir->getDirs().size();
2507 for (size_t i = 0; i < numDirs; i++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002508 bool ignore = ignoreConfig;
2509 const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2510 const char* dirStr = subDir->getLeaf().string();
2511 if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2512 ignore = true;
2513 }
2514 status_t err = addResourcesToBuilder(subDir, builder, ignore);
Adam Lesinskifab50872014-04-16 14:40:42 -07002515 if (err != NO_ERROR) {
2516 return err;
2517 }
2518 }
2519
2520 const size_t numFiles = dir->getFiles().size();
2521 for (size_t i = 0; i < numFiles; i++) {
2522 sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2523 const size_t numConfigs = gp->getFiles().size();
2524 for (size_t j = 0; j < numConfigs; j++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002525 status_t err = NO_ERROR;
Adam Lesinskic7614e52017-03-16 16:54:23 -07002526 if (ignoreConfig) {
Guang Zhu8c2df712017-03-21 03:53:43 +00002527 err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
Adam Lesinskic7614e52017-03-16 16:54:23 -07002528 } else {
Guang Zhu8c2df712017-03-21 03:53:43 +00002529 err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
Adam Lesinskic7614e52017-03-16 16:54:23 -07002530 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002531 if (err != NO_ERROR) {
2532 fprintf(stderr, "Failed to add %s (%s) to builder.\n",
Guang Zhu8c2df712017-03-21 03:53:43 +00002533 gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
Adam Lesinskifab50872014-04-16 14:40:42 -07002534 return err;
2535 }
2536 }
2537 }
2538 return NO_ERROR;
2539}
2540
2541static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2542 if (split->isBase()) {
2543 return original;
2544 }
2545
2546 String8 ext(original.getPathExtension());
2547 if (ext == String8(".apk")) {
2548 return String8::format("%s_%s%s",
2549 original.getBasePath().string(),
2550 split->getDirectorySafeName().string(),
2551 ext.string());
2552 }
2553
2554 return String8::format("%s_%s", original.string(),
2555 split->getDirectorySafeName().string());
2556}
Adam Lesinski282e1812014-01-23 18:17:42 -08002557
2558/*
2559 * Package up an asset directory and associated application files.
2560 */
2561int doPackage(Bundle* bundle)
2562{
2563 const char* outputAPKFile;
2564 int retVal = 1;
2565 status_t err;
2566 sp<AaptAssets> assets;
2567 int N;
2568 FILE* fp;
2569 String8 dependencyFile;
Adam Lesinskifab50872014-04-16 14:40:42 -07002570 sp<ApkBuilder> builder;
Adam Lesinski282e1812014-01-23 18:17:42 -08002571
Anton Krumina2ef5c02014-03-12 14:46:44 -07002572 // -c en_XA or/and ar_XB means do pseudolocalization
Adam Lesinskifab50872014-04-16 14:40:42 -07002573 sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2574 err = configFilter->parse(bundle->getConfigurations());
Adam Lesinski282e1812014-01-23 18:17:42 -08002575 if (err != NO_ERROR) {
2576 goto bail;
2577 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002578 if (configFilter->containsPseudo()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002579 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2580 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002581 if (configFilter->containsPseudoBidi()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002582 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
Adam Lesinski282e1812014-01-23 18:17:42 -08002583 }
2584
2585 N = bundle->getFileSpecCount();
2586 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
Adam Lesinski09384302014-01-22 16:07:42 -08002587 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002588 fprintf(stderr, "ERROR: no input files\n");
2589 goto bail;
2590 }
2591
2592 outputAPKFile = bundle->getOutputAPKFile();
2593
2594 // Make sure the filenames provided exist and are of the appropriate type.
2595 if (outputAPKFile) {
2596 FileType type;
2597 type = getFileType(outputAPKFile);
2598 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2599 fprintf(stderr,
2600 "ERROR: output file '%s' exists but is not regular file\n",
2601 outputAPKFile);
2602 goto bail;
2603 }
2604 }
2605
2606 // Load the assets.
2607 assets = new AaptAssets();
2608
2609 // Set up the resource gathering in assets if we're going to generate
2610 // dependency files. Every time we encounter a resource while slurping
2611 // the tree, we'll add it to these stores so we have full resource paths
2612 // to write to a dependency file.
2613 if (bundle->getGenDependencies()) {
2614 sp<FilePathStore> resPathStore = new FilePathStore;
2615 assets->setFullResPaths(resPathStore);
2616 sp<FilePathStore> assetPathStore = new FilePathStore;
2617 assets->setFullAssetPaths(assetPathStore);
2618 }
2619
2620 err = assets->slurpFromArgs(bundle);
2621 if (err < 0) {
2622 goto bail;
2623 }
2624
2625 if (bundle->getVerbose()) {
2626 assets->print(String8());
2627 }
2628
Adam Lesinskifab50872014-04-16 14:40:42 -07002629 // Create the ApkBuilder, which will collect the compiled files
2630 // to write to the final APK (or sets of APKs if we are building
2631 // a Split APK.
2632 builder = new ApkBuilder(configFilter);
2633
2634 // If we are generating a Split APK, find out which configurations to split on.
2635 if (bundle->getSplitConfigurations().size() > 0) {
2636 const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2637 const size_t numSplits = splitStrs.size();
2638 for (size_t i = 0; i < numSplits; i++) {
2639 std::set<ConfigDescription> configs;
2640 if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2641 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2642 goto bail;
2643 }
2644
2645 err = builder->createSplitForConfigs(configs);
2646 if (err != NO_ERROR) {
2647 goto bail;
2648 }
2649 }
2650 }
2651
Adam Lesinski282e1812014-01-23 18:17:42 -08002652 // If they asked for any fileAs that need to be compiled, do so.
2653 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002654 err = buildResources(bundle, assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002655 if (err != 0) {
2656 goto bail;
2657 }
2658 }
2659
2660 // At this point we've read everything and processed everything. From here
2661 // on out it's just writing output files.
2662 if (SourcePos::hasErrors()) {
2663 goto bail;
2664 }
2665
2666 // Update symbols with information about which ones are needed as Java symbols.
2667 assets->applyJavaSymbols();
2668 if (SourcePos::hasErrors()) {
2669 goto bail;
2670 }
2671
2672 // If we've been asked to generate a dependency file, do that here
2673 if (bundle->getGenDependencies()) {
2674 // If this is the packaging step, generate the dependency file next to
2675 // the output apk (e.g. bin/resources.ap_.d)
2676 if (outputAPKFile) {
2677 dependencyFile = String8(outputAPKFile);
2678 // Add the .d extension to the dependency file.
2679 dependencyFile.append(".d");
2680 } else {
2681 // Else if this is the R.java dependency generation step,
2682 // generate the dependency file in the R.java package subdirectory
2683 // e.g. gen/com/foo/app/R.java.d
2684 dependencyFile = String8(bundle->getRClassDir());
2685 dependencyFile.appendPath("R.java.d");
2686 }
2687 // Make sure we have a clean dependency file to start with
2688 fp = fopen(dependencyFile, "w");
2689 fclose(fp);
2690 }
2691
2692 // Write out R.java constants
2693 if (!assets->havePrivateSymbols()) {
2694 if (bundle->getCustomPackage() == NULL) {
2695 // Write the R.java file into the appropriate class directory
2696 // e.g. gen/com/foo/app/R.java
Adam Lesinski1e4663852014-08-15 14:47:28 -07002697 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002698 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002699 } else {
2700 const String8 customPkg(bundle->getCustomPackage());
Adam Lesinski1e4663852014-08-15 14:47:28 -07002701 err = writeResourceSymbols(bundle, assets, customPkg, true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002702 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002703 }
2704 if (err < 0) {
2705 goto bail;
2706 }
2707 // If we have library files, we're going to write our R.java file into
2708 // the appropriate class directory for those libraries as well.
2709 // e.g. gen/com/foo/app/lib/R.java
2710 if (bundle->getExtraPackages() != NULL) {
2711 // Split on colon
2712 String8 libs(bundle->getExtraPackages());
2713 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2714 while (packageString != NULL) {
2715 // Write the R.java file out with the correct package name
Marcin Kosiba0f3a5a62014-09-11 13:48:48 +01002716 err = writeResourceSymbols(bundle, assets, String8(packageString), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002717 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002718 if (err < 0) {
2719 goto bail;
2720 }
2721 packageString = strtok(NULL, ":");
2722 }
2723 libs.unlockBuffer();
2724 }
2725 } else {
Adam Lesinski1e4663852014-08-15 14:47:28 -07002726 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002727 if (err < 0) {
2728 goto bail;
2729 }
Adam Lesinski1e4663852014-08-15 14:47:28 -07002730 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002731 if (err < 0) {
2732 goto bail;
2733 }
2734 }
2735
2736 // Write out the ProGuard file
2737 err = writeProguardFile(bundle, assets);
2738 if (err < 0) {
2739 goto bail;
2740 }
2741
Rohit Agrawal86229cb2016-04-21 16:29:58 -07002742 // Write out the Main Dex ProGuard file
2743 err = writeMainDexProguardFile(bundle, assets);
2744 if (err < 0) {
2745 goto bail;
2746 }
2747
Adam Lesinski282e1812014-01-23 18:17:42 -08002748 // Write the apk
2749 if (outputAPKFile) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002750 // Gather all resources and add them to the APK Builder. The builder will then
2751 // figure out which Split they belong in.
2752 err = addResourcesToBuilder(assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002753 if (err != NO_ERROR) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002754 goto bail;
2755 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002756
2757 const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2758 const size_t numSplits = splits.size();
2759 for (size_t i = 0; i < numSplits; i++) {
2760 const sp<ApkSplit>& split = splits[i];
2761 String8 outputPath = buildApkName(String8(outputAPKFile), split);
2762 err = writeAPK(bundle, outputPath, split);
2763 if (err != NO_ERROR) {
2764 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2765 goto bail;
2766 }
2767 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002768 }
2769
2770 // If we've been asked to generate a dependency file, we need to finish up here.
2771 // the writeResourceSymbols and writeAPK functions have already written the target
2772 // half of the dependency file, now we need to write the prerequisites. (files that
2773 // the R.java file or .ap_ file depend on)
2774 if (bundle->getGenDependencies()) {
2775 // Now that writeResourceSymbols or writeAPK has taken care of writing
2776 // the targets to our dependency file, we'll write the prereqs
2777 fp = fopen(dependencyFile, "a+");
2778 fprintf(fp, " : ");
2779 bool includeRaw = (outputAPKFile != NULL);
2780 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2781 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2782 // and therefore was not added to our pathstores during slurping
2783 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2784 fclose(fp);
2785 }
2786
2787 retVal = 0;
2788bail:
2789 if (SourcePos::hasErrors()) {
2790 SourcePos::printErrors(stderr);
2791 }
2792 return retVal;
2793}
2794
2795/*
2796 * Do PNG Crunching
2797 * PRECONDITIONS
2798 * -S flag points to a source directory containing drawable* folders
2799 * -C flag points to destination directory. The folder structure in the
2800 * source directory will be mirrored to the destination (cache) directory
2801 *
2802 * POSTCONDITIONS
2803 * Destination directory will be updated to match the PNG files in
Maurice Chu2675f762013-10-22 17:33:11 -07002804 * the source directory.
Adam Lesinski282e1812014-01-23 18:17:42 -08002805 */
2806int doCrunch(Bundle* bundle)
2807{
2808 fprintf(stdout, "Crunching PNG Files in ");
2809 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2810 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2811
2812 updatePreProcessedCache(bundle);
2813
2814 return NO_ERROR;
2815}
2816
2817/*
2818 * Do PNG Crunching on a single flag
2819 * -i points to a single png file
2820 * -o points to a single png output file
2821 */
2822int doSingleCrunch(Bundle* bundle)
2823{
2824 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2825 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2826
2827 String8 input(bundle->getSingleCrunchInputFile());
2828 String8 output(bundle->getSingleCrunchOutputFile());
2829
2830 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2831 // we can't return the status_t as it gets truncate to the lower 8 bits.
2832 return 42;
2833 }
2834
2835 return NO_ERROR;
2836}
2837
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002838int runInDaemonMode(Bundle* bundle) {
2839 std::cout << "Ready" << std::endl;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002840 for (std::string cmd; std::getline(std::cin, cmd);) {
2841 if (cmd == "quit") {
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002842 return NO_ERROR;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002843 } else if (cmd == "s") {
2844 // Two argument crunch
2845 std::string inputFile, outputFile;
2846 std::getline(std::cin, inputFile);
2847 std::getline(std::cin, outputFile);
2848 bundle->setSingleCrunchInputFile(inputFile.c_str());
2849 bundle->setSingleCrunchOutputFile(outputFile.c_str());
2850 std::cout << "Crunching " << inputFile << std::endl;
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002851 if (doSingleCrunch(bundle) != NO_ERROR) {
2852 std::cout << "Error" << std::endl;
2853 }
2854 std::cout << "Done" << std::endl;
2855 } else {
2856 // in case of invalid command, just bail out.
2857 std::cerr << "Unknown command" << std::endl;
2858 return -1;
2859 }
2860 }
2861 return -1;
2862}
2863
Adam Lesinski282e1812014-01-23 18:17:42 -08002864char CONSOLE_DATA[2925] = {
2865 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2866 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
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, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2869 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2870 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2871 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2872 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2873 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2874 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2875 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2876 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2877 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2878 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2879 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2880 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2881 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2882 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2883 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2884 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2885 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2886 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2887 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2888 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2889 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2890 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2891 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2892 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2893 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2894 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2895 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2896 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2897 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2898 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2899 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2900 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2901 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2902 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2903 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2904 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2905 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2906 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2907 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2908 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2909 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2910 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2911 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2912 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2913 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2914 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2915 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2916 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2917 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2918 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2919 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2920 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2921 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2922 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2923 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2924 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2925 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2926 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2927 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2928 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2929 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2930 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2931 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2932 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2933 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2934 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2935 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2936 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2937 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2938 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2939 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2940 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2941 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2942 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2943 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2944 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2945 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2946 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2947 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2948 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2949 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2950 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2951 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2952 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2953 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2954 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2955 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2956 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2957 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2958 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2959 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2960 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2961 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2962 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2963 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2964 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2965 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2966 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2967 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2968 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2969 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2970 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2971 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2972 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2973 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2974 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2975 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2976 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2977 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2978 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2979 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2980 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2981 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2982 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2983 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2984 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2985 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2986 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2987 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2988 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2989 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2990 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2991 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2992 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2993 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2994 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2995 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2996 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2997 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2998 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2999 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
3000 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
3001 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3002 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3003 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
3004 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
3005 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
3006 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3007 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
3008 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3009 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
3010 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
3011 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
3012 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3013 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3014 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
3015 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
3016 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
3017 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
3018 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
3019 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3020 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3021 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
3022 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
3023 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3024 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3025 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3026 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3027 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
3028 };