blob: fecc7b3cbf3721d92e851e46c14d1cb68387276d [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;
Jackal Guo201a60a2021-08-31 12:37:30 +0800772 config.sdkVersion = SDK_CUR_DEVELOPMENT; // Very high.
Adam Lesinski282e1812014-01-23 18:17:42 -0800773 config.screenWidthDp = 320;
774 config.screenHeightDp = 480;
775 config.smallestScreenWidthDp = 320;
Adam Lesinskic2dea8d2014-08-04 16:40:41 -0700776 config.screenLayout |= ResTable_config::SCREENSIZE_NORMAL;
Adam Lesinski282e1812014-01-23 18:17:42 -0800777 assets.setConfiguration(config);
778
779 const ResTable& res = assets.getResources(false);
Dan Albert68001652014-09-09 09:51:01 -0700780 if (res.getError() != NO_ERROR) {
Adam Lesinski25e9d552014-05-19 15:01:43 -0700781 fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
Adam Lesinski63e646e2014-07-30 11:40:39 -0700782 return 1;
Adam Lesinski282e1812014-01-23 18:17:42 -0800783 }
784
Adam Lesinski694d0a72016-04-06 16:12:04 -0700785 // Source for AndroidManifest.xml
Adam Lesinski10de3af12016-07-13 10:14:03 -0700786 const String8 manifestFile("AndroidManifest.xml");
Adam Lesinski694d0a72016-04-06 16:12:04 -0700787
Adam Lesinski2cb761e2014-08-15 13:59:02 -0700788 // The dynamicRefTable can be null if there are no resources for this asset cookie.
789 // This fine.
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700790 auto noop_destructor = [](const DynamicRefTable* /*ref_table */) { };
791 auto dynamicRefTable = std::shared_ptr<const DynamicRefTable>(
792 res.getDynamicRefTableForCookie(assetsCookie), noop_destructor);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700793
794 Asset* asset = NULL;
795
Adam Lesinski282e1812014-01-23 18:17:42 -0800796 if (strcmp("resources", option) == 0) {
Elliott Hughesba3fe562015-08-12 14:49:53 -0700797#ifndef __ANDROID__
Adam Lesinski282e1812014-01-23 18:17:42 -0800798 res.print(bundle->getValues());
799#endif
800
801 } else if (strcmp("strings", option) == 0) {
802 const ResStringPool* pool = res.getTableStringBlock(0);
803 printStringPool(pool);
804
805 } else if (strcmp("xmltree", option) == 0) {
806 if (bundle->getFileSpecCount() < 3) {
807 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
808 goto bail;
809 }
810
811 for (int i=2; i<bundle->getFileSpecCount(); i++) {
812 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700813 ResXMLTree tree(dynamicRefTable);
814 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800815 if (asset == NULL) {
Adam Lesinskifcb5f7b2016-11-02 13:17:10 -0700816 fprintf(stderr, "ERROR: dump failed because resource %s not found\n", resname);
Adam Lesinski282e1812014-01-23 18:17:42 -0800817 goto bail;
818 }
819
820 if (tree.setTo(asset->getBuffer(true),
821 asset->getLength()) != NO_ERROR) {
822 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
823 goto bail;
824 }
825 tree.restart();
826 printXMLBlock(&tree);
827 tree.uninit();
828 delete asset;
829 asset = NULL;
830 }
831
832 } else if (strcmp("xmlstrings", option) == 0) {
833 if (bundle->getFileSpecCount() < 3) {
834 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
835 goto bail;
836 }
837
838 for (int i=2; i<bundle->getFileSpecCount(); i++) {
839 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700840 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800841 if (asset == NULL) {
842 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
843 goto bail;
844 }
845
Adam Lesinski63e646e2014-07-30 11:40:39 -0700846 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800847 if (tree.setTo(asset->getBuffer(true),
848 asset->getLength()) != NO_ERROR) {
849 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
850 goto bail;
851 }
852 printStringPool(&tree.getStrings());
853 delete asset;
854 asset = NULL;
855 }
856
857 } else {
Adam Lesinski63e646e2014-07-30 11:40:39 -0700858 asset = assets.openNonAsset(assetsCookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800859 if (asset == NULL) {
860 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
861 goto bail;
862 }
863
Adam Lesinski63e646e2014-07-30 11:40:39 -0700864 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800865 if (tree.setTo(asset->getBuffer(true),
866 asset->getLength()) != NO_ERROR) {
867 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
868 goto bail;
869 }
870 tree.restart();
871
872 if (strcmp("permissions", option) == 0) {
873 size_t len;
874 ResXMLTree::event_code_t code;
875 int depth = 0;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800876 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
877 code != ResXMLTree::BAD_DOCUMENT) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800878 if (code == ResXMLTree::END_TAG) {
879 depth--;
880 continue;
881 }
882 if (code != ResXMLTree::START_TAG) {
883 continue;
884 }
885 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700886 const char16_t* ctag16 = tree.getElementName(&len);
887 if (ctag16 == NULL) {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700888 SourcePos(manifestFile, tree.getLineNumber()).error(
889 "ERROR: failed to get XML element name (bad string pool)");
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700890 goto bail;
891 }
892 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800893 //printf("Depth %d tag %s\n", depth, tag.string());
894 if (depth == 1) {
895 if (tag != "manifest") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700896 SourcePos(manifestFile, tree.getLineNumber()).error(
897 "ERROR: manifest does not start with <manifest> tag");
Adam Lesinski282e1812014-01-23 18:17:42 -0800898 goto bail;
899 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700900 String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -0700901 printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800902 } else if (depth == 2) {
903 if (tag == "permission") {
904 String8 error;
905 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
906 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700907 SourcePos(manifestFile, tree.getLineNumber()).error(
908 "ERROR getting 'android:name': %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800909 goto bail;
910 }
911
912 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700913 SourcePos(manifestFile, tree.getLineNumber()).error(
914 "ERROR: missing 'android:name' for permission");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800915 goto bail;
916 }
917 printf("permission: %s\n",
918 ResTable::normalizeForOutput(name.string()).string());
919 } else if (tag == "uses-permission") {
920 String8 error;
921 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
922 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700923 SourcePos(manifestFile, tree.getLineNumber()).error(
924 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800925 goto bail;
926 }
927
928 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700929 SourcePos(manifestFile, tree.getLineNumber()).error(
930 "ERROR: missing 'android:name' for uses-permission");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800931 goto bail;
932 }
933 printUsesPermission(name,
934 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
935 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
936 } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
937 String8 error;
938 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
939 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700940 SourcePos(manifestFile, tree.getLineNumber()).error(
941 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800942 goto bail;
943 }
944
945 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700946 SourcePos(manifestFile, tree.getLineNumber()).error(
947 "ERROR: missing 'android:name' for uses-permission-sdk-23");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800948 goto bail;
949 }
950 printUsesPermissionSdk23(
951 name,
952 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
Adam Lesinski282e1812014-01-23 18:17:42 -0800953 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800954 }
955 }
956 } else if (strcmp("badging", option) == 0) {
957 Vector<String8> locales;
958 res.getLocales(&locales);
959
960 Vector<ResTable_config> configs;
961 res.getConfigurations(&configs);
962 SortedVector<int> densities;
963 const size_t NC = configs.size();
964 for (size_t i=0; i<NC; i++) {
965 int dens = configs[i].density;
966 if (dens == 0) {
967 dens = 160;
968 }
969 densities.add(dens);
970 }
971
Ryan Mitchell424db432021-05-03 11:42:52 -0700972 std::vector<ResXMLParser::ResXMLPosition> tagsToSkip;
973
Adam Lesinski282e1812014-01-23 18:17:42 -0800974 size_t len;
975 ResXMLTree::event_code_t code;
976 int depth = 0;
977 String8 error;
978 bool withinActivity = false;
979 bool isMainActivity = false;
980 bool isLauncherActivity = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800981 bool isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800982 bool isSearchable = false;
983 bool withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700984 bool withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700985 bool withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800986 bool withinReceiver = false;
987 bool withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700988 bool withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800989 bool withinIntentFilter = false;
990 bool hasMainActivity = false;
991 bool hasOtherActivities = false;
992 bool hasOtherReceivers = false;
993 bool hasOtherServices = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700994 bool hasIntentFilter = false;
995
Adam Lesinski282e1812014-01-23 18:17:42 -0800996 bool hasWallpaperService = false;
997 bool hasImeService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700998 bool hasAccessibilityService = false;
999 bool hasPrintService = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001000 bool hasWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001001 bool hasDeviceAdminReceiver = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001002 bool hasPaymentService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001003 bool hasDocumentsProvider = false;
1004 bool hasCameraActivity = false;
1005 bool hasCameraSecureActivity = false;
1006 bool hasLauncher = false;
1007 bool hasNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001008 bool hasDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001009
Adam Lesinski282e1812014-01-23 18:17:42 -08001010 bool actMainActivity = false;
1011 bool actWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001012 bool actDeviceAdminEnabled = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001013 bool actImeService = false;
1014 bool actWallpaperService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001015 bool actAccessibilityService = false;
1016 bool actPrintService = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001017 bool actHostApduService = false;
1018 bool actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001019 bool actDocumentsProvider = false;
1020 bool actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001021 bool actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001022 bool actCamera = false;
1023 bool actCameraSecure = false;
1024 bool catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001025 bool hasMetaHostPaymentCategory = false;
1026 bool hasMetaOffHostPaymentCategory = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001027
1028 // These permissions are required by services implementing services
1029 // the system binds to (IME, Accessibility, PrintServices, etc.)
1030 bool hasBindDeviceAdminPermission = false;
1031 bool hasBindInputMethodPermission = false;
1032 bool hasBindAccessibilityServicePermission = false;
1033 bool hasBindPrintServicePermission = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001034 bool hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001035 bool hasRequiredSafAttributes = false;
1036 bool hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001037 bool hasBindDreamServicePermission = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001038
1039 // These two implement the implicit permissions that are granted
1040 // to pre-1.6 applications.
1041 bool hasWriteExternalStoragePermission = false;
Adam Lesinski2386df22016-12-28 15:08:58 -05001042 int32_t writeExternalStoragePermissionMaxSdkVersion = -1;
Adam Lesinski282e1812014-01-23 18:17:42 -08001043 bool hasReadPhoneStatePermission = false;
1044
1045 // If an app requests write storage, they will also get read storage.
1046 bool hasReadExternalStoragePermission = false;
1047
1048 // Implement transition to read and write call log.
1049 bool hasReadContactsPermission = false;
1050 bool hasWriteContactsPermission = false;
1051 bool hasReadCallLogPermission = false;
1052 bool hasWriteCallLogPermission = false;
1053
Adam Lesinskie47fd122014-08-15 22:25:36 -07001054 // If an app declares itself as multiArch, we report the
1055 // native libraries differently.
1056 bool hasMultiArch = false;
1057
Adam Lesinski282e1812014-01-23 18:17:42 -08001058 // This next group of variables is used to implement a group of
1059 // backward-compatibility heuristics necessitated by the addition of
1060 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
1061 // heuristic is "if an app requests a permission but doesn't explicitly
1062 // request the corresponding <uses-feature>, presume it's there anyway".
Adam Lesinski2c72b682014-06-24 09:56:01 -07001063
Adam Lesinski282e1812014-01-23 18:17:42 -08001064 // 2.2 also added some other features that apps can request, but that
1065 // have no corresponding permission, so we cannot implement any
1066 // back-compatibility heuristic for them. The below are thus unnecessary
1067 // (but are retained here for documentary purposes.)
1068 //bool specCompassFeature = false;
1069 //bool specAccelerometerFeature = false;
1070 //bool specProximityFeature = false;
1071 //bool specAmbientLightFeature = false;
1072 //bool specLiveWallpaperFeature = false;
1073
1074 int targetSdk = 0;
1075 int smallScreen = 1;
1076 int normalScreen = 1;
1077 int largeScreen = 1;
1078 int xlargeScreen = 1;
1079 int anyDensity = 1;
1080 int requiresSmallestWidthDp = 0;
1081 int compatibleWidthLimitDp = 0;
1082 int largestWidthLimitDp = 0;
1083 String8 pkg;
1084 String8 activityName;
1085 String8 activityLabel;
1086 String8 activityIcon;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001087 String8 activityBanner;
Adam Lesinski282e1812014-01-23 18:17:42 -08001088 String8 receiverName;
1089 String8 serviceName;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001090 Vector<String8> supportedInput;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001091
1092 FeatureGroup commonFeatures;
1093 Vector<FeatureGroup> featureGroups;
1094 KeyedVector<String8, ImpliedFeature> impliedFeatures;
1095
Ryan Mitchell424db432021-05-03 11:42:52 -07001096 {
1097 int curDepth = 0;
1098 ResXMLParser::ResXMLPosition initialPos;
1099 tree.getPosition(&initialPos);
1100
1101 // Find all of the "uses-sdk" tags within the "manifest" tag.
1102 std::vector<ResXMLParser::ResXMLPosition> usesSdkTagPositions;
1103 ResXMLParser::ResXMLPosition curPos;
1104 while ((code = tree.next()) != ResXMLTree::END_DOCUMENT &&
1105 code != ResXMLTree::BAD_DOCUMENT) {
1106 if (code == ResXMLTree::END_TAG) {
1107 curDepth--;
1108 continue;
1109 }
1110 if (code == ResXMLTree::START_TAG) {
1111 curDepth++;
1112 }
1113 const char16_t* ctag16 = tree.getElementName(&len);
1114 if (ctag16 == NULL || String8(ctag16) != "uses-sdk" || curDepth != 2) {
1115 continue;
1116 }
1117
1118 tree.getPosition(&curPos);
1119 usesSdkTagPositions.emplace_back(curPos);
1120 }
1121
1122 // Skip all "uses-sdk" tags besides the very last tag. The android runtime only uses
1123 // the attribute values from the last defined tag.
Ryan Mitchell957168e2021-05-10 11:46:49 -07001124 for (size_t i = 1; i < usesSdkTagPositions.size(); i++) {
1125 tagsToSkip.emplace_back(usesSdkTagPositions[i - 1]);
Ryan Mitchell424db432021-05-03 11:42:52 -07001126 }
1127
1128 // Reset the position before parsing.
1129 tree.setPosition(initialPos);
1130 }
1131
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001132 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
1133 code != ResXMLTree::BAD_DOCUMENT) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001134 if (code == ResXMLTree::END_TAG) {
1135 depth--;
1136 if (depth < 2) {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001137 if (withinSupportsInput && !supportedInput.isEmpty()) {
1138 printf("supports-input: '");
1139 const size_t N = supportedInput.size();
1140 for (size_t i=0; i<N; i++) {
Maurice Chu2675f762013-10-22 17:33:11 -07001141 printf("%s", ResTable::normalizeForOutput(
1142 supportedInput[i].string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001143 if (i != N - 1) {
1144 printf("' '");
1145 } else {
1146 printf("'\n");
1147 }
1148 }
1149 supportedInput.clear();
1150 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001151 withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001152 withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001153 withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001154 } else if (depth < 3) {
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001155 if (withinActivity && isMainActivity) {
Maurice Chu2675f762013-10-22 17:33:11 -07001156 String8 aName(getComponentName(pkg, activityName));
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001157 if (isLauncherActivity) {
1158 printf("launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001159 if (aName.length() > 0) {
1160 printf(" name='%s' ",
1161 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001162 }
1163 printf(" label='%s' icon='%s'\n",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001164 ResTable::normalizeForOutput(activityLabel.string())
1165 .string(),
1166 ResTable::normalizeForOutput(activityIcon.string())
1167 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001168 }
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001169 if (isLeanbackLauncherActivity) {
1170 printf("leanback-launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001171 if (aName.length() > 0) {
1172 printf(" name='%s' ",
1173 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001174 }
1175 printf(" label='%s' icon='%s' banner='%s'\n",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001176 ResTable::normalizeForOutput(activityLabel.string())
1177 .string(),
1178 ResTable::normalizeForOutput(activityIcon.string())
1179 .string(),
1180 ResTable::normalizeForOutput(activityBanner.string())
1181 .string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001182 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001183 }
1184 if (!hasIntentFilter) {
1185 hasOtherActivities |= withinActivity;
1186 hasOtherReceivers |= withinReceiver;
1187 hasOtherServices |= withinService;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001188 } else {
1189 if (withinService) {
1190 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
1191 hasBindNfcServicePermission);
1192 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
1193 hasBindNfcServicePermission);
1194 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001195 }
1196 withinActivity = false;
1197 withinService = false;
1198 withinReceiver = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001199 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001200 hasIntentFilter = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001201 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001202 } else if (depth < 4) {
1203 if (withinIntentFilter) {
1204 if (withinActivity) {
1205 hasMainActivity |= actMainActivity;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001206 hasLauncher |= catLauncher;
1207 hasCameraActivity |= actCamera;
1208 hasCameraSecureActivity |= actCameraSecure;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001209 hasOtherActivities |=
1210 !actMainActivity && !actCamera && !actCameraSecure;
Adam Lesinski282e1812014-01-23 18:17:42 -08001211 } else if (withinReceiver) {
1212 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001213 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
1214 hasBindDeviceAdminPermission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001215 hasOtherReceivers |=
1216 (!actWidgetReceivers && !actDeviceAdminEnabled);
Adam Lesinski282e1812014-01-23 18:17:42 -08001217 } else if (withinService) {
1218 hasImeService |= actImeService;
1219 hasWallpaperService |= actWallpaperService;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001220 hasAccessibilityService |= (actAccessibilityService &&
1221 hasBindAccessibilityServicePermission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001222 hasPrintService |=
1223 (actPrintService && hasBindPrintServicePermission);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001224 hasNotificationListenerService |= actNotificationListenerService &&
1225 hasBindNotificationListenerServicePermission;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001226 hasDreamService |= actDreamService && hasBindDreamServicePermission;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001227 hasOtherServices |= (!actImeService && !actWallpaperService &&
Adam Lesinski94fc9122013-09-30 17:16:09 -07001228 !actAccessibilityService && !actPrintService &&
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001229 !actHostApduService && !actOffHostApduService &&
1230 !actNotificationListenerService);
1231 } else if (withinProvider) {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001232 hasDocumentsProvider |=
1233 actDocumentsProvider && hasRequiredSafAttributes;
Adam Lesinski282e1812014-01-23 18:17:42 -08001234 }
1235 }
1236 withinIntentFilter = false;
1237 }
1238 continue;
1239 }
1240 if (code != ResXMLTree::START_TAG) {
1241 continue;
1242 }
Ryan Mitchell424db432021-05-03 11:42:52 -07001243
Adam Lesinski282e1812014-01-23 18:17:42 -08001244 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001245
Ryan Mitchell424db432021-05-03 11:42:52 -07001246 // If this tag should be skipped, skip to the end of this tag.
1247 ResXMLParser::ResXMLPosition curPos;
1248 tree.getPosition(&curPos);
1249 if (std::find(tagsToSkip.begin(), tagsToSkip.end(), curPos) != tagsToSkip.end()) {
1250 const int breakDepth = depth - 1;
1251 while ((code = tree.next()) != ResXMLTree::END_DOCUMENT &&
1252 code != ResXMLTree::BAD_DOCUMENT) {
1253 if (code == ResXMLTree::END_TAG && --depth == breakDepth) {
1254 break;
1255 } else if (code == ResXMLTree::START_TAG) {
1256 depth++;
1257 }
1258 }
1259 continue;
1260 }
1261
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001262 const char16_t* ctag16 = tree.getElementName(&len);
1263 if (ctag16 == NULL) {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001264 SourcePos(manifestFile, tree.getLineNumber()).error(
1265 "ERROR: failed to get XML element name (bad string pool)");
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001266 goto bail;
1267 }
1268 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -08001269 //printf("Depth %d, %s\n", depth, tag.string());
1270 if (depth == 1) {
1271 if (tag != "manifest") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001272 SourcePos(manifestFile, tree.getLineNumber()).error(
1273 "ERROR: manifest does not start with <manifest> tag");
Adam Lesinski282e1812014-01-23 18:17:42 -08001274 goto bail;
1275 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001276 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -07001277 printf("package: name='%s' ",
1278 ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001279 int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR,
1280 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001281 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001282 SourcePos(manifestFile, tree.getLineNumber()).error(
1283 "ERROR getting 'android:versionCode' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001284 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001285 goto bail;
1286 }
1287 if (versionCode > 0) {
1288 printf("versionCode='%d' ", versionCode);
1289 } else {
1290 printf("versionCode='' ");
1291 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001292 String8 versionName = AaptXml::getResolvedAttribute(res, tree,
1293 VERSION_NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001294 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001295 SourcePos(manifestFile, tree.getLineNumber()).error(
1296 "ERROR getting 'android:versionName' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001297 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001298 goto bail;
1299 }
Adam Lesinski25d35a92014-08-11 09:41:56 -07001300 printf("versionName='%s'",
Maurice Chu2675f762013-10-22 17:33:11 -07001301 ResTable::normalizeForOutput(versionName.string()).string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001302
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001303 String8 splitName = AaptXml::getAttribute(tree, NULL, "split");
Adam Lesinski25d35a92014-08-11 09:41:56 -07001304 if (!splitName.isEmpty()) {
1305 printf(" split='%s'", ResTable::normalizeForOutput(
1306 splitName.string()).string());
1307 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001308
Jackal Guo201a60a2021-08-31 12:37:30 +08001309 // For 'platformBuildVersionName', using both string and int type as a fallback
1310 // since it may be the code name of Android or the API level.
Alan Viverette11be9312017-11-09 15:41:44 -05001311 String8 platformBuildVersionName = AaptXml::getAttribute(tree, NULL,
Adam Lesinski5283fab2014-08-29 11:23:55 -07001312 "platformBuildVersionName");
Jackal Guo201a60a2021-08-31 12:37:30 +08001313 int32_t platformBuildVersionNameInt =
1314 AaptXml::getIntegerAttribute(tree, NULL, "platformBuildVersionName", 0,
1315 NULL);
Alan Viverette11be9312017-11-09 15:41:44 -05001316 if (platformBuildVersionName != "") {
1317 printf(" platformBuildVersionName='%s'", platformBuildVersionName.string());
Jackal Guo201a60a2021-08-31 12:37:30 +08001318 } else if (platformBuildVersionNameInt > 0) {
1319 printf(" platformBuildVersionName='%d'", platformBuildVersionNameInt);
Alan Viverette11be9312017-11-09 15:41:44 -05001320 }
1321
Jackal Guo201a60a2021-08-31 12:37:30 +08001322 // For 'platformBuildVersionCode', using both string and int type as a fallback
1323 // since it may be the code name of Android or the API level.
Alan Viverette11be9312017-11-09 15:41:44 -05001324 String8 platformBuildVersionCode = AaptXml::getAttribute(tree, NULL,
1325 "platformBuildVersionCode");
Jackal Guo201a60a2021-08-31 12:37:30 +08001326 int32_t platformBuildVersionCodeInt =
1327 AaptXml::getIntegerAttribute(tree, NULL, "platformBuildVersionCode", 0,
1328 NULL);
Alan Viverette11be9312017-11-09 15:41:44 -05001329 if (platformBuildVersionCode != "") {
1330 printf(" platformBuildVersionCode='%s'", platformBuildVersionCode.string());
Jackal Guo201a60a2021-08-31 12:37:30 +08001331 } else if (platformBuildVersionCodeInt > 0) {
1332 printf(" platformBuildVersionCode='%d'", platformBuildVersionCodeInt);
Alan Viverette11be9312017-11-09 15:41:44 -05001333 }
1334
1335 int32_t compileSdkVersion = AaptXml::getIntegerAttribute(tree,
1336 COMPILE_SDK_VERSION_ATTR, &error);
1337 if (error != "") {
1338 SourcePos(manifestFile, tree.getLineNumber()).error(
1339 "ERROR getting 'android:compileSdkVersion' attribute: %s",
1340 error.string());
1341 goto bail;
1342 }
1343 if (compileSdkVersion > 0) {
1344 printf(" compileSdkVersion='%d'", compileSdkVersion);
1345 }
1346
1347 String8 compileSdkVersionCodename = AaptXml::getResolvedAttribute(res, tree,
1348 COMPILE_SDK_VERSION_CODENAME_ATTR, &error);
1349 if (compileSdkVersionCodename != "") {
1350 printf(" compileSdkVersionCodename='%s'", ResTable::normalizeForOutput(
1351 compileSdkVersionCodename.string()).string());
1352 }
1353
Adam Lesinski25d35a92014-08-11 09:41:56 -07001354 printf("\n");
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001355
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001356 int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
1357 INSTALL_LOCATION_ATTR, &error);
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001358 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001359 SourcePos(manifestFile, tree.getLineNumber()).error(
1360 "ERROR getting 'android:installLocation' attribute: %s",
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001361 error.string());
1362 goto bail;
1363 }
1364
1365 if (installLocation >= 0) {
1366 printf("install-location:'");
1367 switch (installLocation) {
1368 case 0:
1369 printf("auto");
1370 break;
1371 case 1:
1372 printf("internalOnly");
1373 break;
1374 case 2:
1375 printf("preferExternal");
1376 break;
1377 default:
1378 fprintf(stderr, "Invalid installLocation %d\n", installLocation);
1379 goto bail;
1380 }
1381 printf("'\n");
1382 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001383 } else if (depth == 2) {
1384 withinApplication = false;
1385 if (tag == "application") {
1386 withinApplication = true;
1387
1388 String8 label;
1389 const size_t NL = locales.size();
1390 for (size_t i=0; i<NL; i++) {
1391 const char* localeStr = locales[i].string();
Adam Lesinskia77685f2016-10-03 16:26:28 -07001392 assets.setConfiguration(config, localeStr != NULL ? localeStr : "");
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001393 String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1394 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001395 if (llabel != "") {
1396 if (localeStr == NULL || strlen(localeStr) == 0) {
1397 label = llabel;
Maurice Chu2675f762013-10-22 17:33:11 -07001398 printf("application-label:'%s'\n",
1399 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001400 } else {
1401 if (label == "") {
1402 label = llabel;
1403 }
1404 printf("application-label-%s:'%s'\n", localeStr,
Maurice Chu2675f762013-10-22 17:33:11 -07001405 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001406 }
1407 }
1408 }
1409
1410 ResTable_config tmpConfig = config;
1411 const size_t ND = densities.size();
1412 for (size_t i=0; i<ND; i++) {
1413 tmpConfig.density = densities[i];
1414 assets.setConfiguration(tmpConfig);
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001415 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1416 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001417 if (icon != "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001418 printf("application-icon-%d:'%s'\n", densities[i],
1419 ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001420 }
1421 }
1422 assets.setConfiguration(config);
1423
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001424 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001425 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001426 SourcePos(manifestFile, tree.getLineNumber()).error(
1427 "ERROR getting 'android:icon' attribute: %s", error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001428 goto bail;
1429 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001430 int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0,
1431 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001432 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001433 SourcePos(manifestFile, tree.getLineNumber()).error(
1434 "ERROR getting 'android:testOnly' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001435 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001436 goto bail;
1437 }
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001438
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001439 String8 banner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1440 &error);
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001441 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001442 SourcePos(manifestFile, tree.getLineNumber()).error(
1443 "ERROR getting 'android:banner' attribute: %s", error.string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001444 goto bail;
1445 }
Maurice Chu2675f762013-10-22 17:33:11 -07001446 printf("application: label='%s' ",
1447 ResTable::normalizeForOutput(label.string()).string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001448 printf("icon='%s'", ResTable::normalizeForOutput(icon.string()).string());
1449 if (banner != "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001450 printf(" banner='%s'",
1451 ResTable::normalizeForOutput(banner.string()).string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001452 }
1453 printf("\n");
Adam Lesinski282e1812014-01-23 18:17:42 -08001454 if (testOnly != 0) {
1455 printf("testOnly='%d'\n", testOnly);
1456 }
1457
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001458 int32_t isGame = AaptXml::getResolvedIntegerAttribute(res, tree,
1459 ISGAME_ATTR, 0, &error);
1460 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001461 SourcePos(manifestFile, tree.getLineNumber()).error(
1462 "ERROR getting 'android:isGame' attribute: %s", error.string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001463 goto bail;
1464 }
1465 if (isGame != 0) {
1466 printf("application-isGame\n");
1467 }
1468
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001469 int32_t debuggable = AaptXml::getResolvedIntegerAttribute(res, tree,
1470 DEBUGGABLE_ATTR, 0, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001471 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001472 SourcePos(manifestFile, tree.getLineNumber()).error(
1473 "ERROR getting 'android:debuggable' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001474 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001475 goto bail;
1476 }
1477 if (debuggable != 0) {
1478 printf("application-debuggable\n");
1479 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07001480
1481 // We must search by name because the multiArch flag hasn't been API
1482 // frozen yet.
1483 int32_t multiArchIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
1484 "multiArch");
1485 if (multiArchIndex >= 0) {
1486 Res_value value;
1487 if (tree.getAttributeValue(multiArchIndex, &value) != NO_ERROR) {
1488 if (value.dataType >= Res_value::TYPE_FIRST_INT &&
1489 value.dataType <= Res_value::TYPE_LAST_INT) {
1490 hasMultiArch = value.data;
1491 }
1492 }
1493 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001494 } else if (tag == "uses-sdk") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001495 int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR,
1496 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001497 if (error != "") {
1498 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001499 String8 name = AaptXml::getResolvedAttribute(res, tree,
1500 MIN_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001501 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001502 SourcePos(manifestFile, tree.getLineNumber()).error(
1503 "ERROR getting 'android:minSdkVersion' attribute: %s",
Adam Lesinski282e1812014-01-23 18:17:42 -08001504 error.string());
1505 goto bail;
1506 }
Jackal Guo201a60a2021-08-31 12:37:30 +08001507 if (name == "Donut") targetSdk = SDK_DONUT;
Maurice Chu2675f762013-10-22 17:33:11 -07001508 printf("sdkVersion:'%s'\n",
1509 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001510 } else if (code != -1) {
1511 targetSdk = code;
1512 printf("sdkVersion:'%d'\n", code);
1513 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001514 code = AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -08001515 if (code != -1) {
1516 printf("maxSdkVersion:'%d'\n", code);
1517 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001518 code = AaptXml::getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001519 if (error != "") {
1520 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001521 String8 name = AaptXml::getResolvedAttribute(res, tree,
1522 TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001523 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001524 SourcePos(manifestFile, tree.getLineNumber()).error(
1525 "ERROR getting 'android:targetSdkVersion' attribute: %s",
Adam Lesinski282e1812014-01-23 18:17:42 -08001526 error.string());
1527 goto bail;
1528 }
Jackal Guo201a60a2021-08-31 12:37:30 +08001529 if (name == "Donut" && targetSdk < SDK_DONUT) {
1530 targetSdk = SDK_DONUT;
1531 } else if (name != "" && targetSdk == 0) {
1532 // Bump to current development version
1533 targetSdk = SDK_CUR_DEVELOPMENT;
1534 }
Maurice Chu2675f762013-10-22 17:33:11 -07001535 printf("targetSdkVersion:'%s'\n",
1536 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001537 } else if (code != -1) {
1538 if (targetSdk < code) {
1539 targetSdk = code;
1540 }
1541 printf("targetSdkVersion:'%d'\n", code);
1542 }
1543 } else if (tag == "uses-configuration") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001544 int32_t reqTouchScreen = AaptXml::getIntegerAttribute(tree,
1545 REQ_TOUCH_SCREEN_ATTR, 0);
1546 int32_t reqKeyboardType = AaptXml::getIntegerAttribute(tree,
1547 REQ_KEYBOARD_TYPE_ATTR, 0);
1548 int32_t reqHardKeyboard = AaptXml::getIntegerAttribute(tree,
1549 REQ_HARD_KEYBOARD_ATTR, 0);
1550 int32_t reqNavigation = AaptXml::getIntegerAttribute(tree,
1551 REQ_NAVIGATION_ATTR, 0);
1552 int32_t reqFiveWayNav = AaptXml::getIntegerAttribute(tree,
1553 REQ_FIVE_WAY_NAV_ATTR, 0);
Adam Lesinski282e1812014-01-23 18:17:42 -08001554 printf("uses-configuration:");
1555 if (reqTouchScreen != 0) {
1556 printf(" reqTouchScreen='%d'", reqTouchScreen);
1557 }
1558 if (reqKeyboardType != 0) {
1559 printf(" reqKeyboardType='%d'", reqKeyboardType);
1560 }
1561 if (reqHardKeyboard != 0) {
1562 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1563 }
1564 if (reqNavigation != 0) {
1565 printf(" reqNavigation='%d'", reqNavigation);
1566 }
1567 if (reqFiveWayNav != 0) {
1568 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1569 }
1570 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001571 } else if (tag == "supports-input") {
1572 withinSupportsInput = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001573 } else if (tag == "supports-screens") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001574 smallScreen = AaptXml::getIntegerAttribute(tree,
1575 SMALL_SCREEN_ATTR, 1);
1576 normalScreen = AaptXml::getIntegerAttribute(tree,
1577 NORMAL_SCREEN_ATTR, 1);
1578 largeScreen = AaptXml::getIntegerAttribute(tree,
1579 LARGE_SCREEN_ATTR, 1);
1580 xlargeScreen = AaptXml::getIntegerAttribute(tree,
1581 XLARGE_SCREEN_ATTR, 1);
1582 anyDensity = AaptXml::getIntegerAttribute(tree,
1583 ANY_DENSITY_ATTR, 1);
1584 requiresSmallestWidthDp = AaptXml::getIntegerAttribute(tree,
1585 REQUIRES_SMALLEST_WIDTH_DP_ATTR, 0);
1586 compatibleWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1587 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, 0);
1588 largestWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1589 LARGEST_WIDTH_LIMIT_DP_ATTR, 0);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001590 } else if (tag == "feature-group") {
1591 withinFeatureGroup = true;
1592 FeatureGroup group;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001593 group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001594 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001595 SourcePos(manifestFile, tree.getLineNumber()).error(
1596 "ERROR getting 'android:label' attribute: %s", error.string());
Adam Lesinski2c72b682014-06-24 09:56:01 -07001597 goto bail;
1598 }
1599 featureGroups.add(group);
1600
Adam Lesinski282e1812014-01-23 18:17:42 -08001601 } else if (tag == "uses-feature") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001602 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001603 if (name != "" && error == "") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001604 const char* androidSchema =
1605 "http://schemas.android.com/apk/res/android";
Adam Lesinski282e1812014-01-23 18:17:42 -08001606
Adam Lesinski694d0a72016-04-06 16:12:04 -07001607 int32_t req = AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1,
1608 &error);
1609 if (error != "") {
1610 SourcePos(manifestFile, tree.getLineNumber()).error(
1611 "failed to read attribute 'android:required': %s",
1612 error.string());
1613 goto bail;
1614 }
1615
1616 int32_t version = AaptXml::getIntegerAttribute(tree, androidSchema,
1617 "version", 0, &error);
1618 if (error != "") {
1619 SourcePos(manifestFile, tree.getLineNumber()).error(
1620 "failed to read attribute 'android:version': %s",
1621 error.string());
1622 goto bail;
1623 }
1624
1625 commonFeatures.features.add(name, Feature(req != 0, version));
Adam Lesinski2c72b682014-06-24 09:56:01 -07001626 if (req) {
1627 addParentFeatures(&commonFeatures, name);
Adam Lesinski282e1812014-01-23 18:17:42 -08001628 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001629 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001630 int vers = AaptXml::getIntegerAttribute(tree,
Adam Lesinski282e1812014-01-23 18:17:42 -08001631 GL_ES_VERSION_ATTR, &error);
1632 if (error == "") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001633 if (vers > commonFeatures.openGLESVersion) {
1634 commonFeatures.openGLESVersion = vers;
1635 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001636 }
1637 }
1638 } else if (tag == "uses-permission") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001639 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001640 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001641 SourcePos(manifestFile, tree.getLineNumber()).error(
1642 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001643 goto bail;
1644 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001645
1646 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001647 SourcePos(manifestFile, tree.getLineNumber()).error(
1648 "ERROR: missing 'android:name' for uses-permission");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001649 goto bail;
1650 }
1651
1652 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, false);
1653
Adam Lesinski2386df22016-12-28 15:08:58 -05001654 const int32_t maxSdkVersion =
1655 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, -1);
Dianne Hackborncd154e92017-02-28 17:37:35 -08001656 const String8 requiredFeature = AaptXml::getAttribute(tree,
1657 REQUIRED_FEATURE_ATTR, &error);
1658 const String8 requiredNotFeature = AaptXml::getAttribute(tree,
1659 REQUIRED_NOT_FEATURE_ATTR, &error);
Adam Lesinski2386df22016-12-28 15:08:58 -05001660
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001661 if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1662 hasWriteExternalStoragePermission = true;
Adam Lesinski2386df22016-12-28 15:08:58 -05001663 writeExternalStoragePermissionMaxSdkVersion = maxSdkVersion;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001664 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1665 hasReadExternalStoragePermission = true;
1666 } else if (name == "android.permission.READ_PHONE_STATE") {
1667 hasReadPhoneStatePermission = true;
1668 } else if (name == "android.permission.READ_CONTACTS") {
1669 hasReadContactsPermission = true;
1670 } else if (name == "android.permission.WRITE_CONTACTS") {
1671 hasWriteContactsPermission = true;
1672 } else if (name == "android.permission.READ_CALL_LOG") {
1673 hasReadCallLogPermission = true;
1674 } else if (name == "android.permission.WRITE_CALL_LOG") {
1675 hasWriteCallLogPermission = true;
1676 }
1677
1678 printUsesPermission(name,
1679 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
Dianne Hackborncd154e92017-02-28 17:37:35 -08001680 maxSdkVersion, requiredFeature, requiredNotFeature);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001681
1682 } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
1683 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1684 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001685 SourcePos(manifestFile, tree.getLineNumber()).error(
1686 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001687 goto bail;
1688 }
1689
1690 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001691 SourcePos(manifestFile, tree.getLineNumber()).error(
1692 "ERROR: missing 'android:name' for uses-permission-sdk-23");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001693 goto bail;
1694 }
1695
1696 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, true);
1697
1698 printUsesPermissionSdk23(
1699 name, AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
1700
Adam Lesinski282e1812014-01-23 18:17:42 -08001701 } else if (tag == "uses-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001702 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001703 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001704 printf("uses-package:'%s'\n",
1705 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001706 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001707 SourcePos(manifestFile, tree.getLineNumber()).error(
1708 "ERROR getting 'android:name' attribute: %s", error.string());
1709 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001710 }
1711 } else if (tag == "original-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001712 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001713 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001714 printf("original-package:'%s'\n",
1715 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001716 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001717 SourcePos(manifestFile, tree.getLineNumber()).error(
1718 "ERROR getting 'android:name' attribute: %s", error.string());
1719 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001720 }
1721 } else if (tag == "supports-gl-texture") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001722 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001723 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001724 printf("supports-gl-texture:'%s'\n",
1725 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001726 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001727 SourcePos(manifestFile, tree.getLineNumber()).error(
1728 "ERROR getting 'android:name' attribute: %s", error.string());
1729 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001730 }
1731 } else if (tag == "compatible-screens") {
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001732 printCompatibleScreens(tree, &error);
1733 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001734 SourcePos(manifestFile, tree.getLineNumber()).error(
1735 "ERROR getting compatible screens: %s", error.string());
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001736 goto bail;
1737 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001738 depth--;
1739 } else if (tag == "package-verifier") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001740 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001741 if (name != "" && error == "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001742 String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR,
1743 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001744 if (publicKey != "" && error == "") {
1745 printf("package-verifier: name='%s' publicKey='%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001746 ResTable::normalizeForOutput(name.string()).string(),
1747 ResTable::normalizeForOutput(publicKey.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001748 }
1749 }
1750 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001751 } else if (depth == 3) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001752 withinActivity = false;
1753 withinReceiver = false;
1754 withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001755 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001756 hasIntentFilter = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001757 hasMetaHostPaymentCategory = false;
1758 hasMetaOffHostPaymentCategory = false;
1759 hasBindDeviceAdminPermission = false;
1760 hasBindInputMethodPermission = false;
1761 hasBindAccessibilityServicePermission = false;
1762 hasBindPrintServicePermission = false;
1763 hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001764 hasRequiredSafAttributes = false;
1765 hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001766 hasBindDreamServicePermission = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001767 if (withinApplication) {
1768 if(tag == "activity") {
1769 withinActivity = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001770 activityName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001771 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001772 SourcePos(manifestFile, tree.getLineNumber()).error(
1773 "ERROR getting 'android:name' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001774 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001775 goto bail;
1776 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001777
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001778 activityLabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1779 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001780 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001781 SourcePos(manifestFile, tree.getLineNumber()).error(
1782 "ERROR getting 'android:label' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001783 error.string());
1784 goto bail;
1785 }
1786
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001787 activityIcon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1788 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001789 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001790 SourcePos(manifestFile, tree.getLineNumber()).error(
1791 "ERROR getting 'android:icon' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001792 error.string());
1793 goto bail;
1794 }
1795
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001796 activityBanner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1797 &error);
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001798 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001799 SourcePos(manifestFile, tree.getLineNumber()).error(
1800 "ERROR getting 'android:banner' attribute: %s",
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001801 error.string());
1802 goto bail;
1803 }
1804
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001805 int32_t orien = AaptXml::getResolvedIntegerAttribute(res, tree,
Michael Wrightec4fdec2013-09-06 16:50:52 -07001806 SCREEN_ORIENTATION_ATTR, &error);
1807 if (error == "") {
1808 if (orien == 0 || orien == 6 || orien == 8) {
1809 // Requests landscape, sensorLandscape, or reverseLandscape.
Adam Lesinski43158772015-11-11 15:13:55 -08001810 addImpliedFeature(
1811 &impliedFeatures, "android.hardware.screen.landscape",
1812 String8("one or more activities have specified a "
1813 "landscape orientation"),
1814 false);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001815 } else if (orien == 1 || orien == 7 || orien == 9) {
1816 // Requests portrait, sensorPortrait, or reversePortrait.
Adam Lesinski43158772015-11-11 15:13:55 -08001817 addImpliedFeature(
1818 &impliedFeatures, "android.hardware.screen.portrait",
1819 String8("one or more activities have specified a "
1820 "portrait orientation"),
1821 false);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001822 }
1823 }
1824 } else if (tag == "uses-library") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001825 String8 libraryName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001826 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001827 SourcePos(manifestFile, tree.getLineNumber()).error(
Michael Wrightec4fdec2013-09-06 16:50:52 -07001828 "ERROR getting 'android:name' attribute for uses-library"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001829 " %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001830 goto bail;
1831 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001832 int req = AaptXml::getIntegerAttribute(tree,
1833 REQUIRED_ATTR, 1);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001834 printf("uses-library%s:'%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001835 req ? "" : "-not-required", ResTable::normalizeForOutput(
1836 libraryName.string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001837 } else if (tag == "receiver") {
1838 withinReceiver = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001839 receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001840
1841 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001842 SourcePos(manifestFile, tree.getLineNumber()).error(
Michael Wrightec4fdec2013-09-06 16:50:52 -07001843 "ERROR getting 'android:name' attribute for receiver:"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001844 " %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001845 goto bail;
1846 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001847
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001848 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1849 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001850 if (error == "") {
1851 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1852 hasBindDeviceAdminPermission = true;
1853 }
1854 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001855 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001856 "ERROR getting 'android:permission' attribute for"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001857 " receiver '%s': %s",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001858 receiverName.string(), error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001859 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001860 } else if (tag == "service") {
1861 withinService = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001862 serviceName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001863
1864 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001865 SourcePos(manifestFile, tree.getLineNumber()).error(
1866 "ERROR getting 'android:name' attribute for "
1867 "service:%s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001868 goto bail;
1869 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001870
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001871 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1872 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001873 if (error == "") {
1874 if (permission == "android.permission.BIND_INPUT_METHOD") {
1875 hasBindInputMethodPermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001876 } else if (permission ==
1877 "android.permission.BIND_ACCESSIBILITY_SERVICE") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001878 hasBindAccessibilityServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001879 } else if (permission ==
1880 "android.permission.BIND_PRINT_SERVICE") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001881 hasBindPrintServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001882 } else if (permission ==
1883 "android.permission.BIND_NFC_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07001884 hasBindNfcServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001885 } else if (permission ==
1886 "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001887 hasBindNotificationListenerServicePermission = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001888 } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
1889 hasBindDreamServicePermission = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001890 }
1891 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001892 SourcePos(manifestFile, tree.getLineNumber()).error(
1893 "ERROR getting 'android:permission' attribute for "
1894 "service '%s': %s", serviceName.string(), error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001895 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001896 } else if (tag == "provider") {
1897 withinProvider = true;
1898
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001899 bool exported = AaptXml::getResolvedIntegerAttribute(res, tree,
1900 EXPORTED_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001901 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001902 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001903 "ERROR getting 'android:exported' attribute for provider:"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001904 " %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001905 goto bail;
1906 }
1907
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001908 bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute(
1909 res, tree, GRANT_URI_PERMISSIONS_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001910 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001911 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001912 "ERROR getting 'android:grantUriPermissions' attribute for "
Adam Lesinski10de3af12016-07-13 10:14:03 -07001913 "provider: %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001914 goto bail;
1915 }
1916
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001917 String8 permission = AaptXml::getResolvedAttribute(res, tree,
1918 PERMISSION_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001919 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001920 SourcePos(manifestFile, tree.getLineNumber()).error(
1921 "ERROR getting 'android:permission' attribute for "
1922 "provider: %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001923 goto bail;
1924 }
1925
1926 hasRequiredSafAttributes |= exported && grantUriPermissions &&
1927 permission == "android.permission.MANAGE_DOCUMENTS";
1928
Michael Wrightec4fdec2013-09-06 16:50:52 -07001929 } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001930 String8 metaDataName = AaptXml::getResolvedAttribute(res, tree,
1931 NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001932 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001933 SourcePos(manifestFile, tree.getLineNumber()).error(
1934 "ERROR getting 'android:name' attribute for "
1935 "meta-data: %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001936 goto bail;
1937 }
Maurice Chu2675f762013-10-22 17:33:11 -07001938 printf("meta-data: name='%s' ",
1939 ResTable::normalizeForOutput(metaDataName.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001940 printResolvedResourceAttribute(res, tree, VALUE_ATTR, String8("value"),
Maurice Chu76327312013-10-16 18:28:46 -07001941 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001942 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001943 // Try looking for a RESOURCE_ATTR
1944 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001945 printResolvedResourceAttribute(res, tree, RESOURCE_ATTR,
Maurice Chu76327312013-10-16 18:28:46 -07001946 String8("resource"), &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001947 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001948 SourcePos(manifestFile, tree.getLineNumber()).error(
1949 "ERROR getting 'android:value' or "
Maurice Chu76327312013-10-16 18:28:46 -07001950 "'android:resource' attribute for "
Adam Lesinski10de3af12016-07-13 10:14:03 -07001951 "meta-data: %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001952 goto bail;
1953 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001954 }
Maurice Chu76327312013-10-16 18:28:46 -07001955 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001956 } else if (withinSupportsInput && tag == "input-type") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001957 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001958 if (name != "" && error == "") {
1959 supportedInput.add(name);
1960 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001961 SourcePos(manifestFile, tree.getLineNumber()).error(
1962 "ERROR getting 'android:name' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001963 error.string());
1964 goto bail;
1965 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001966 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001967 } else if (withinFeatureGroup && tag == "uses-feature") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001968 const String8 androidSchema("http://schemas.android.com/apk/res/android");
Adam Lesinski2c72b682014-06-24 09:56:01 -07001969 FeatureGroup& top = featureGroups.editTop();
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001970
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001971 String8 name = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001972 if (name != "" && error == "") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001973 Feature feature(true);
1974
1975 int32_t featureVers = AaptXml::getIntegerAttribute(
1976 tree, androidSchema.string(), "version", 0, &error);
1977 if (error == "") {
1978 feature.version = featureVers;
1979 } else {
1980 SourcePos(manifestFile, tree.getLineNumber()).error(
1981 "failed to read attribute 'android:version': %s",
1982 error.string());
1983 goto bail;
1984 }
1985
1986 top.features.add(name, feature);
Adam Lesinskid3edfde2014-08-08 17:32:44 -07001987 addParentFeatures(&top, name);
Adam Lesinski694d0a72016-04-06 16:12:04 -07001988
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001989 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001990 int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR,
1991 &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001992 if (error == "") {
1993 if (vers > top.openGLESVersion) {
1994 top.openGLESVersion = vers;
1995 }
1996 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001997 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001998 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001999 } else if (depth == 4) {
2000 if (tag == "intent-filter") {
2001 hasIntentFilter = true;
2002 withinIntentFilter = true;
2003 actMainActivity = false;
2004 actWidgetReceivers = false;
2005 actImeService = false;
2006 actWallpaperService = false;
2007 actAccessibilityService = false;
2008 actPrintService = false;
2009 actDeviceAdminEnabled = false;
2010 actHostApduService = false;
2011 actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002012 actDocumentsProvider = false;
2013 actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04002014 actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002015 actCamera = false;
2016 actCameraSecure = false;
2017 catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07002018 } else if (withinService && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07002019 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07002020 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07002021 SourcePos(manifestFile, tree.getLineNumber()).error(
2022 "ERROR getting 'android:name' attribute for "
2023 "meta-data tag in service '%s': %s", serviceName.string(),
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002024 error.string());
Adam Lesinski94fc9122013-09-30 17:16:09 -07002025 goto bail;
2026 }
2027
2028 if (name == "android.nfc.cardemulation.host_apdu_service" ||
2029 name == "android.nfc.cardemulation.off_host_apdu_service") {
2030 bool offHost = true;
2031 if (name == "android.nfc.cardemulation.host_apdu_service") {
2032 offHost = false;
2033 }
2034
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07002035 String8 xmlPath = AaptXml::getResolvedAttribute(res, tree,
2036 RESOURCE_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07002037 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07002038 SourcePos(manifestFile, tree.getLineNumber()).error(
2039 "ERROR getting 'android:resource' attribute for "
2040 "meta-data tag in service '%s': %s",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002041 serviceName.string(), error.string());
Adam Lesinski94fc9122013-09-30 17:16:09 -07002042 goto bail;
2043 }
2044
2045 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
2046 offHost, &error);
2047 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07002048 SourcePos(manifestFile, tree.getLineNumber()).error(
2049 "ERROR getting AID category for service '%s'",
Adam Lesinski94fc9122013-09-30 17:16:09 -07002050 serviceName.string());
2051 goto bail;
2052 }
2053
2054 const size_t catLen = categories.size();
2055 for (size_t i = 0; i < catLen; i++) {
2056 bool paymentCategory = (categories[i] == "payment");
2057 if (offHost) {
2058 hasMetaOffHostPaymentCategory |= paymentCategory;
2059 } else {
2060 hasMetaHostPaymentCategory |= paymentCategory;
2061 }
2062 }
2063 }
2064 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002065 } else if ((depth == 5) && withinIntentFilter) {
2066 String8 action;
2067 if (tag == "action") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07002068 action = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07002069 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07002070 SourcePos(manifestFile, tree.getLineNumber()).error(
2071 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07002072 goto bail;
Michael Wrightec4fdec2013-09-06 16:50:52 -07002073 }
2074
Adam Lesinskia5018c92013-09-30 16:23:15 -07002075 if (withinActivity) {
2076 if (action == "android.intent.action.MAIN") {
2077 isMainActivity = true;
2078 actMainActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002079 } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
2080 action == "android.media.action.VIDEO_CAMERA") {
2081 actCamera = true;
2082 } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
2083 actCameraSecure = true;
Michael Wrightec4fdec2013-09-06 16:50:52 -07002084 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002085 } else if (withinReceiver) {
2086 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
2087 actWidgetReceivers = true;
2088 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
2089 actDeviceAdminEnabled = true;
2090 }
2091 } else if (withinService) {
2092 if (action == "android.view.InputMethod") {
2093 actImeService = true;
2094 } else if (action == "android.service.wallpaper.WallpaperService") {
2095 actWallpaperService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002096 } else if (action ==
2097 "android.accessibilityservice.AccessibilityService") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07002098 actAccessibilityService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002099 } else if (action =="android.printservice.PrintService") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07002100 actPrintService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002101 } else if (action ==
2102 "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07002103 actHostApduService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002104 } else if (action ==
2105 "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07002106 actOffHostApduService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002107 } else if (action ==
2108 "android.service.notification.NotificationListenerService") {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002109 actNotificationListenerService = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04002110 } else if (action == "android.service.dreams.DreamService") {
2111 actDreamService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002112 }
2113 } else if (withinProvider) {
2114 if (action == "android.content.action.DOCUMENTS_PROVIDER") {
2115 actDocumentsProvider = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07002116 }
2117 }
2118 if (action == "android.intent.action.SEARCH") {
2119 isSearchable = true;
2120 }
2121 }
2122
2123 if (tag == "category") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07002124 String8 category = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07002125 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07002126 SourcePos(manifestFile, tree.getLineNumber()).error(
2127 "ERROR getting 'name' attribute: %s", error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07002128 goto bail;
2129 }
2130 if (withinActivity) {
2131 if (category == "android.intent.category.LAUNCHER") {
2132 isLauncherActivity = true;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08002133 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
2134 isLeanbackLauncherActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002135 } else if (category == "android.intent.category.HOME") {
2136 catLauncher = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08002137 }
2138 }
2139 }
2140 }
2141 }
2142
2143 // Pre-1.6 implicitly granted permission compatibility logic
Jackal Guo201a60a2021-08-31 12:37:30 +08002144 if (targetSdk < SDK_DONUT) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002145 if (!hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002146 printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
2147 printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
2148 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002149 hasWriteExternalStoragePermission = true;
2150 }
2151 if (!hasReadPhoneStatePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002152 printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
2153 printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
2154 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002155 }
2156 }
2157
2158 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
2159 // force them to always take READ_EXTERNAL_STORAGE as well. We always
2160 // do this (regardless of target API version) because we can't have
2161 // an app with write permission but not read permission.
2162 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
Adam Lesinski2386df22016-12-28 15:08:58 -05002163 printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
2164 false /* optional */, writeExternalStoragePermissionMaxSdkVersion);
Adam Lesinski58f1f362013-11-12 12:59:08 -08002165 printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
Adam Lesinski2386df22016-12-28 15:08:58 -05002166 String8("requested WRITE_EXTERNAL_STORAGE"),
2167 writeExternalStoragePermissionMaxSdkVersion);
Adam Lesinski282e1812014-01-23 18:17:42 -08002168 }
2169
2170 // Pre-JellyBean call log permission compatibility.
Jackal Guo201a60a2021-08-31 12:37:30 +08002171 if (targetSdk < SDK_JELLY_BEAN) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002172 if (!hasReadCallLogPermission && hasReadContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002173 printUsesPermission(String8("android.permission.READ_CALL_LOG"));
2174 printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
2175 String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002176 }
2177 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002178 printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
2179 printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
2180 String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002181 }
2182 }
2183
Adam Lesinskica955a42016-08-01 16:44:29 -07002184 // If the app hasn't declared the touchscreen as a feature requirement (either
2185 // directly or implied, required or not), then the faketouch feature is implied.
2186 if (!hasFeature("android.hardware.touchscreen", commonFeatures, impliedFeatures)) {
2187 addImpliedFeature(&impliedFeatures, "android.hardware.faketouch",
Adam Lesinski43158772015-11-11 15:13:55 -08002188 String8("default feature for all apps"), false);
Adam Lesinskica955a42016-08-01 16:44:29 -07002189 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07002190
2191 const size_t numFeatureGroups = featureGroups.size();
2192 if (numFeatureGroups == 0) {
2193 // If no <feature-group> tags were defined, apply auto-implied features.
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002194 printDefaultFeatureGroup(commonFeatures, impliedFeatures);
Adam Lesinski2c72b682014-06-24 09:56:01 -07002195
2196 } else {
2197 // <feature-group> tags are defined, so we ignore implied features and
2198 for (size_t i = 0; i < numFeatureGroups; i++) {
2199 FeatureGroup& grp = featureGroups.editItemAt(i);
2200
Adam Lesinskid7a94da2014-07-25 14:38:54 -07002201 if (commonFeatures.openGLESVersion > grp.openGLESVersion) {
2202 grp.openGLESVersion = commonFeatures.openGLESVersion;
2203 }
2204
Adam Lesinski2c72b682014-06-24 09:56:01 -07002205 // Merge the features defined in the top level (not inside a <feature-group>)
2206 // with this feature group.
2207 const size_t numCommonFeatures = commonFeatures.features.size();
2208 for (size_t j = 0; j < numCommonFeatures; j++) {
2209 if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07002210 grp.features.add(commonFeatures.features.keyAt(j),
2211 commonFeatures.features[j]);
Adam Lesinski2c72b682014-06-24 09:56:01 -07002212 }
2213 }
2214
Adam Lesinski73a05112014-12-08 12:53:17 -08002215 if (!grp.features.isEmpty()) {
Adam Lesinski2c72b682014-06-24 09:56:01 -07002216 printFeatureGroup(grp);
Adam Lesinski282e1812014-01-23 18:17:42 -08002217 }
2218 }
2219 }
2220
Adam Lesinski282e1812014-01-23 18:17:42 -08002221
Adam Lesinski282e1812014-01-23 18:17:42 -08002222 if (hasWidgetReceivers) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002223 printComponentPresence("app-widget");
Adam Lesinski282e1812014-01-23 18:17:42 -08002224 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002225 if (hasDeviceAdminReceiver) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002226 printComponentPresence("device-admin");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002227 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002228 if (hasImeService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002229 printComponentPresence("ime");
Adam Lesinski282e1812014-01-23 18:17:42 -08002230 }
2231 if (hasWallpaperService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002232 printComponentPresence("wallpaper");
Adam Lesinski282e1812014-01-23 18:17:42 -08002233 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002234 if (hasAccessibilityService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002235 printComponentPresence("accessibility");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002236 }
2237 if (hasPrintService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002238 printComponentPresence("print-service");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002239 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07002240 if (hasPaymentService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002241 printComponentPresence("payment");
2242 }
2243 if (isSearchable) {
2244 printComponentPresence("search");
2245 }
2246 if (hasDocumentsProvider) {
2247 printComponentPresence("document-provider");
2248 }
2249 if (hasLauncher) {
2250 printComponentPresence("launcher");
2251 }
2252 if (hasNotificationListenerService) {
2253 printComponentPresence("notification-listener");
2254 }
John Spurlockeb8d1be2014-06-25 17:46:15 -04002255 if (hasDreamService) {
2256 printComponentPresence("dream");
2257 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002258 if (hasCameraActivity) {
2259 printComponentPresence("camera");
2260 }
2261 if (hasCameraSecureActivity) {
2262 printComponentPresence("camera-secure");
2263 }
2264
2265 if (hasMainActivity) {
2266 printf("main\n");
Adam Lesinski94fc9122013-09-30 17:16:09 -07002267 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002268 if (hasOtherActivities) {
2269 printf("other-activities\n");
2270 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002271 if (hasOtherReceivers) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002272 printf("other-receivers\n");
2273 }
2274 if (hasOtherServices) {
2275 printf("other-services\n");
2276 }
2277
2278 // For modern apps, if screen size buckets haven't been specified
2279 // but the new width ranges have, then infer the buckets from them.
2280 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
2281 && requiresSmallestWidthDp > 0) {
2282 int compatWidth = compatibleWidthLimitDp;
2283 if (compatWidth <= 0) {
2284 compatWidth = requiresSmallestWidthDp;
2285 }
2286 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
2287 smallScreen = -1;
2288 } else {
2289 smallScreen = 0;
2290 }
2291 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
2292 normalScreen = -1;
2293 } else {
2294 normalScreen = 0;
2295 }
2296 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
2297 largeScreen = -1;
2298 } else {
2299 largeScreen = 0;
2300 }
2301 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
2302 xlargeScreen = -1;
2303 } else {
2304 xlargeScreen = 0;
2305 }
2306 }
2307
2308 // Determine default values for any unspecified screen sizes,
2309 // based on the target SDK of the package. As of 4 (donut)
2310 // the screen size support was introduced, so all default to
2311 // enabled.
2312 if (smallScreen > 0) {
Jackal Guo201a60a2021-08-31 12:37:30 +08002313 smallScreen = targetSdk >= SDK_DONUT ? -1 : 0;
Adam Lesinski282e1812014-01-23 18:17:42 -08002314 }
2315 if (normalScreen > 0) {
2316 normalScreen = -1;
2317 }
2318 if (largeScreen > 0) {
Jackal Guo201a60a2021-08-31 12:37:30 +08002319 largeScreen = targetSdk >= SDK_DONUT ? -1 : 0;
Adam Lesinski282e1812014-01-23 18:17:42 -08002320 }
2321 if (xlargeScreen > 0) {
2322 // Introduced in Gingerbread.
Jackal Guo201a60a2021-08-31 12:37:30 +08002323 xlargeScreen = targetSdk >= SDK_GINGERBREAD ? -1 : 0;
Adam Lesinski282e1812014-01-23 18:17:42 -08002324 }
2325 if (anyDensity > 0) {
Jackal Guo201a60a2021-08-31 12:37:30 +08002326 anyDensity = (targetSdk >= SDK_DONUT || requiresSmallestWidthDp > 0 ||
2327 compatibleWidthLimitDp > 0)
2328 ? -1
2329 : 0;
Adam Lesinski282e1812014-01-23 18:17:42 -08002330 }
2331 printf("supports-screens:");
2332 if (smallScreen != 0) {
2333 printf(" 'small'");
2334 }
2335 if (normalScreen != 0) {
2336 printf(" 'normal'");
2337 }
2338 if (largeScreen != 0) {
2339 printf(" 'large'");
2340 }
2341 if (xlargeScreen != 0) {
2342 printf(" 'xlarge'");
2343 }
2344 printf("\n");
2345 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
2346 if (requiresSmallestWidthDp > 0) {
2347 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
2348 }
2349 if (compatibleWidthLimitDp > 0) {
2350 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
2351 }
2352 if (largestWidthLimitDp > 0) {
2353 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
2354 }
2355
2356 printf("locales:");
2357 const size_t NL = locales.size();
2358 for (size_t i=0; i<NL; i++) {
2359 const char* localeStr = locales[i].string();
2360 if (localeStr == NULL || strlen(localeStr) == 0) {
2361 localeStr = "--_--";
2362 }
2363 printf(" '%s'", localeStr);
2364 }
2365 printf("\n");
2366
2367 printf("densities:");
2368 const size_t ND = densities.size();
2369 for (size_t i=0; i<ND; i++) {
2370 printf(" '%d'", densities[i]);
2371 }
2372 printf("\n");
2373
2374 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
2375 if (dir != NULL) {
2376 if (dir->getFileCount() > 0) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002377 SortedVector<String8> architectures;
Adam Lesinski282e1812014-01-23 18:17:42 -08002378 for (size_t i=0; i<dir->getFileCount(); i++) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002379 architectures.add(ResTable::normalizeForOutput(
2380 dir->getFileName(i).string()));
Adam Lesinski282e1812014-01-23 18:17:42 -08002381 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07002382
2383 bool outputAltNativeCode = false;
2384 // A multiArch package is one that contains 64-bit and
2385 // 32-bit versions of native code and expects 3rd-party
2386 // apps to load these native code libraries. Since most
2387 // 64-bit systems also support 32-bit apps, the apps
2388 // loading this multiArch package's code may be either
2389 // 32-bit or 64-bit.
2390 if (hasMultiArch) {
2391 // If this is a multiArch package, report the 64-bit
2392 // version only. Then as a separate entry, report the
2393 // rest.
2394 //
2395 // If we report the 32-bit architecture, this APK will
2396 // be installed on a 32-bit device, causing a large waste
2397 // of bandwidth and disk space. This assumes that
2398 // the developer of the multiArch package has also
2399 // made a version that is 32-bit only.
2400 String8 intel64("x86_64");
2401 String8 arm64("arm64-v8a");
2402 ssize_t index = architectures.indexOf(intel64);
2403 if (index < 0) {
2404 index = architectures.indexOf(arm64);
2405 }
2406
2407 if (index >= 0) {
2408 printf("native-code: '%s'\n", architectures[index].string());
2409 architectures.removeAt(index);
2410 outputAltNativeCode = true;
2411 }
2412 }
2413
2414 const size_t archCount = architectures.size();
2415 if (archCount > 0) {
2416 if (outputAltNativeCode) {
2417 printf("alt-");
2418 }
2419 printf("native-code:");
2420 for (size_t i = 0; i < archCount; i++) {
2421 printf(" '%s'", architectures[i].string());
2422 }
2423 printf("\n");
2424 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002425 }
2426 delete dir;
2427 }
2428 } else if (strcmp("badger", option) == 0) {
2429 printf("%s", CONSOLE_DATA);
2430 } else if (strcmp("configurations", option) == 0) {
2431 Vector<ResTable_config> configs;
2432 res.getConfigurations(&configs);
2433 const size_t N = configs.size();
2434 for (size_t i=0; i<N; i++) {
2435 printf("%s\n", configs[i].toString().string());
2436 }
2437 } else {
2438 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2439 goto bail;
2440 }
2441 }
2442
2443 result = NO_ERROR;
2444
2445bail:
Adam Lesinski10de3af12016-07-13 10:14:03 -07002446 if (SourcePos::hasErrors()) {
2447 SourcePos::printErrors(stderr);
2448 }
2449
Adam Lesinski282e1812014-01-23 18:17:42 -08002450 if (asset) {
2451 delete asset;
2452 }
2453 return (result != NO_ERROR);
2454}
2455
2456
2457/*
2458 * Handle the "add" command, which wants to add files to a new or
2459 * pre-existing archive.
2460 */
2461int doAdd(Bundle* bundle)
2462{
2463 ZipFile* zip = NULL;
2464 status_t result = UNKNOWN_ERROR;
2465 const char* zipFileName;
2466
2467 if (bundle->getUpdate()) {
2468 /* avoid confusion */
2469 fprintf(stderr, "ERROR: can't use '-u' with add\n");
2470 goto bail;
2471 }
2472
2473 if (bundle->getFileSpecCount() < 1) {
2474 fprintf(stderr, "ERROR: must specify zip file name\n");
2475 goto bail;
2476 }
2477 zipFileName = bundle->getFileSpecEntry(0);
2478
2479 if (bundle->getFileSpecCount() < 2) {
2480 fprintf(stderr, "NOTE: nothing to do\n");
2481 goto bail;
2482 }
2483
2484 zip = openReadWrite(zipFileName, true);
2485 if (zip == NULL) {
2486 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2487 goto bail;
2488 }
2489
2490 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2491 const char* fileName = bundle->getFileSpecEntry(i);
2492
2493 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
2494 printf(" '%s'... (from gzip)\n", fileName);
2495 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
2496 } else {
2497 if (bundle->getJunkPath()) {
2498 String8 storageName = String8(fileName).getPathLeaf();
Maurice Chu2675f762013-10-22 17:33:11 -07002499 printf(" '%s' as '%s'...\n", fileName,
2500 ResTable::normalizeForOutput(storageName.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08002501 result = zip->add(fileName, storageName.string(),
2502 bundle->getCompressionMethod(), NULL);
2503 } else {
2504 printf(" '%s'...\n", fileName);
2505 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2506 }
2507 }
2508 if (result != NO_ERROR) {
2509 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2510 if (result == NAME_NOT_FOUND) {
2511 fprintf(stderr, ": file not found\n");
2512 } else if (result == ALREADY_EXISTS) {
2513 fprintf(stderr, ": already exists in archive\n");
2514 } else {
2515 fprintf(stderr, "\n");
2516 }
2517 goto bail;
2518 }
2519 }
2520
2521 result = NO_ERROR;
2522
2523bail:
2524 delete zip;
2525 return (result != NO_ERROR);
2526}
2527
2528
2529/*
2530 * Delete files from an existing archive.
2531 */
2532int doRemove(Bundle* bundle)
2533{
2534 ZipFile* zip = NULL;
2535 status_t result = UNKNOWN_ERROR;
2536 const char* zipFileName;
2537
2538 if (bundle->getFileSpecCount() < 1) {
2539 fprintf(stderr, "ERROR: must specify zip file name\n");
2540 goto bail;
2541 }
2542 zipFileName = bundle->getFileSpecEntry(0);
2543
2544 if (bundle->getFileSpecCount() < 2) {
2545 fprintf(stderr, "NOTE: nothing to do\n");
2546 goto bail;
2547 }
2548
2549 zip = openReadWrite(zipFileName, false);
2550 if (zip == NULL) {
2551 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2552 zipFileName);
2553 goto bail;
2554 }
2555
2556 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2557 const char* fileName = bundle->getFileSpecEntry(i);
2558 ZipEntry* entry;
2559
2560 entry = zip->getEntryByName(fileName);
2561 if (entry == NULL) {
2562 printf(" '%s' NOT FOUND\n", fileName);
2563 continue;
2564 }
2565
2566 result = zip->remove(entry);
2567
2568 if (result != NO_ERROR) {
2569 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2570 bundle->getFileSpecEntry(i), zipFileName);
2571 goto bail;
2572 }
2573 }
2574
2575 /* update the archive */
2576 zip->flush();
2577
2578bail:
2579 delete zip;
2580 return (result != NO_ERROR);
2581}
2582
Adam Lesinski3921e872014-05-13 10:56:25 -07002583static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002584 const size_t numDirs = dir->getDirs().size();
2585 for (size_t i = 0; i < numDirs; i++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002586 bool ignore = ignoreConfig;
2587 const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2588 const char* dirStr = subDir->getLeaf().string();
2589 if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2590 ignore = true;
2591 }
2592 status_t err = addResourcesToBuilder(subDir, builder, ignore);
Adam Lesinskifab50872014-04-16 14:40:42 -07002593 if (err != NO_ERROR) {
2594 return err;
2595 }
2596 }
2597
2598 const size_t numFiles = dir->getFiles().size();
2599 for (size_t i = 0; i < numFiles; i++) {
2600 sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2601 const size_t numConfigs = gp->getFiles().size();
2602 for (size_t j = 0; j < numConfigs; j++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002603 status_t err = NO_ERROR;
Adam Lesinskic7614e52017-03-16 16:54:23 -07002604 if (ignoreConfig) {
Guang Zhu8c2df712017-03-21 03:53:43 +00002605 err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
Adam Lesinskic7614e52017-03-16 16:54:23 -07002606 } else {
Guang Zhu8c2df712017-03-21 03:53:43 +00002607 err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
Adam Lesinskic7614e52017-03-16 16:54:23 -07002608 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002609 if (err != NO_ERROR) {
2610 fprintf(stderr, "Failed to add %s (%s) to builder.\n",
Guang Zhu8c2df712017-03-21 03:53:43 +00002611 gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
Adam Lesinskifab50872014-04-16 14:40:42 -07002612 return err;
2613 }
2614 }
2615 }
2616 return NO_ERROR;
2617}
2618
2619static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2620 if (split->isBase()) {
2621 return original;
2622 }
2623
2624 String8 ext(original.getPathExtension());
2625 if (ext == String8(".apk")) {
2626 return String8::format("%s_%s%s",
2627 original.getBasePath().string(),
2628 split->getDirectorySafeName().string(),
2629 ext.string());
2630 }
2631
2632 return String8::format("%s_%s", original.string(),
2633 split->getDirectorySafeName().string());
2634}
Adam Lesinski282e1812014-01-23 18:17:42 -08002635
2636/*
2637 * Package up an asset directory and associated application files.
2638 */
2639int doPackage(Bundle* bundle)
2640{
2641 const char* outputAPKFile;
2642 int retVal = 1;
2643 status_t err;
2644 sp<AaptAssets> assets;
2645 int N;
2646 FILE* fp;
2647 String8 dependencyFile;
Adam Lesinskifab50872014-04-16 14:40:42 -07002648 sp<ApkBuilder> builder;
Adam Lesinski282e1812014-01-23 18:17:42 -08002649
Anton Krumina2ef5c02014-03-12 14:46:44 -07002650 // -c en_XA or/and ar_XB means do pseudolocalization
Adam Lesinskifab50872014-04-16 14:40:42 -07002651 sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2652 err = configFilter->parse(bundle->getConfigurations());
Adam Lesinski282e1812014-01-23 18:17:42 -08002653 if (err != NO_ERROR) {
2654 goto bail;
2655 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002656 if (configFilter->containsPseudo()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002657 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2658 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002659 if (configFilter->containsPseudoBidi()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002660 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
Adam Lesinski282e1812014-01-23 18:17:42 -08002661 }
2662
2663 N = bundle->getFileSpecCount();
2664 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
Adam Lesinski09384302014-01-22 16:07:42 -08002665 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002666 fprintf(stderr, "ERROR: no input files\n");
2667 goto bail;
2668 }
2669
2670 outputAPKFile = bundle->getOutputAPKFile();
2671
2672 // Make sure the filenames provided exist and are of the appropriate type.
2673 if (outputAPKFile) {
2674 FileType type;
2675 type = getFileType(outputAPKFile);
2676 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2677 fprintf(stderr,
2678 "ERROR: output file '%s' exists but is not regular file\n",
2679 outputAPKFile);
2680 goto bail;
2681 }
2682 }
2683
2684 // Load the assets.
2685 assets = new AaptAssets();
2686
2687 // Set up the resource gathering in assets if we're going to generate
2688 // dependency files. Every time we encounter a resource while slurping
2689 // the tree, we'll add it to these stores so we have full resource paths
2690 // to write to a dependency file.
2691 if (bundle->getGenDependencies()) {
2692 sp<FilePathStore> resPathStore = new FilePathStore;
2693 assets->setFullResPaths(resPathStore);
2694 sp<FilePathStore> assetPathStore = new FilePathStore;
2695 assets->setFullAssetPaths(assetPathStore);
2696 }
2697
2698 err = assets->slurpFromArgs(bundle);
2699 if (err < 0) {
2700 goto bail;
2701 }
2702
2703 if (bundle->getVerbose()) {
2704 assets->print(String8());
2705 }
2706
Adam Lesinskifab50872014-04-16 14:40:42 -07002707 // Create the ApkBuilder, which will collect the compiled files
2708 // to write to the final APK (or sets of APKs if we are building
2709 // a Split APK.
2710 builder = new ApkBuilder(configFilter);
2711
2712 // If we are generating a Split APK, find out which configurations to split on.
2713 if (bundle->getSplitConfigurations().size() > 0) {
2714 const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2715 const size_t numSplits = splitStrs.size();
2716 for (size_t i = 0; i < numSplits; i++) {
2717 std::set<ConfigDescription> configs;
2718 if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2719 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2720 goto bail;
2721 }
2722
2723 err = builder->createSplitForConfigs(configs);
2724 if (err != NO_ERROR) {
2725 goto bail;
2726 }
2727 }
2728 }
2729
Adam Lesinski282e1812014-01-23 18:17:42 -08002730 // If they asked for any fileAs that need to be compiled, do so.
2731 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002732 err = buildResources(bundle, assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002733 if (err != 0) {
2734 goto bail;
2735 }
2736 }
2737
2738 // At this point we've read everything and processed everything. From here
2739 // on out it's just writing output files.
2740 if (SourcePos::hasErrors()) {
2741 goto bail;
2742 }
2743
2744 // Update symbols with information about which ones are needed as Java symbols.
2745 assets->applyJavaSymbols();
2746 if (SourcePos::hasErrors()) {
2747 goto bail;
2748 }
2749
2750 // If we've been asked to generate a dependency file, do that here
2751 if (bundle->getGenDependencies()) {
2752 // If this is the packaging step, generate the dependency file next to
2753 // the output apk (e.g. bin/resources.ap_.d)
2754 if (outputAPKFile) {
2755 dependencyFile = String8(outputAPKFile);
2756 // Add the .d extension to the dependency file.
2757 dependencyFile.append(".d");
2758 } else {
2759 // Else if this is the R.java dependency generation step,
2760 // generate the dependency file in the R.java package subdirectory
2761 // e.g. gen/com/foo/app/R.java.d
2762 dependencyFile = String8(bundle->getRClassDir());
2763 dependencyFile.appendPath("R.java.d");
2764 }
2765 // Make sure we have a clean dependency file to start with
2766 fp = fopen(dependencyFile, "w");
2767 fclose(fp);
2768 }
2769
2770 // Write out R.java constants
2771 if (!assets->havePrivateSymbols()) {
2772 if (bundle->getCustomPackage() == NULL) {
2773 // Write the R.java file into the appropriate class directory
2774 // e.g. gen/com/foo/app/R.java
Adam Lesinski1e4663852014-08-15 14:47:28 -07002775 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002776 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002777 } else {
2778 const String8 customPkg(bundle->getCustomPackage());
Adam Lesinski1e4663852014-08-15 14:47:28 -07002779 err = writeResourceSymbols(bundle, assets, customPkg, true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002780 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002781 }
2782 if (err < 0) {
2783 goto bail;
2784 }
2785 // If we have library files, we're going to write our R.java file into
2786 // the appropriate class directory for those libraries as well.
2787 // e.g. gen/com/foo/app/lib/R.java
2788 if (bundle->getExtraPackages() != NULL) {
2789 // Split on colon
2790 String8 libs(bundle->getExtraPackages());
2791 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2792 while (packageString != NULL) {
2793 // Write the R.java file out with the correct package name
Marcin Kosiba0f3a5a62014-09-11 13:48:48 +01002794 err = writeResourceSymbols(bundle, assets, String8(packageString), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002795 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002796 if (err < 0) {
2797 goto bail;
2798 }
2799 packageString = strtok(NULL, ":");
2800 }
2801 libs.unlockBuffer();
2802 }
2803 } else {
Adam Lesinski1e4663852014-08-15 14:47:28 -07002804 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002805 if (err < 0) {
2806 goto bail;
2807 }
Adam Lesinski1e4663852014-08-15 14:47:28 -07002808 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002809 if (err < 0) {
2810 goto bail;
2811 }
2812 }
2813
2814 // Write out the ProGuard file
2815 err = writeProguardFile(bundle, assets);
2816 if (err < 0) {
2817 goto bail;
2818 }
2819
Rohit Agrawal86229cb2016-04-21 16:29:58 -07002820 // Write out the Main Dex ProGuard file
2821 err = writeMainDexProguardFile(bundle, assets);
2822 if (err < 0) {
2823 goto bail;
2824 }
2825
Adam Lesinski282e1812014-01-23 18:17:42 -08002826 // Write the apk
2827 if (outputAPKFile) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002828 // Gather all resources and add them to the APK Builder. The builder will then
2829 // figure out which Split they belong in.
2830 err = addResourcesToBuilder(assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002831 if (err != NO_ERROR) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002832 goto bail;
2833 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002834
2835 const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2836 const size_t numSplits = splits.size();
2837 for (size_t i = 0; i < numSplits; i++) {
2838 const sp<ApkSplit>& split = splits[i];
2839 String8 outputPath = buildApkName(String8(outputAPKFile), split);
2840 err = writeAPK(bundle, outputPath, split);
2841 if (err != NO_ERROR) {
2842 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2843 goto bail;
2844 }
2845 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002846 }
2847
2848 // If we've been asked to generate a dependency file, we need to finish up here.
2849 // the writeResourceSymbols and writeAPK functions have already written the target
2850 // half of the dependency file, now we need to write the prerequisites. (files that
2851 // the R.java file or .ap_ file depend on)
2852 if (bundle->getGenDependencies()) {
2853 // Now that writeResourceSymbols or writeAPK has taken care of writing
2854 // the targets to our dependency file, we'll write the prereqs
2855 fp = fopen(dependencyFile, "a+");
2856 fprintf(fp, " : ");
2857 bool includeRaw = (outputAPKFile != NULL);
2858 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2859 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2860 // and therefore was not added to our pathstores during slurping
2861 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2862 fclose(fp);
2863 }
2864
2865 retVal = 0;
2866bail:
2867 if (SourcePos::hasErrors()) {
2868 SourcePos::printErrors(stderr);
2869 }
2870 return retVal;
2871}
2872
2873/*
2874 * Do PNG Crunching
2875 * PRECONDITIONS
2876 * -S flag points to a source directory containing drawable* folders
2877 * -C flag points to destination directory. The folder structure in the
2878 * source directory will be mirrored to the destination (cache) directory
2879 *
2880 * POSTCONDITIONS
2881 * Destination directory will be updated to match the PNG files in
Maurice Chu2675f762013-10-22 17:33:11 -07002882 * the source directory.
Adam Lesinski282e1812014-01-23 18:17:42 -08002883 */
2884int doCrunch(Bundle* bundle)
2885{
2886 fprintf(stdout, "Crunching PNG Files in ");
2887 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2888 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2889
2890 updatePreProcessedCache(bundle);
2891
2892 return NO_ERROR;
2893}
2894
2895/*
2896 * Do PNG Crunching on a single flag
2897 * -i points to a single png file
2898 * -o points to a single png output file
2899 */
2900int doSingleCrunch(Bundle* bundle)
2901{
2902 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2903 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2904
2905 String8 input(bundle->getSingleCrunchInputFile());
2906 String8 output(bundle->getSingleCrunchOutputFile());
2907
2908 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2909 // we can't return the status_t as it gets truncate to the lower 8 bits.
2910 return 42;
2911 }
2912
2913 return NO_ERROR;
2914}
2915
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002916int runInDaemonMode(Bundle* bundle) {
2917 std::cout << "Ready" << std::endl;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002918 for (std::string cmd; std::getline(std::cin, cmd);) {
2919 if (cmd == "quit") {
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002920 return NO_ERROR;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002921 } else if (cmd == "s") {
2922 // Two argument crunch
2923 std::string inputFile, outputFile;
2924 std::getline(std::cin, inputFile);
2925 std::getline(std::cin, outputFile);
2926 bundle->setSingleCrunchInputFile(inputFile.c_str());
2927 bundle->setSingleCrunchOutputFile(outputFile.c_str());
2928 std::cout << "Crunching " << inputFile << std::endl;
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002929 if (doSingleCrunch(bundle) != NO_ERROR) {
2930 std::cout << "Error" << std::endl;
2931 }
2932 std::cout << "Done" << std::endl;
2933 } else {
2934 // in case of invalid command, just bail out.
2935 std::cerr << "Unknown command" << std::endl;
2936 return -1;
2937 }
2938 }
2939 return -1;
2940}
2941
Adam Lesinski282e1812014-01-23 18:17:42 -08002942char CONSOLE_DATA[2925] = {
2943 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2944 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2945 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2946 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2947 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2948 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2949 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2950 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2951 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2952 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2953 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2954 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2955 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2956 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2957 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2958 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2959 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2960 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2961 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2962 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2963 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2964 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2965 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2966 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2967 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2968 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2969 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2970 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2971 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2972 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2973 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2974 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2975 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2976 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2977 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2978 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2979 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2980 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2981 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2982 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2983 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2984 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2985 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2986 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2987 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2988 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2989 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2990 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2991 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2992 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2993 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2994 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2995 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2996 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2997 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2998 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2999 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
3000 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
3001 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
3002 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
3003 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
3004 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
3005 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
3006 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
3007 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
3008 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
3009 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
3010 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
3011 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
3012 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
3013 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
3014 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
3015 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3016 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
3017 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
3018 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
3019 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3020 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
3021 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
3022 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
3023 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
3024 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
3025 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
3026 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3027 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
3028 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
3029 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
3030 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
3031 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
3032 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3033 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3034 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
3035 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
3036 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3037 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3038 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
3039 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3040 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
3041 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
3042 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
3043 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3044 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3045 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
3046 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
3047 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
3048 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3049 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
3050 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3051 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3052 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
3053 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
3054 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3055 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3056 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
3057 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3058 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
3059 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
3060 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
3061 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3062 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3063 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
3064 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
3065 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
3066 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
3067 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
3068 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
3069 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
3070 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
3071 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
3072 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3073 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3074 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
3075 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
3076 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
3077 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
3078 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
3079 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3080 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3081 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
3082 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
3083 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
3084 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3085 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
3086 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3087 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
3088 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
3089 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
3090 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3091 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3092 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
3093 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
3094 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
3095 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
3096 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
3097 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3098 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3099 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
3100 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
3101 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3102 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3103 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3104 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3105 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
3106 };