blob: b5c35f62895bab52ecc8d5abfeb06ab29de43963 [file] [log] [blame]
Adam Lesinski282e1812014-01-23 18:17:42 -08001//
2// Copyright 2006 The Android Open Source Project
3//
4// Android Asset Packaging Tool main entry point.
5//
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07006#include "AaptXml.h"
Adam Lesinskifab50872014-04-16 14:40:42 -07007#include "ApkBuilder.h"
Adam Lesinski282e1812014-01-23 18:17:42 -08008#include "Bundle.h"
Adam Lesinski2c72b682014-06-24 09:56:01 -07009#include "Images.h"
10#include "Main.h"
Adam Lesinski282e1812014-01-23 18:17:42 -080011#include "ResourceFilter.h"
12#include "ResourceTable.h"
Adam Lesinski282e1812014-01-23 18:17:42 -080013#include "XMLNode.h"
14
Adam Lesinski282e1812014-01-23 18:17:42 -080015#include <utils/Errors.h>
Adam Lesinski2c72b682014-06-24 09:56:01 -070016#include <utils/KeyedVector.h>
17#include <utils/List.h>
18#include <utils/Log.h>
19#include <utils/SortedVector.h>
20#include <utils/threads.h>
21#include <utils/Vector.h>
Adam Lesinski282e1812014-01-23 18:17:42 -080022
Adam Lesinski282e1812014-01-23 18:17:42 -080023#include <errno.h>
Adam Lesinski2c72b682014-06-24 09:56:01 -070024#include <fcntl.h>
Adam Lesinski282e1812014-01-23 18:17:42 -080025
Jerome Dochez6f1280c2014-09-26 10:21:21 -070026#include <iostream>
27#include <string>
28#include <sstream>
29
Adam Lesinski282e1812014-01-23 18:17:42 -080030using namespace android;
31
Adam Lesinski282e1812014-01-23 18:17:42 -080032/*
33 * Open the file read only. The call fails if the file doesn't exist.
34 *
35 * Returns NULL on failure.
36 */
37ZipFile* openReadOnly(const char* fileName)
38{
39 ZipFile* zip;
40 status_t result;
41
42 zip = new ZipFile;
43 result = zip->open(fileName, ZipFile::kOpenReadOnly);
44 if (result != NO_ERROR) {
45 if (result == NAME_NOT_FOUND) {
46 fprintf(stderr, "ERROR: '%s' not found\n", fileName);
47 } else if (result == PERMISSION_DENIED) {
48 fprintf(stderr, "ERROR: '%s' access denied\n", fileName);
49 } else {
50 fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n",
51 fileName);
52 }
53 delete zip;
54 return NULL;
55 }
56
57 return zip;
58}
59
60/*
61 * Open the file read-write. The file will be created if it doesn't
62 * already exist and "okayToCreate" is set.
63 *
64 * Returns NULL on failure.
65 */
66ZipFile* openReadWrite(const char* fileName, bool okayToCreate)
67{
68 ZipFile* zip = NULL;
69 status_t result;
70 int flags;
71
72 flags = ZipFile::kOpenReadWrite;
73 if (okayToCreate) {
74 flags |= ZipFile::kOpenCreate;
75 }
76
77 zip = new ZipFile;
78 result = zip->open(fileName, flags);
79 if (result != NO_ERROR) {
80 delete zip;
81 zip = NULL;
82 goto bail;
83 }
84
85bail:
86 return zip;
87}
88
89
90/*
91 * Return a short string describing the compression method.
92 */
93const char* compressionName(int method)
94{
95 if (method == ZipEntry::kCompressStored) {
96 return "Stored";
97 } else if (method == ZipEntry::kCompressDeflated) {
98 return "Deflated";
99 } else {
100 return "Unknown";
101 }
102}
103
104/*
105 * Return the percent reduction in size (0% == no compression).
106 */
107int calcPercent(long uncompressedLen, long compressedLen)
108{
109 if (!uncompressedLen) {
110 return 0;
111 } else {
112 return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5);
113 }
114}
115
116/*
117 * Handle the "list" command, which can be a simple file dump or
118 * a verbose listing.
119 *
120 * The verbose listing closely matches the output of the Info-ZIP "unzip"
121 * command.
122 */
123int doList(Bundle* bundle)
124{
125 int result = 1;
126 ZipFile* zip = NULL;
127 const ZipEntry* entry;
128 long totalUncLen, totalCompLen;
129 const char* zipFileName;
130
131 if (bundle->getFileSpecCount() != 1) {
132 fprintf(stderr, "ERROR: specify zip file name (only)\n");
133 goto bail;
134 }
135 zipFileName = bundle->getFileSpecEntry(0);
136
137 zip = openReadOnly(zipFileName);
138 if (zip == NULL) {
139 goto bail;
140 }
141
142 int count, i;
143
144 if (bundle->getVerbose()) {
145 printf("Archive: %s\n", zipFileName);
146 printf(
147 " Length Method Size Ratio Offset Date Time CRC-32 Name\n");
148 printf(
149 "-------- ------ ------- ----- ------- ---- ---- ------ ----\n");
150 }
151
152 totalUncLen = totalCompLen = 0;
153
154 count = zip->getNumEntries();
155 for (i = 0; i < count; i++) {
156 entry = zip->getEntryByIndex(i);
157 if (bundle->getVerbose()) {
158 char dateBuf[32];
159 time_t when;
160
161 when = entry->getModWhen();
162 strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M",
163 localtime(&when));
164
165 printf("%8ld %-7.7s %7ld %3d%% %8zd %s %08lx %s\n",
166 (long) entry->getUncompressedLen(),
167 compressionName(entry->getCompressionMethod()),
168 (long) entry->getCompressedLen(),
169 calcPercent(entry->getUncompressedLen(),
170 entry->getCompressedLen()),
171 (size_t) entry->getLFHOffset(),
172 dateBuf,
173 entry->getCRC32(),
174 entry->getFileName());
175 } else {
176 printf("%s\n", entry->getFileName());
177 }
178
179 totalUncLen += entry->getUncompressedLen();
180 totalCompLen += entry->getCompressedLen();
181 }
182
183 if (bundle->getVerbose()) {
184 printf(
185 "-------- ------- --- -------\n");
186 printf("%8ld %7ld %2d%% %d files\n",
187 totalUncLen,
188 totalCompLen,
189 calcPercent(totalUncLen, totalCompLen),
190 zip->getNumEntries());
191 }
192
193 if (bundle->getAndroidList()) {
194 AssetManager assets;
195 if (!assets.addAssetPath(String8(zipFileName), NULL)) {
196 fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n");
197 goto bail;
198 }
199
Elliott Hughesba3fe562015-08-12 14:49:53 -0700200#ifdef __ANDROID__
Andreas Gampeb8dc7bc2014-10-01 19:07:51 -0700201 static const bool kHaveAndroidOs = true;
202#else
203 static const bool kHaveAndroidOs = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800204#endif
Andreas Gampeb8dc7bc2014-10-01 19:07:51 -0700205 const ResTable& res = assets.getResources(false);
206 if (!kHaveAndroidOs) {
207 printf("\nResource table:\n");
208 res.print(false);
209 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800210
211 Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
212 Asset::ACCESS_BUFFER);
213 if (manifestAsset == NULL) {
214 printf("\nNo AndroidManifest.xml found.\n");
215 } else {
216 printf("\nAndroid manifest:\n");
217 ResXMLTree tree;
218 tree.setTo(manifestAsset->getBuffer(true),
219 manifestAsset->getLength());
220 printXMLBlock(&tree);
221 }
222 delete manifestAsset;
223 }
224
225 result = 0;
226
227bail:
228 delete zip;
229 return result;
230}
231
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700232static void printResolvedResourceAttribute(const ResTable& resTable, const ResXMLTree& tree,
Chih-Hung Hsieh9b8528f2016-08-10 14:15:30 -0700233 uint32_t attrRes, const String8& attrLabel, String8* outError)
Maurice Chu76327312013-10-16 18:28:46 -0700234{
235 Res_value value;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700236 AaptXml::getResolvedResourceAttribute(resTable, tree, attrRes, &value, outError);
Maurice Chu76327312013-10-16 18:28:46 -0700237 if (*outError != "") {
238 *outError = "error print resolved resource attribute";
239 return;
240 }
241 if (value.dataType == Res_value::TYPE_STRING) {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700242 String8 result = AaptXml::getResolvedAttribute(resTable, tree, attrRes, outError);
Maurice Chu2675f762013-10-22 17:33:11 -0700243 printf("%s='%s'", attrLabel.string(),
244 ResTable::normalizeForOutput(result.string()).string());
Maurice Chu76327312013-10-16 18:28:46 -0700245 } else if (Res_value::TYPE_FIRST_INT <= value.dataType &&
246 value.dataType <= Res_value::TYPE_LAST_INT) {
247 printf("%s='%d'", attrLabel.string(), value.data);
248 } else {
249 printf("%s='0x%x'", attrLabel.string(), (int)value.data);
250 }
251}
252
Adam Lesinski282e1812014-01-23 18:17:42 -0800253// These are attribute resource constants for the platform, as found
254// in android.R.attr
255enum {
256 LABEL_ATTR = 0x01010001,
257 ICON_ATTR = 0x01010002,
258 NAME_ATTR = 0x01010003,
Adam Lesinskia5018c92013-09-30 16:23:15 -0700259 PERMISSION_ATTR = 0x01010006,
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700260 EXPORTED_ATTR = 0x01010010,
261 GRANT_URI_PERMISSIONS_ATTR = 0x0101001b,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700262 RESOURCE_ATTR = 0x01010025,
Adam Lesinski282e1812014-01-23 18:17:42 -0800263 DEBUGGABLE_ATTR = 0x0101000f,
264 VALUE_ATTR = 0x01010024,
265 VERSION_CODE_ATTR = 0x0101021b,
266 VERSION_NAME_ATTR = 0x0101021c,
267 SCREEN_ORIENTATION_ATTR = 0x0101001e,
268 MIN_SDK_VERSION_ATTR = 0x0101020c,
269 MAX_SDK_VERSION_ATTR = 0x01010271,
270 REQ_TOUCH_SCREEN_ATTR = 0x01010227,
271 REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
272 REQ_HARD_KEYBOARD_ATTR = 0x01010229,
273 REQ_NAVIGATION_ATTR = 0x0101022a,
274 REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
275 TARGET_SDK_VERSION_ATTR = 0x01010270,
276 TEST_ONLY_ATTR = 0x01010272,
277 ANY_DENSITY_ATTR = 0x0101026c,
278 GL_ES_VERSION_ATTR = 0x01010281,
279 SMALL_SCREEN_ATTR = 0x01010284,
280 NORMAL_SCREEN_ATTR = 0x01010285,
281 LARGE_SCREEN_ATTR = 0x01010286,
282 XLARGE_SCREEN_ATTR = 0x010102bf,
283 REQUIRED_ATTR = 0x0101028e,
Adam Lesinskicaf797c2014-08-22 12:56:26 -0700284 INSTALL_LOCATION_ATTR = 0x010102b7,
Adam Lesinski282e1812014-01-23 18:17:42 -0800285 SCREEN_SIZE_ATTR = 0x010102ca,
286 SCREEN_DENSITY_ATTR = 0x010102cb,
287 REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
288 COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
289 LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
290 PUBLIC_KEY_ATTR = 0x010103a6,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700291 CATEGORY_ATTR = 0x010103e8,
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800292 BANNER_ATTR = 0x10103f2,
Tim Kilbournd9b1cad2014-10-24 12:43:41 -0700293 ISGAME_ATTR = 0x10103f4,
Dianne Hackborncd154e92017-02-28 17:37:35 -0800294 REQUIRED_FEATURE_ATTR = 0x1010557,
295 REQUIRED_NOT_FEATURE_ATTR = 0x1010558,
Alan Viverette11be9312017-11-09 15:41:44 -0500296 COMPILE_SDK_VERSION_ATTR = 0x01010572, // NOT FINALIZED
297 COMPILE_SDK_VERSION_CODENAME_ATTR = 0x01010573, // NOT FINALIZED
Adam Lesinski282e1812014-01-23 18:17:42 -0800298};
299
Maurice Chu2675f762013-10-22 17:33:11 -0700300String8 getComponentName(String8 &pkgName, String8 &componentName) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800301 ssize_t idx = componentName.find(".");
302 String8 retStr(pkgName);
303 if (idx == 0) {
304 retStr += componentName;
305 } else if (idx < 0) {
306 retStr += ".";
307 retStr += componentName;
308 } else {
Maurice Chu2675f762013-10-22 17:33:11 -0700309 return componentName;
Adam Lesinski282e1812014-01-23 18:17:42 -0800310 }
Maurice Chu2675f762013-10-22 17:33:11 -0700311 return retStr;
Adam Lesinski282e1812014-01-23 18:17:42 -0800312}
313
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700314static void printCompatibleScreens(ResXMLTree& tree, String8* outError) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800315 size_t len;
316 ResXMLTree::event_code_t code;
317 int depth = 0;
318 bool first = true;
319 printf("compatible-screens:");
320 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
321 if (code == ResXMLTree::END_TAG) {
322 depth--;
323 if (depth < 0) {
324 break;
325 }
326 continue;
327 }
328 if (code != ResXMLTree::START_TAG) {
329 continue;
330 }
331 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700332 const char16_t* ctag16 = tree.getElementName(&len);
333 if (ctag16 == NULL) {
334 *outError = "failed to get XML element name (bad string pool)";
335 return;
336 }
337 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800338 if (tag == "screen") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700339 int32_t screenSize = AaptXml::getIntegerAttribute(tree,
340 SCREEN_SIZE_ATTR);
341 int32_t screenDensity = AaptXml::getIntegerAttribute(tree,
342 SCREEN_DENSITY_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -0800343 if (screenSize > 0 && screenDensity > 0) {
344 if (!first) {
345 printf(",");
346 }
347 first = false;
348 printf("'%d/%d'", screenSize, screenDensity);
349 }
350 }
351 }
352 printf("\n");
353}
354
Dianne Hackborncd154e92017-02-28 17:37:35 -0800355static void printUsesPermission(const String8& name, bool optional=false, int maxSdkVersion=-1,
356 const String8& requiredFeature = String8::empty(),
357 const String8& requiredNotFeature = String8::empty()) {
Adam Lesinski58f1f362013-11-12 12:59:08 -0800358 printf("uses-permission: name='%s'", ResTable::normalizeForOutput(name.string()).string());
359 if (maxSdkVersion != -1) {
360 printf(" maxSdkVersion='%d'", maxSdkVersion);
361 }
Dianne Hackborncd154e92017-02-28 17:37:35 -0800362 if (requiredFeature.length() > 0) {
363 printf(" requiredFeature='%s'", requiredFeature.string());
364 }
365 if (requiredNotFeature.length() > 0) {
366 printf(" requiredNotFeature='%s'", requiredNotFeature.string());
367 }
Adam Lesinski58f1f362013-11-12 12:59:08 -0800368 printf("\n");
369
370 if (optional) {
371 printf("optional-permission: name='%s'",
372 ResTable::normalizeForOutput(name.string()).string());
373 if (maxSdkVersion != -1) {
374 printf(" maxSdkVersion='%d'", maxSdkVersion);
375 }
376 printf("\n");
377 }
378}
379
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800380static void printUsesPermissionSdk23(const String8& name, int maxSdkVersion=-1) {
381 printf("uses-permission-sdk-23: ");
382
383 printf("name='%s'", ResTable::normalizeForOutput(name.string()).string());
384 if (maxSdkVersion != -1) {
385 printf(" maxSdkVersion='%d'", maxSdkVersion);
386 }
387 printf("\n");
388}
389
Adam Lesinski2386df22016-12-28 15:08:58 -0500390static void printUsesImpliedPermission(const String8& name, const String8& reason,
391 const int32_t maxSdkVersion = -1) {
392 printf("uses-implied-permission: name='%s'",
393 ResTable::normalizeForOutput(name.string()).string());
394 if (maxSdkVersion != -1) {
395 printf(" maxSdkVersion='%d'", maxSdkVersion);
396 }
397 printf(" reason='%s'\n", ResTable::normalizeForOutput(reason.string()).string());
Adam Lesinski58f1f362013-11-12 12:59:08 -0800398}
399
Chih-Hung Hsieh9b8528f2016-08-10 14:15:30 -0700400Vector<String8> getNfcAidCategories(AssetManager& assets, const String8& xmlPath, bool offHost,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700401 String8 *outError = NULL)
402{
403 Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER);
404 if (aidAsset == NULL) {
405 if (outError != NULL) *outError = "xml resource does not exist";
406 return Vector<String8>();
407 }
408
409 const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service");
410
411 bool withinApduService = false;
412 Vector<String8> categories;
413
414 String8 error;
415 ResXMLTree tree;
416 tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength());
417
418 size_t len;
419 int depth = 0;
420 ResXMLTree::event_code_t code;
421 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
422 if (code == ResXMLTree::END_TAG) {
423 depth--;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700424 const char16_t* ctag16 = tree.getElementName(&len);
425 if (ctag16 == NULL) {
426 *outError = "failed to get XML element name (bad string pool)";
427 return Vector<String8>();
428 }
429 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700430
431 if (depth == 0 && tag == serviceTagName) {
432 withinApduService = false;
433 }
434
435 } else if (code == ResXMLTree::START_TAG) {
436 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700437 const char16_t* ctag16 = tree.getElementName(&len);
438 if (ctag16 == NULL) {
439 *outError = "failed to get XML element name (bad string pool)";
440 return Vector<String8>();
441 }
442 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700443
444 if (depth == 1) {
445 if (tag == serviceTagName) {
446 withinApduService = true;
447 }
448 } else if (depth == 2 && withinApduService) {
449 if (tag == "aid-group") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700450 String8 category = AaptXml::getAttribute(tree, CATEGORY_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700451 if (error != "") {
452 if (outError != NULL) *outError = error;
453 return Vector<String8>();
454 }
455
456 categories.add(category);
457 }
458 }
459 }
460 }
461 aidAsset->close();
462 return categories;
463}
464
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700465static void printComponentPresence(const char* componentName) {
466 printf("provides-component:'%s'\n", componentName);
467}
468
Adam Lesinski2c72b682014-06-24 09:56:01 -0700469/**
470 * Represents a feature that has been automatically added due to
471 * a pre-requisite or some other reason.
472 */
473struct ImpliedFeature {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800474 ImpliedFeature() : impliedBySdk23(false) {}
475 ImpliedFeature(const String8& n, bool sdk23) : name(n), impliedBySdk23(sdk23) {}
476
Adam Lesinski2c72b682014-06-24 09:56:01 -0700477 /**
478 * Name of the implied feature.
479 */
480 String8 name;
481
482 /**
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800483 * Was this implied by a permission from SDK 23 (<uses-permission-sdk-23 />)?
484 */
485 bool impliedBySdk23;
486
487 /**
Adam Lesinski2c72b682014-06-24 09:56:01 -0700488 * List of human-readable reasons for why this feature was implied.
489 */
490 SortedVector<String8> reasons;
491};
492
Adam Lesinski694d0a72016-04-06 16:12:04 -0700493struct Feature {
494 Feature() : required(false), version(-1) {}
Chih-Hung Hsiehd53e3be2016-05-03 10:02:51 -0700495 explicit Feature(bool required, int32_t version = -1) : required(required), version(version) {}
Adam Lesinski694d0a72016-04-06 16:12:04 -0700496
497 /**
498 * Whether the feature is required.
499 */
500 bool required;
501
502 /**
503 * What version of the feature is requested.
504 */
505 int32_t version;
506};
507
Adam Lesinski2c72b682014-06-24 09:56:01 -0700508/**
509 * Represents a <feature-group> tag in the AndroidManifest.xml
510 */
511struct FeatureGroup {
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700512 FeatureGroup() : openGLESVersion(-1) {}
513
Adam Lesinski2c72b682014-06-24 09:56:01 -0700514 /**
515 * Human readable label
516 */
517 String8 label;
518
519 /**
520 * Explicit features defined in the group
521 */
Adam Lesinski694d0a72016-04-06 16:12:04 -0700522 KeyedVector<String8, Feature> features;
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700523
524 /**
525 * OpenGL ES version required
526 */
527 int openGLESVersion;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700528};
529
Adam Lesinskica955a42016-08-01 16:44:29 -0700530static bool hasFeature(const char* name, const FeatureGroup& grp,
531 const KeyedVector<String8, ImpliedFeature>& implied) {
532 String8 name8(name);
533 ssize_t idx = grp.features.indexOfKey(name8);
534 if (idx < 0) {
535 idx = implied.indexOfKey(name8);
536 }
537 return idx >= 0;
538}
539
Adam Lesinski2c72b682014-06-24 09:56:01 -0700540static void addImpliedFeature(KeyedVector<String8, ImpliedFeature>* impliedFeatures,
Adam Lesinski43158772015-11-11 15:13:55 -0800541 const char* name, const String8& reason, bool sdk23) {
Adam Lesinski2c72b682014-06-24 09:56:01 -0700542 String8 name8(name);
543 ssize_t idx = impliedFeatures->indexOfKey(name8);
544 if (idx < 0) {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800545 idx = impliedFeatures->add(name8, ImpliedFeature(name8, sdk23));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700546 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800547
548 ImpliedFeature* feature = &impliedFeatures->editValueAt(idx);
549
550 // A non-sdk 23 implied feature takes precedence.
551 if (feature->impliedBySdk23 && !sdk23) {
552 feature->impliedBySdk23 = false;
553 }
Adam Lesinski43158772015-11-11 15:13:55 -0800554 feature->reasons.add(reason);
Adam Lesinski2c72b682014-06-24 09:56:01 -0700555}
556
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800557static void printFeatureGroupImpl(const FeatureGroup& grp,
558 const KeyedVector<String8, ImpliedFeature>* impliedFeatures) {
Adam Lesinski2c72b682014-06-24 09:56:01 -0700559 printf("feature-group: label='%s'\n", grp.label.string());
560
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700561 if (grp.openGLESVersion > 0) {
562 printf(" uses-gl-es: '0x%x'\n", grp.openGLESVersion);
563 }
564
Adam Lesinski2c72b682014-06-24 09:56:01 -0700565 const size_t numFeatures = grp.features.size();
566 for (size_t i = 0; i < numFeatures; i++) {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700567 const Feature& feature = grp.features[i];
568 const bool required = feature.required;
569 const int32_t version = feature.version;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700570
571 const String8& featureName = grp.features.keyAt(i);
Adam Lesinski694d0a72016-04-06 16:12:04 -0700572 printf(" uses-feature%s: name='%s'", (required ? "" : "-not-required"),
Adam Lesinski2c72b682014-06-24 09:56:01 -0700573 ResTable::normalizeForOutput(featureName.string()).string());
Adam Lesinski694d0a72016-04-06 16:12:04 -0700574
575 if (version > 0) {
576 printf(" version='%d'", version);
577 }
578 printf("\n");
Adam Lesinski2c72b682014-06-24 09:56:01 -0700579 }
580
581 const size_t numImpliedFeatures =
582 (impliedFeatures != NULL) ? impliedFeatures->size() : 0;
583 for (size_t i = 0; i < numImpliedFeatures; i++) {
584 const ImpliedFeature& impliedFeature = impliedFeatures->valueAt(i);
585 if (grp.features.indexOfKey(impliedFeature.name) >= 0) {
586 // The feature is explicitly set, no need to use implied
587 // definition.
588 continue;
589 }
590
591 String8 printableFeatureName(ResTable::normalizeForOutput(
592 impliedFeature.name.string()));
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800593 const char* sdk23Suffix = impliedFeature.impliedBySdk23 ? "-sdk-23" : "";
594
595 printf(" uses-feature%s: name='%s'\n", sdk23Suffix, printableFeatureName.string());
596 printf(" uses-implied-feature%s: name='%s' reason='", sdk23Suffix,
597 printableFeatureName.string());
Adam Lesinski2c72b682014-06-24 09:56:01 -0700598 const size_t numReasons = impliedFeature.reasons.size();
599 for (size_t j = 0; j < numReasons; j++) {
600 printf("%s", impliedFeature.reasons[j].string());
601 if (j + 2 < numReasons) {
602 printf(", ");
603 } else if (j + 1 < numReasons) {
604 printf(", and ");
605 }
606 }
607 printf("'\n");
608 }
609}
610
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800611static void printFeatureGroup(const FeatureGroup& grp) {
612 printFeatureGroupImpl(grp, NULL);
613}
614
615static void printDefaultFeatureGroup(const FeatureGroup& grp,
616 const KeyedVector<String8, ImpliedFeature>& impliedFeatures) {
617 printFeatureGroupImpl(grp, &impliedFeatures);
618}
619
Adam Lesinski2c72b682014-06-24 09:56:01 -0700620static void addParentFeatures(FeatureGroup* grp, const String8& name) {
621 if (name == "android.hardware.camera.autofocus" ||
622 name == "android.hardware.camera.flash") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700623 grp->features.add(String8("android.hardware.camera"), Feature(true));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700624 } else if (name == "android.hardware.location.gps" ||
625 name == "android.hardware.location.network") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700626 grp->features.add(String8("android.hardware.location"), Feature(true));
Adam Lesinskica955a42016-08-01 16:44:29 -0700627 } else if (name == "android.hardware.faketouch.multitouch") {
628 grp->features.add(String8("android.hardware.faketouch"), Feature(true));
629 } else if (name == "android.hardware.faketouch.multitouch.distinct" ||
630 name == "android.hardware.faketouch.multitouch.jazzhands") {
631 grp->features.add(String8("android.hardware.faketouch.multitouch"), Feature(true));
632 grp->features.add(String8("android.hardware.faketouch"), Feature(true));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700633 } else if (name == "android.hardware.touchscreen.multitouch") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700634 grp->features.add(String8("android.hardware.touchscreen"), Feature(true));
Adam Lesinskica955a42016-08-01 16:44:29 -0700635 } else if (name == "android.hardware.touchscreen.multitouch.distinct" ||
636 name == "android.hardware.touchscreen.multitouch.jazzhands") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700637 grp->features.add(String8("android.hardware.touchscreen.multitouch"), Feature(true));
638 grp->features.add(String8("android.hardware.touchscreen"), Feature(true));
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700639 } else if (name == "android.hardware.opengles.aep") {
640 const int openGLESVersion31 = 0x00030001;
641 if (openGLESVersion31 > grp->openGLESVersion) {
642 grp->openGLESVersion = openGLESVersion31;
643 }
Adam Lesinski2c72b682014-06-24 09:56:01 -0700644 }
645}
646
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800647static void addImpliedFeaturesForPermission(const int targetSdk, const String8& name,
648 KeyedVector<String8, ImpliedFeature>* impliedFeatures,
649 bool impliedBySdk23Permission) {
650 if (name == "android.permission.CAMERA") {
651 addImpliedFeature(impliedFeatures, "android.hardware.camera",
Adam Lesinski43158772015-11-11 15:13:55 -0800652 String8::format("requested %s permission", name.string()),
653 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800654 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
Adam Lesinski43158772015-11-11 15:13:55 -0800655 if (targetSdk < SDK_LOLLIPOP) {
656 addImpliedFeature(impliedFeatures, "android.hardware.location.gps",
657 String8::format("requested %s permission", name.string()),
658 impliedBySdk23Permission);
659 addImpliedFeature(impliedFeatures, "android.hardware.location.gps",
660 String8::format("targetSdkVersion < %d", SDK_LOLLIPOP),
661 impliedBySdk23Permission);
662 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800663 addImpliedFeature(impliedFeatures, "android.hardware.location",
Adam Lesinski43158772015-11-11 15:13:55 -0800664 String8::format("requested %s permission", name.string()),
665 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800666 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
Adam Lesinski43158772015-11-11 15:13:55 -0800667 if (targetSdk < SDK_LOLLIPOP) {
668 addImpliedFeature(impliedFeatures, "android.hardware.location.network",
669 String8::format("requested %s permission", name.string()),
670 impliedBySdk23Permission);
671 addImpliedFeature(impliedFeatures, "android.hardware.location.network",
672 String8::format("targetSdkVersion < %d", SDK_LOLLIPOP),
673 impliedBySdk23Permission);
674 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800675 addImpliedFeature(impliedFeatures, "android.hardware.location",
Adam Lesinski43158772015-11-11 15:13:55 -0800676 String8::format("requested %s permission", name.string()),
677 impliedBySdk23Permission);
678 } else if (name == "android.permission.ACCESS_MOCK_LOCATION" ||
679 name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800680 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
681 addImpliedFeature(impliedFeatures, "android.hardware.location",
Adam Lesinski43158772015-11-11 15:13:55 -0800682 String8::format("requested %s permission", name.string()),
683 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800684 } else if (name == "android.permission.BLUETOOTH" ||
685 name == "android.permission.BLUETOOTH_ADMIN") {
Adam Lesinski43158772015-11-11 15:13:55 -0800686 if (targetSdk > SDK_DONUT) {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800687 addImpliedFeature(impliedFeatures, "android.hardware.bluetooth",
Adam Lesinski43158772015-11-11 15:13:55 -0800688 String8::format("requested %s permission", name.string()),
689 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800690 addImpliedFeature(impliedFeatures, "android.hardware.bluetooth",
Adam Lesinski43158772015-11-11 15:13:55 -0800691 String8::format("targetSdkVersion > %d", SDK_DONUT),
692 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800693 }
694 } else if (name == "android.permission.RECORD_AUDIO") {
695 addImpliedFeature(impliedFeatures, "android.hardware.microphone",
Adam Lesinski43158772015-11-11 15:13:55 -0800696 String8::format("requested %s permission", name.string()),
697 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800698 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
699 name == "android.permission.CHANGE_WIFI_STATE" ||
700 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
701 addImpliedFeature(impliedFeatures, "android.hardware.wifi",
Adam Lesinski43158772015-11-11 15:13:55 -0800702 String8::format("requested %s permission", name.string()),
703 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800704 } else if (name == "android.permission.CALL_PHONE" ||
705 name == "android.permission.CALL_PRIVILEGED" ||
706 name == "android.permission.MODIFY_PHONE_STATE" ||
707 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
708 name == "android.permission.READ_SMS" ||
709 name == "android.permission.RECEIVE_SMS" ||
710 name == "android.permission.RECEIVE_MMS" ||
711 name == "android.permission.RECEIVE_WAP_PUSH" ||
712 name == "android.permission.SEND_SMS" ||
713 name == "android.permission.WRITE_APN_SETTINGS" ||
714 name == "android.permission.WRITE_SMS") {
715 addImpliedFeature(impliedFeatures, "android.hardware.telephony",
Adam Lesinski43158772015-11-11 15:13:55 -0800716 String8("requested a telephony permission"),
717 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800718 }
719}
720
Adam Lesinski282e1812014-01-23 18:17:42 -0800721/*
722 * Handle the "dump" command, to extract select data from an archive.
723 */
724extern char CONSOLE_DATA[2925]; // see EOF
725int doDump(Bundle* bundle)
726{
727 status_t result = UNKNOWN_ERROR;
Adam Lesinski282e1812014-01-23 18:17:42 -0800728
729 if (bundle->getFileSpecCount() < 1) {
730 fprintf(stderr, "ERROR: no dump option specified\n");
731 return 1;
732 }
733
734 if (bundle->getFileSpecCount() < 2) {
735 fprintf(stderr, "ERROR: no dump file specified\n");
736 return 1;
737 }
738
739 const char* option = bundle->getFileSpecEntry(0);
740 const char* filename = bundle->getFileSpecEntry(1);
741
742 AssetManager assets;
Narayan Kamathf85e41f2014-01-24 14:14:30 +0000743 int32_t assetsCookie;
Adam Lesinski282e1812014-01-23 18:17:42 -0800744
Donald Chaid1ac6e12017-10-12 21:00:45 -0700745 // Add any dependencies passed in.
Adam Lesinski57fe4832017-05-10 15:42:22 -0700746 for (size_t i = 0; i < bundle->getPackageIncludes().size(); i++) {
747 const String8& assetPath = bundle->getPackageIncludes()[i];
748 if (!assets.addAssetPath(assetPath, NULL)) {
749 fprintf(stderr, "ERROR: included asset path %s could not be loaded\n", assetPath.string());
750 return 1;
751 }
752 }
753
Donald Chaid1ac6e12017-10-12 21:00:45 -0700754 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
755 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
756 return 1;
757 }
758
Adam Lesinski282e1812014-01-23 18:17:42 -0800759 // Make a dummy config for retrieving resources... we need to supply
760 // non-default values for some configs so that we can retrieve resources
761 // in the app that don't have a default. The most important of these is
762 // the API version because key resources like icons will have an implicit
763 // version if they are using newer config types like density.
764 ResTable_config config;
Narayan Kamath91447d82014-01-21 15:32:36 +0000765 memset(&config, 0, sizeof(ResTable_config));
Adam Lesinski282e1812014-01-23 18:17:42 -0800766 config.language[0] = 'e';
767 config.language[1] = 'n';
768 config.country[0] = 'U';
769 config.country[1] = 'S';
770 config.orientation = ResTable_config::ORIENTATION_PORT;
771 config.density = ResTable_config::DENSITY_MEDIUM;
772 config.sdkVersion = 10000; // Very high.
773 config.screenWidthDp = 320;
774 config.screenHeightDp = 480;
775 config.smallestScreenWidthDp = 320;
Adam Lesinskic2dea8d2014-08-04 16:40:41 -0700776 config.screenLayout |= ResTable_config::SCREENSIZE_NORMAL;
Adam Lesinski282e1812014-01-23 18:17:42 -0800777 assets.setConfiguration(config);
778
779 const ResTable& res = assets.getResources(false);
Dan Albert68001652014-09-09 09:51:01 -0700780 if (res.getError() != NO_ERROR) {
Adam Lesinski25e9d552014-05-19 15:01:43 -0700781 fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
Adam Lesinski63e646e2014-07-30 11:40:39 -0700782 return 1;
Adam Lesinski282e1812014-01-23 18:17:42 -0800783 }
784
Adam Lesinski694d0a72016-04-06 16:12:04 -0700785 // Source for AndroidManifest.xml
Adam Lesinski10de3af12016-07-13 10:14:03 -0700786 const String8 manifestFile("AndroidManifest.xml");
Adam Lesinski694d0a72016-04-06 16:12:04 -0700787
Adam Lesinski2cb761e2014-08-15 13:59:02 -0700788 // The dynamicRefTable can be null if there are no resources for this asset cookie.
789 // This fine.
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700790 auto noop_destructor = [](const DynamicRefTable* /*ref_table */) { };
791 auto dynamicRefTable = std::shared_ptr<const DynamicRefTable>(
792 res.getDynamicRefTableForCookie(assetsCookie), noop_destructor);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700793
794 Asset* asset = NULL;
795
Adam Lesinski282e1812014-01-23 18:17:42 -0800796 if (strcmp("resources", option) == 0) {
Elliott Hughesba3fe562015-08-12 14:49:53 -0700797#ifndef __ANDROID__
Adam Lesinski282e1812014-01-23 18:17:42 -0800798 res.print(bundle->getValues());
799#endif
800
801 } else if (strcmp("strings", option) == 0) {
802 const ResStringPool* pool = res.getTableStringBlock(0);
803 printStringPool(pool);
804
805 } else if (strcmp("xmltree", option) == 0) {
806 if (bundle->getFileSpecCount() < 3) {
807 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
808 goto bail;
809 }
810
811 for (int i=2; i<bundle->getFileSpecCount(); i++) {
812 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700813 ResXMLTree tree(dynamicRefTable);
814 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800815 if (asset == NULL) {
Adam Lesinskifcb5f7b2016-11-02 13:17:10 -0700816 fprintf(stderr, "ERROR: dump failed because resource %s not found\n", resname);
Adam Lesinski282e1812014-01-23 18:17:42 -0800817 goto bail;
818 }
819
820 if (tree.setTo(asset->getBuffer(true),
821 asset->getLength()) != NO_ERROR) {
822 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
823 goto bail;
824 }
825 tree.restart();
826 printXMLBlock(&tree);
827 tree.uninit();
828 delete asset;
829 asset = NULL;
830 }
831
832 } else if (strcmp("xmlstrings", option) == 0) {
833 if (bundle->getFileSpecCount() < 3) {
834 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
835 goto bail;
836 }
837
838 for (int i=2; i<bundle->getFileSpecCount(); i++) {
839 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700840 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800841 if (asset == NULL) {
842 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
843 goto bail;
844 }
845
Adam Lesinski63e646e2014-07-30 11:40:39 -0700846 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800847 if (tree.setTo(asset->getBuffer(true),
848 asset->getLength()) != NO_ERROR) {
849 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
850 goto bail;
851 }
852 printStringPool(&tree.getStrings());
853 delete asset;
854 asset = NULL;
855 }
856
857 } else {
Adam Lesinski63e646e2014-07-30 11:40:39 -0700858 asset = assets.openNonAsset(assetsCookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800859 if (asset == NULL) {
860 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
861 goto bail;
862 }
863
Adam Lesinski63e646e2014-07-30 11:40:39 -0700864 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800865 if (tree.setTo(asset->getBuffer(true),
866 asset->getLength()) != NO_ERROR) {
867 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
868 goto bail;
869 }
870 tree.restart();
871
872 if (strcmp("permissions", option) == 0) {
873 size_t len;
874 ResXMLTree::event_code_t code;
875 int depth = 0;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800876 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
877 code != ResXMLTree::BAD_DOCUMENT) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800878 if (code == ResXMLTree::END_TAG) {
879 depth--;
880 continue;
881 }
882 if (code != ResXMLTree::START_TAG) {
883 continue;
884 }
885 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700886 const char16_t* ctag16 = tree.getElementName(&len);
887 if (ctag16 == NULL) {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700888 SourcePos(manifestFile, tree.getLineNumber()).error(
889 "ERROR: failed to get XML element name (bad string pool)");
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700890 goto bail;
891 }
892 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800893 //printf("Depth %d tag %s\n", depth, tag.string());
894 if (depth == 1) {
895 if (tag != "manifest") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700896 SourcePos(manifestFile, tree.getLineNumber()).error(
897 "ERROR: manifest does not start with <manifest> tag");
Adam Lesinski282e1812014-01-23 18:17:42 -0800898 goto bail;
899 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700900 String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -0700901 printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800902 } else if (depth == 2) {
903 if (tag == "permission") {
904 String8 error;
905 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
906 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700907 SourcePos(manifestFile, tree.getLineNumber()).error(
908 "ERROR getting 'android:name': %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800909 goto bail;
910 }
911
912 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700913 SourcePos(manifestFile, tree.getLineNumber()).error(
914 "ERROR: missing 'android:name' for permission");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800915 goto bail;
916 }
917 printf("permission: %s\n",
918 ResTable::normalizeForOutput(name.string()).string());
919 } else if (tag == "uses-permission") {
920 String8 error;
921 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
922 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700923 SourcePos(manifestFile, tree.getLineNumber()).error(
924 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800925 goto bail;
926 }
927
928 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700929 SourcePos(manifestFile, tree.getLineNumber()).error(
930 "ERROR: missing 'android:name' for uses-permission");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800931 goto bail;
932 }
933 printUsesPermission(name,
934 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
935 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
936 } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
937 String8 error;
938 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
939 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700940 SourcePos(manifestFile, tree.getLineNumber()).error(
941 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800942 goto bail;
943 }
944
945 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700946 SourcePos(manifestFile, tree.getLineNumber()).error(
947 "ERROR: missing 'android:name' for uses-permission-sdk-23");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800948 goto bail;
949 }
950 printUsesPermissionSdk23(
951 name,
952 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
Adam Lesinski282e1812014-01-23 18:17:42 -0800953 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800954 }
955 }
956 } else if (strcmp("badging", option) == 0) {
957 Vector<String8> locales;
958 res.getLocales(&locales);
959
960 Vector<ResTable_config> configs;
961 res.getConfigurations(&configs);
962 SortedVector<int> densities;
963 const size_t NC = configs.size();
964 for (size_t i=0; i<NC; i++) {
965 int dens = configs[i].density;
966 if (dens == 0) {
967 dens = 160;
968 }
969 densities.add(dens);
970 }
971
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;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001031 bool hasBindAccessibilityServicePermission = false;
1032 bool hasBindPrintServicePermission = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001033 bool hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001034 bool hasRequiredSafAttributes = false;
1035 bool hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001036 bool hasBindDreamServicePermission = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001037
1038 // These two implement the implicit permissions that are granted
1039 // to pre-1.6 applications.
1040 bool hasWriteExternalStoragePermission = false;
Adam Lesinski2386df22016-12-28 15:08:58 -05001041 int32_t writeExternalStoragePermissionMaxSdkVersion = -1;
Adam Lesinski282e1812014-01-23 18:17:42 -08001042 bool hasReadPhoneStatePermission = false;
1043
1044 // If an app requests write storage, they will also get read storage.
1045 bool hasReadExternalStoragePermission = false;
1046
1047 // Implement transition to read and write call log.
1048 bool hasReadContactsPermission = false;
1049 bool hasWriteContactsPermission = false;
1050 bool hasReadCallLogPermission = false;
1051 bool hasWriteCallLogPermission = false;
1052
Adam Lesinskie47fd122014-08-15 22:25:36 -07001053 // If an app declares itself as multiArch, we report the
1054 // native libraries differently.
1055 bool hasMultiArch = false;
1056
Adam Lesinski282e1812014-01-23 18:17:42 -08001057 // This next group of variables is used to implement a group of
1058 // backward-compatibility heuristics necessitated by the addition of
1059 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
1060 // heuristic is "if an app requests a permission but doesn't explicitly
1061 // request the corresponding <uses-feature>, presume it's there anyway".
Adam Lesinski2c72b682014-06-24 09:56:01 -07001062
Adam Lesinski282e1812014-01-23 18:17:42 -08001063 // 2.2 also added some other features that apps can request, but that
1064 // have no corresponding permission, so we cannot implement any
1065 // back-compatibility heuristic for them. The below are thus unnecessary
1066 // (but are retained here for documentary purposes.)
1067 //bool specCompassFeature = false;
1068 //bool specAccelerometerFeature = false;
1069 //bool specProximityFeature = false;
1070 //bool specAmbientLightFeature = false;
1071 //bool specLiveWallpaperFeature = false;
1072
1073 int targetSdk = 0;
1074 int smallScreen = 1;
1075 int normalScreen = 1;
1076 int largeScreen = 1;
1077 int xlargeScreen = 1;
1078 int anyDensity = 1;
1079 int requiresSmallestWidthDp = 0;
1080 int compatibleWidthLimitDp = 0;
1081 int largestWidthLimitDp = 0;
1082 String8 pkg;
1083 String8 activityName;
1084 String8 activityLabel;
1085 String8 activityIcon;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001086 String8 activityBanner;
Adam Lesinski282e1812014-01-23 18:17:42 -08001087 String8 receiverName;
1088 String8 serviceName;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001089 Vector<String8> supportedInput;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001090
1091 FeatureGroup commonFeatures;
1092 Vector<FeatureGroup> featureGroups;
1093 KeyedVector<String8, ImpliedFeature> impliedFeatures;
1094
Ryan Mitchell424db432021-05-03 11:42:52 -07001095 {
1096 int curDepth = 0;
1097 ResXMLParser::ResXMLPosition initialPos;
1098 tree.getPosition(&initialPos);
1099
1100 // Find all of the "uses-sdk" tags within the "manifest" tag.
1101 std::vector<ResXMLParser::ResXMLPosition> usesSdkTagPositions;
1102 ResXMLParser::ResXMLPosition curPos;
1103 while ((code = tree.next()) != ResXMLTree::END_DOCUMENT &&
1104 code != ResXMLTree::BAD_DOCUMENT) {
1105 if (code == ResXMLTree::END_TAG) {
1106 curDepth--;
1107 continue;
1108 }
1109 if (code == ResXMLTree::START_TAG) {
1110 curDepth++;
1111 }
1112 const char16_t* ctag16 = tree.getElementName(&len);
1113 if (ctag16 == NULL || String8(ctag16) != "uses-sdk" || curDepth != 2) {
1114 continue;
1115 }
1116
1117 tree.getPosition(&curPos);
1118 usesSdkTagPositions.emplace_back(curPos);
1119 }
1120
1121 // Skip all "uses-sdk" tags besides the very last tag. The android runtime only uses
1122 // the attribute values from the last defined tag.
Ryan Mitchell957168e2021-05-10 11:46:49 -07001123 for (size_t i = 1; i < usesSdkTagPositions.size(); i++) {
1124 tagsToSkip.emplace_back(usesSdkTagPositions[i - 1]);
Ryan Mitchell424db432021-05-03 11:42:52 -07001125 }
1126
1127 // Reset the position before parsing.
1128 tree.setPosition(initialPos);
1129 }
1130
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001131 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
1132 code != ResXMLTree::BAD_DOCUMENT) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001133 if (code == ResXMLTree::END_TAG) {
1134 depth--;
1135 if (depth < 2) {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001136 if (withinSupportsInput && !supportedInput.isEmpty()) {
1137 printf("supports-input: '");
1138 const size_t N = supportedInput.size();
1139 for (size_t i=0; i<N; i++) {
Maurice Chu2675f762013-10-22 17:33:11 -07001140 printf("%s", ResTable::normalizeForOutput(
1141 supportedInput[i].string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001142 if (i != N - 1) {
1143 printf("' '");
1144 } else {
1145 printf("'\n");
1146 }
1147 }
1148 supportedInput.clear();
1149 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001150 withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001151 withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001152 withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001153 } else if (depth < 3) {
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001154 if (withinActivity && isMainActivity) {
Maurice Chu2675f762013-10-22 17:33:11 -07001155 String8 aName(getComponentName(pkg, activityName));
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001156 if (isLauncherActivity) {
1157 printf("launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001158 if (aName.length() > 0) {
1159 printf(" name='%s' ",
1160 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001161 }
1162 printf(" label='%s' icon='%s'\n",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001163 ResTable::normalizeForOutput(activityLabel.string())
1164 .string(),
1165 ResTable::normalizeForOutput(activityIcon.string())
1166 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001167 }
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001168 if (isLeanbackLauncherActivity) {
1169 printf("leanback-launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001170 if (aName.length() > 0) {
1171 printf(" name='%s' ",
1172 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001173 }
1174 printf(" label='%s' icon='%s' banner='%s'\n",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001175 ResTable::normalizeForOutput(activityLabel.string())
1176 .string(),
1177 ResTable::normalizeForOutput(activityIcon.string())
1178 .string(),
1179 ResTable::normalizeForOutput(activityBanner.string())
1180 .string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001181 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001182 }
1183 if (!hasIntentFilter) {
1184 hasOtherActivities |= withinActivity;
1185 hasOtherReceivers |= withinReceiver;
1186 hasOtherServices |= withinService;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001187 } else {
1188 if (withinService) {
1189 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
1190 hasBindNfcServicePermission);
1191 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
1192 hasBindNfcServicePermission);
1193 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001194 }
1195 withinActivity = false;
1196 withinService = false;
1197 withinReceiver = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001198 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001199 hasIntentFilter = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001200 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001201 } else if (depth < 4) {
1202 if (withinIntentFilter) {
1203 if (withinActivity) {
1204 hasMainActivity |= actMainActivity;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001205 hasLauncher |= catLauncher;
1206 hasCameraActivity |= actCamera;
1207 hasCameraSecureActivity |= actCameraSecure;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001208 hasOtherActivities |=
1209 !actMainActivity && !actCamera && !actCameraSecure;
Adam Lesinski282e1812014-01-23 18:17:42 -08001210 } else if (withinReceiver) {
1211 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001212 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
1213 hasBindDeviceAdminPermission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001214 hasOtherReceivers |=
1215 (!actWidgetReceivers && !actDeviceAdminEnabled);
Adam Lesinski282e1812014-01-23 18:17:42 -08001216 } else if (withinService) {
1217 hasImeService |= actImeService;
1218 hasWallpaperService |= actWallpaperService;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001219 hasAccessibilityService |= (actAccessibilityService &&
1220 hasBindAccessibilityServicePermission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001221 hasPrintService |=
1222 (actPrintService && hasBindPrintServicePermission);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001223 hasNotificationListenerService |= actNotificationListenerService &&
1224 hasBindNotificationListenerServicePermission;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001225 hasDreamService |= actDreamService && hasBindDreamServicePermission;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001226 hasOtherServices |= (!actImeService && !actWallpaperService &&
Adam Lesinski94fc9122013-09-30 17:16:09 -07001227 !actAccessibilityService && !actPrintService &&
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001228 !actHostApduService && !actOffHostApduService &&
1229 !actNotificationListenerService);
1230 } else if (withinProvider) {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001231 hasDocumentsProvider |=
1232 actDocumentsProvider && hasRequiredSafAttributes;
Adam Lesinski282e1812014-01-23 18:17:42 -08001233 }
1234 }
1235 withinIntentFilter = false;
1236 }
1237 continue;
1238 }
1239 if (code != ResXMLTree::START_TAG) {
1240 continue;
1241 }
Ryan Mitchell424db432021-05-03 11:42:52 -07001242
Adam Lesinski282e1812014-01-23 18:17:42 -08001243 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001244
Ryan Mitchell424db432021-05-03 11:42:52 -07001245 // If this tag should be skipped, skip to the end of this tag.
1246 ResXMLParser::ResXMLPosition curPos;
1247 tree.getPosition(&curPos);
1248 if (std::find(tagsToSkip.begin(), tagsToSkip.end(), curPos) != tagsToSkip.end()) {
1249 const int breakDepth = depth - 1;
1250 while ((code = tree.next()) != ResXMLTree::END_DOCUMENT &&
1251 code != ResXMLTree::BAD_DOCUMENT) {
1252 if (code == ResXMLTree::END_TAG && --depth == breakDepth) {
1253 break;
1254 } else if (code == ResXMLTree::START_TAG) {
1255 depth++;
1256 }
1257 }
1258 continue;
1259 }
1260
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001261 const char16_t* ctag16 = tree.getElementName(&len);
1262 if (ctag16 == NULL) {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001263 SourcePos(manifestFile, tree.getLineNumber()).error(
1264 "ERROR: failed to get XML element name (bad string pool)");
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001265 goto bail;
1266 }
1267 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -08001268 //printf("Depth %d, %s\n", depth, tag.string());
1269 if (depth == 1) {
1270 if (tag != "manifest") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001271 SourcePos(manifestFile, tree.getLineNumber()).error(
1272 "ERROR: manifest does not start with <manifest> tag");
Adam Lesinski282e1812014-01-23 18:17:42 -08001273 goto bail;
1274 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001275 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -07001276 printf("package: name='%s' ",
1277 ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001278 int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR,
1279 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001280 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001281 SourcePos(manifestFile, tree.getLineNumber()).error(
1282 "ERROR getting 'android:versionCode' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001283 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001284 goto bail;
1285 }
1286 if (versionCode > 0) {
1287 printf("versionCode='%d' ", versionCode);
1288 } else {
1289 printf("versionCode='' ");
1290 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001291 String8 versionName = AaptXml::getResolvedAttribute(res, tree,
1292 VERSION_NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001293 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001294 SourcePos(manifestFile, tree.getLineNumber()).error(
1295 "ERROR getting 'android:versionName' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001296 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001297 goto bail;
1298 }
Adam Lesinski25d35a92014-08-11 09:41:56 -07001299 printf("versionName='%s'",
Maurice Chu2675f762013-10-22 17:33:11 -07001300 ResTable::normalizeForOutput(versionName.string()).string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001301
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001302 String8 splitName = AaptXml::getAttribute(tree, NULL, "split");
Adam Lesinski25d35a92014-08-11 09:41:56 -07001303 if (!splitName.isEmpty()) {
1304 printf(" split='%s'", ResTable::normalizeForOutput(
1305 splitName.string()).string());
1306 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001307
Alan Viverette11be9312017-11-09 15:41:44 -05001308 String8 platformBuildVersionName = AaptXml::getAttribute(tree, NULL,
Adam Lesinski5283fab2014-08-29 11:23:55 -07001309 "platformBuildVersionName");
Alan Viverette11be9312017-11-09 15:41:44 -05001310 if (platformBuildVersionName != "") {
1311 printf(" platformBuildVersionName='%s'", platformBuildVersionName.string());
1312 }
1313
1314 String8 platformBuildVersionCode = AaptXml::getAttribute(tree, NULL,
1315 "platformBuildVersionCode");
1316 if (platformBuildVersionCode != "") {
1317 printf(" platformBuildVersionCode='%s'", platformBuildVersionCode.string());
1318 }
1319
1320 int32_t compileSdkVersion = AaptXml::getIntegerAttribute(tree,
1321 COMPILE_SDK_VERSION_ATTR, &error);
1322 if (error != "") {
1323 SourcePos(manifestFile, tree.getLineNumber()).error(
1324 "ERROR getting 'android:compileSdkVersion' attribute: %s",
1325 error.string());
1326 goto bail;
1327 }
1328 if (compileSdkVersion > 0) {
1329 printf(" compileSdkVersion='%d'", compileSdkVersion);
1330 }
1331
1332 String8 compileSdkVersionCodename = AaptXml::getResolvedAttribute(res, tree,
1333 COMPILE_SDK_VERSION_CODENAME_ATTR, &error);
1334 if (compileSdkVersionCodename != "") {
1335 printf(" compileSdkVersionCodename='%s'", ResTable::normalizeForOutput(
1336 compileSdkVersionCodename.string()).string());
1337 }
1338
Adam Lesinski25d35a92014-08-11 09:41:56 -07001339 printf("\n");
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001340
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001341 int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
1342 INSTALL_LOCATION_ATTR, &error);
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001343 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001344 SourcePos(manifestFile, tree.getLineNumber()).error(
1345 "ERROR getting 'android:installLocation' attribute: %s",
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001346 error.string());
1347 goto bail;
1348 }
1349
1350 if (installLocation >= 0) {
1351 printf("install-location:'");
1352 switch (installLocation) {
1353 case 0:
1354 printf("auto");
1355 break;
1356 case 1:
1357 printf("internalOnly");
1358 break;
1359 case 2:
1360 printf("preferExternal");
1361 break;
1362 default:
1363 fprintf(stderr, "Invalid installLocation %d\n", installLocation);
1364 goto bail;
1365 }
1366 printf("'\n");
1367 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001368 } else if (depth == 2) {
1369 withinApplication = false;
1370 if (tag == "application") {
1371 withinApplication = true;
1372
1373 String8 label;
1374 const size_t NL = locales.size();
1375 for (size_t i=0; i<NL; i++) {
1376 const char* localeStr = locales[i].string();
Adam Lesinskia77685f2016-10-03 16:26:28 -07001377 assets.setConfiguration(config, localeStr != NULL ? localeStr : "");
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001378 String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1379 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001380 if (llabel != "") {
1381 if (localeStr == NULL || strlen(localeStr) == 0) {
1382 label = llabel;
Maurice Chu2675f762013-10-22 17:33:11 -07001383 printf("application-label:'%s'\n",
1384 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001385 } else {
1386 if (label == "") {
1387 label = llabel;
1388 }
1389 printf("application-label-%s:'%s'\n", localeStr,
Maurice Chu2675f762013-10-22 17:33:11 -07001390 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001391 }
1392 }
1393 }
1394
1395 ResTable_config tmpConfig = config;
1396 const size_t ND = densities.size();
1397 for (size_t i=0; i<ND; i++) {
1398 tmpConfig.density = densities[i];
1399 assets.setConfiguration(tmpConfig);
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001400 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1401 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001402 if (icon != "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001403 printf("application-icon-%d:'%s'\n", densities[i],
1404 ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001405 }
1406 }
1407 assets.setConfiguration(config);
1408
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001409 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001410 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001411 SourcePos(manifestFile, tree.getLineNumber()).error(
1412 "ERROR getting 'android:icon' attribute: %s", error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001413 goto bail;
1414 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001415 int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0,
1416 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001417 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001418 SourcePos(manifestFile, tree.getLineNumber()).error(
1419 "ERROR getting 'android:testOnly' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001420 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001421 goto bail;
1422 }
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001423
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001424 String8 banner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1425 &error);
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001426 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001427 SourcePos(manifestFile, tree.getLineNumber()).error(
1428 "ERROR getting 'android:banner' attribute: %s", error.string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001429 goto bail;
1430 }
Maurice Chu2675f762013-10-22 17:33:11 -07001431 printf("application: label='%s' ",
1432 ResTable::normalizeForOutput(label.string()).string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001433 printf("icon='%s'", ResTable::normalizeForOutput(icon.string()).string());
1434 if (banner != "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001435 printf(" banner='%s'",
1436 ResTable::normalizeForOutput(banner.string()).string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001437 }
1438 printf("\n");
Adam Lesinski282e1812014-01-23 18:17:42 -08001439 if (testOnly != 0) {
1440 printf("testOnly='%d'\n", testOnly);
1441 }
1442
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001443 int32_t isGame = AaptXml::getResolvedIntegerAttribute(res, tree,
1444 ISGAME_ATTR, 0, &error);
1445 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001446 SourcePos(manifestFile, tree.getLineNumber()).error(
1447 "ERROR getting 'android:isGame' attribute: %s", error.string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001448 goto bail;
1449 }
1450 if (isGame != 0) {
1451 printf("application-isGame\n");
1452 }
1453
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001454 int32_t debuggable = AaptXml::getResolvedIntegerAttribute(res, tree,
1455 DEBUGGABLE_ATTR, 0, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001456 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001457 SourcePos(manifestFile, tree.getLineNumber()).error(
1458 "ERROR getting 'android:debuggable' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001459 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001460 goto bail;
1461 }
1462 if (debuggable != 0) {
1463 printf("application-debuggable\n");
1464 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07001465
1466 // We must search by name because the multiArch flag hasn't been API
1467 // frozen yet.
1468 int32_t multiArchIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
1469 "multiArch");
1470 if (multiArchIndex >= 0) {
1471 Res_value value;
1472 if (tree.getAttributeValue(multiArchIndex, &value) != NO_ERROR) {
1473 if (value.dataType >= Res_value::TYPE_FIRST_INT &&
1474 value.dataType <= Res_value::TYPE_LAST_INT) {
1475 hasMultiArch = value.data;
1476 }
1477 }
1478 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001479 } else if (tag == "uses-sdk") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001480 int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR,
1481 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001482 if (error != "") {
1483 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001484 String8 name = AaptXml::getResolvedAttribute(res, tree,
1485 MIN_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001486 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001487 SourcePos(manifestFile, tree.getLineNumber()).error(
1488 "ERROR getting 'android:minSdkVersion' attribute: %s",
Adam Lesinski282e1812014-01-23 18:17:42 -08001489 error.string());
1490 goto bail;
1491 }
1492 if (name == "Donut") targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001493 printf("sdkVersion:'%s'\n",
1494 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001495 } else if (code != -1) {
1496 targetSdk = code;
1497 printf("sdkVersion:'%d'\n", code);
1498 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001499 code = AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -08001500 if (code != -1) {
1501 printf("maxSdkVersion:'%d'\n", code);
1502 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001503 code = AaptXml::getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001504 if (error != "") {
1505 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001506 String8 name = AaptXml::getResolvedAttribute(res, tree,
1507 TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001508 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001509 SourcePos(manifestFile, tree.getLineNumber()).error(
1510 "ERROR getting 'android:targetSdkVersion' attribute: %s",
Adam Lesinski282e1812014-01-23 18:17:42 -08001511 error.string());
1512 goto bail;
1513 }
1514 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001515 printf("targetSdkVersion:'%s'\n",
1516 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001517 } else if (code != -1) {
1518 if (targetSdk < code) {
1519 targetSdk = code;
1520 }
1521 printf("targetSdkVersion:'%d'\n", code);
1522 }
1523 } else if (tag == "uses-configuration") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001524 int32_t reqTouchScreen = AaptXml::getIntegerAttribute(tree,
1525 REQ_TOUCH_SCREEN_ATTR, 0);
1526 int32_t reqKeyboardType = AaptXml::getIntegerAttribute(tree,
1527 REQ_KEYBOARD_TYPE_ATTR, 0);
1528 int32_t reqHardKeyboard = AaptXml::getIntegerAttribute(tree,
1529 REQ_HARD_KEYBOARD_ATTR, 0);
1530 int32_t reqNavigation = AaptXml::getIntegerAttribute(tree,
1531 REQ_NAVIGATION_ATTR, 0);
1532 int32_t reqFiveWayNav = AaptXml::getIntegerAttribute(tree,
1533 REQ_FIVE_WAY_NAV_ATTR, 0);
Adam Lesinski282e1812014-01-23 18:17:42 -08001534 printf("uses-configuration:");
1535 if (reqTouchScreen != 0) {
1536 printf(" reqTouchScreen='%d'", reqTouchScreen);
1537 }
1538 if (reqKeyboardType != 0) {
1539 printf(" reqKeyboardType='%d'", reqKeyboardType);
1540 }
1541 if (reqHardKeyboard != 0) {
1542 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1543 }
1544 if (reqNavigation != 0) {
1545 printf(" reqNavigation='%d'", reqNavigation);
1546 }
1547 if (reqFiveWayNav != 0) {
1548 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1549 }
1550 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001551 } else if (tag == "supports-input") {
1552 withinSupportsInput = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001553 } else if (tag == "supports-screens") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001554 smallScreen = AaptXml::getIntegerAttribute(tree,
1555 SMALL_SCREEN_ATTR, 1);
1556 normalScreen = AaptXml::getIntegerAttribute(tree,
1557 NORMAL_SCREEN_ATTR, 1);
1558 largeScreen = AaptXml::getIntegerAttribute(tree,
1559 LARGE_SCREEN_ATTR, 1);
1560 xlargeScreen = AaptXml::getIntegerAttribute(tree,
1561 XLARGE_SCREEN_ATTR, 1);
1562 anyDensity = AaptXml::getIntegerAttribute(tree,
1563 ANY_DENSITY_ATTR, 1);
1564 requiresSmallestWidthDp = AaptXml::getIntegerAttribute(tree,
1565 REQUIRES_SMALLEST_WIDTH_DP_ATTR, 0);
1566 compatibleWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1567 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, 0);
1568 largestWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1569 LARGEST_WIDTH_LIMIT_DP_ATTR, 0);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001570 } else if (tag == "feature-group") {
1571 withinFeatureGroup = true;
1572 FeatureGroup group;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001573 group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001574 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001575 SourcePos(manifestFile, tree.getLineNumber()).error(
1576 "ERROR getting 'android:label' attribute: %s", error.string());
Adam Lesinski2c72b682014-06-24 09:56:01 -07001577 goto bail;
1578 }
1579 featureGroups.add(group);
1580
Adam Lesinski282e1812014-01-23 18:17:42 -08001581 } else if (tag == "uses-feature") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001582 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001583 if (name != "" && error == "") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001584 const char* androidSchema =
1585 "http://schemas.android.com/apk/res/android";
Adam Lesinski282e1812014-01-23 18:17:42 -08001586
Adam Lesinski694d0a72016-04-06 16:12:04 -07001587 int32_t req = AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1,
1588 &error);
1589 if (error != "") {
1590 SourcePos(manifestFile, tree.getLineNumber()).error(
1591 "failed to read attribute 'android:required': %s",
1592 error.string());
1593 goto bail;
1594 }
1595
1596 int32_t version = AaptXml::getIntegerAttribute(tree, androidSchema,
1597 "version", 0, &error);
1598 if (error != "") {
1599 SourcePos(manifestFile, tree.getLineNumber()).error(
1600 "failed to read attribute 'android:version': %s",
1601 error.string());
1602 goto bail;
1603 }
1604
1605 commonFeatures.features.add(name, Feature(req != 0, version));
Adam Lesinski2c72b682014-06-24 09:56:01 -07001606 if (req) {
1607 addParentFeatures(&commonFeatures, name);
Adam Lesinski282e1812014-01-23 18:17:42 -08001608 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001609 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001610 int vers = AaptXml::getIntegerAttribute(tree,
Adam Lesinski282e1812014-01-23 18:17:42 -08001611 GL_ES_VERSION_ATTR, &error);
1612 if (error == "") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001613 if (vers > commonFeatures.openGLESVersion) {
1614 commonFeatures.openGLESVersion = vers;
1615 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001616 }
1617 }
1618 } else if (tag == "uses-permission") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001619 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001620 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001621 SourcePos(manifestFile, tree.getLineNumber()).error(
1622 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001623 goto bail;
1624 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001625
1626 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001627 SourcePos(manifestFile, tree.getLineNumber()).error(
1628 "ERROR: missing 'android:name' for uses-permission");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001629 goto bail;
1630 }
1631
1632 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, false);
1633
Adam Lesinski2386df22016-12-28 15:08:58 -05001634 const int32_t maxSdkVersion =
1635 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, -1);
Dianne Hackborncd154e92017-02-28 17:37:35 -08001636 const String8 requiredFeature = AaptXml::getAttribute(tree,
1637 REQUIRED_FEATURE_ATTR, &error);
1638 const String8 requiredNotFeature = AaptXml::getAttribute(tree,
1639 REQUIRED_NOT_FEATURE_ATTR, &error);
Adam Lesinski2386df22016-12-28 15:08:58 -05001640
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001641 if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1642 hasWriteExternalStoragePermission = true;
Adam Lesinski2386df22016-12-28 15:08:58 -05001643 writeExternalStoragePermissionMaxSdkVersion = maxSdkVersion;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001644 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1645 hasReadExternalStoragePermission = true;
1646 } else if (name == "android.permission.READ_PHONE_STATE") {
1647 hasReadPhoneStatePermission = true;
1648 } else if (name == "android.permission.READ_CONTACTS") {
1649 hasReadContactsPermission = true;
1650 } else if (name == "android.permission.WRITE_CONTACTS") {
1651 hasWriteContactsPermission = true;
1652 } else if (name == "android.permission.READ_CALL_LOG") {
1653 hasReadCallLogPermission = true;
1654 } else if (name == "android.permission.WRITE_CALL_LOG") {
1655 hasWriteCallLogPermission = true;
1656 }
1657
1658 printUsesPermission(name,
1659 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
Dianne Hackborncd154e92017-02-28 17:37:35 -08001660 maxSdkVersion, requiredFeature, requiredNotFeature);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001661
1662 } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
1663 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1664 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001665 SourcePos(manifestFile, tree.getLineNumber()).error(
1666 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001667 goto bail;
1668 }
1669
1670 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001671 SourcePos(manifestFile, tree.getLineNumber()).error(
1672 "ERROR: missing 'android:name' for uses-permission-sdk-23");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001673 goto bail;
1674 }
1675
1676 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, true);
1677
1678 printUsesPermissionSdk23(
1679 name, AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
1680
Adam Lesinski282e1812014-01-23 18:17:42 -08001681 } else if (tag == "uses-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001682 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001683 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001684 printf("uses-package:'%s'\n",
1685 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001686 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001687 SourcePos(manifestFile, tree.getLineNumber()).error(
1688 "ERROR getting 'android:name' attribute: %s", error.string());
1689 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001690 }
1691 } else if (tag == "original-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001692 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001693 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001694 printf("original-package:'%s'\n",
1695 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001696 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001697 SourcePos(manifestFile, tree.getLineNumber()).error(
1698 "ERROR getting 'android:name' attribute: %s", error.string());
1699 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001700 }
1701 } else if (tag == "supports-gl-texture") {
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("supports-gl-texture:'%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 == "compatible-screens") {
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001712 printCompatibleScreens(tree, &error);
1713 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001714 SourcePos(manifestFile, tree.getLineNumber()).error(
1715 "ERROR getting compatible screens: %s", error.string());
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001716 goto bail;
1717 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001718 depth--;
1719 } else if (tag == "package-verifier") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001720 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001721 if (name != "" && error == "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001722 String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR,
1723 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001724 if (publicKey != "" && error == "") {
1725 printf("package-verifier: name='%s' publicKey='%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001726 ResTable::normalizeForOutput(name.string()).string(),
1727 ResTable::normalizeForOutput(publicKey.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001728 }
1729 }
1730 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001731 } else if (depth == 3) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001732 withinActivity = false;
1733 withinReceiver = false;
1734 withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001735 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001736 hasIntentFilter = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001737 hasMetaHostPaymentCategory = false;
1738 hasMetaOffHostPaymentCategory = false;
1739 hasBindDeviceAdminPermission = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001740 hasBindAccessibilityServicePermission = false;
1741 hasBindPrintServicePermission = false;
1742 hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001743 hasRequiredSafAttributes = false;
1744 hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001745 hasBindDreamServicePermission = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001746 if (withinApplication) {
1747 if(tag == "activity") {
1748 withinActivity = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001749 activityName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001750 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001751 SourcePos(manifestFile, tree.getLineNumber()).error(
1752 "ERROR getting 'android:name' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001753 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001754 goto bail;
1755 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001756
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001757 activityLabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1758 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001759 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001760 SourcePos(manifestFile, tree.getLineNumber()).error(
1761 "ERROR getting 'android:label' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001762 error.string());
1763 goto bail;
1764 }
1765
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001766 activityIcon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1767 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001768 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001769 SourcePos(manifestFile, tree.getLineNumber()).error(
1770 "ERROR getting 'android:icon' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001771 error.string());
1772 goto bail;
1773 }
1774
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001775 activityBanner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1776 &error);
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001777 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001778 SourcePos(manifestFile, tree.getLineNumber()).error(
1779 "ERROR getting 'android:banner' attribute: %s",
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001780 error.string());
1781 goto bail;
1782 }
1783
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001784 int32_t orien = AaptXml::getResolvedIntegerAttribute(res, tree,
Michael Wrightec4fdec2013-09-06 16:50:52 -07001785 SCREEN_ORIENTATION_ATTR, &error);
1786 if (error == "") {
1787 if (orien == 0 || orien == 6 || orien == 8) {
1788 // Requests landscape, sensorLandscape, or reverseLandscape.
Adam Lesinski43158772015-11-11 15:13:55 -08001789 addImpliedFeature(
1790 &impliedFeatures, "android.hardware.screen.landscape",
1791 String8("one or more activities have specified a "
1792 "landscape orientation"),
1793 false);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001794 } else if (orien == 1 || orien == 7 || orien == 9) {
1795 // Requests portrait, sensorPortrait, or reversePortrait.
Adam Lesinski43158772015-11-11 15:13:55 -08001796 addImpliedFeature(
1797 &impliedFeatures, "android.hardware.screen.portrait",
1798 String8("one or more activities have specified a "
1799 "portrait orientation"),
1800 false);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001801 }
1802 }
1803 } else if (tag == "uses-library") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001804 String8 libraryName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001805 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001806 SourcePos(manifestFile, tree.getLineNumber()).error(
Michael Wrightec4fdec2013-09-06 16:50:52 -07001807 "ERROR getting 'android:name' attribute for uses-library"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001808 " %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001809 goto bail;
1810 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001811 int req = AaptXml::getIntegerAttribute(tree,
1812 REQUIRED_ATTR, 1);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001813 printf("uses-library%s:'%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001814 req ? "" : "-not-required", ResTable::normalizeForOutput(
1815 libraryName.string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001816 } else if (tag == "receiver") {
1817 withinReceiver = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001818 receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001819
1820 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001821 SourcePos(manifestFile, tree.getLineNumber()).error(
Michael Wrightec4fdec2013-09-06 16:50:52 -07001822 "ERROR getting 'android:name' attribute for receiver:"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001823 " %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001824 goto bail;
1825 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001826
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001827 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1828 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001829 if (error == "") {
1830 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1831 hasBindDeviceAdminPermission = true;
1832 }
1833 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001834 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001835 "ERROR getting 'android:permission' attribute for"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001836 " receiver '%s': %s",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001837 receiverName.string(), error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001838 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001839 } else if (tag == "service") {
1840 withinService = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001841 serviceName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001842
1843 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001844 SourcePos(manifestFile, tree.getLineNumber()).error(
1845 "ERROR getting 'android:name' attribute for "
1846 "service:%s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001847 goto bail;
1848 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001849
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001850 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1851 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001852 if (error == "") {
Yi Kongb172c312022-07-20 15:15:33 +08001853 if (permission ==
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001854 "android.permission.BIND_ACCESSIBILITY_SERVICE") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001855 hasBindAccessibilityServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001856 } else if (permission ==
1857 "android.permission.BIND_PRINT_SERVICE") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001858 hasBindPrintServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001859 } else if (permission ==
1860 "android.permission.BIND_NFC_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07001861 hasBindNfcServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001862 } else if (permission ==
1863 "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001864 hasBindNotificationListenerServicePermission = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001865 } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
1866 hasBindDreamServicePermission = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001867 }
1868 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001869 SourcePos(manifestFile, tree.getLineNumber()).error(
1870 "ERROR getting 'android:permission' attribute for "
1871 "service '%s': %s", serviceName.string(), error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001872 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001873 } else if (tag == "provider") {
1874 withinProvider = true;
1875
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001876 bool exported = AaptXml::getResolvedIntegerAttribute(res, tree,
1877 EXPORTED_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001878 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001879 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001880 "ERROR getting 'android:exported' attribute for provider:"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001881 " %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001882 goto bail;
1883 }
1884
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001885 bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute(
1886 res, tree, GRANT_URI_PERMISSIONS_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001887 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001888 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001889 "ERROR getting 'android:grantUriPermissions' attribute for "
Adam Lesinski10de3af12016-07-13 10:14:03 -07001890 "provider: %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001891 goto bail;
1892 }
1893
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001894 String8 permission = AaptXml::getResolvedAttribute(res, tree,
1895 PERMISSION_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001896 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001897 SourcePos(manifestFile, tree.getLineNumber()).error(
1898 "ERROR getting 'android:permission' attribute for "
1899 "provider: %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001900 goto bail;
1901 }
1902
1903 hasRequiredSafAttributes |= exported && grantUriPermissions &&
1904 permission == "android.permission.MANAGE_DOCUMENTS";
1905
Michael Wrightec4fdec2013-09-06 16:50:52 -07001906 } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001907 String8 metaDataName = AaptXml::getResolvedAttribute(res, tree,
1908 NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001909 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001910 SourcePos(manifestFile, tree.getLineNumber()).error(
1911 "ERROR getting 'android:name' attribute for "
1912 "meta-data: %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001913 goto bail;
1914 }
Maurice Chu2675f762013-10-22 17:33:11 -07001915 printf("meta-data: name='%s' ",
1916 ResTable::normalizeForOutput(metaDataName.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001917 printResolvedResourceAttribute(res, tree, VALUE_ATTR, String8("value"),
Maurice Chu76327312013-10-16 18:28:46 -07001918 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001919 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001920 // Try looking for a RESOURCE_ATTR
1921 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001922 printResolvedResourceAttribute(res, tree, RESOURCE_ATTR,
Maurice Chu76327312013-10-16 18:28:46 -07001923 String8("resource"), &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001924 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001925 SourcePos(manifestFile, tree.getLineNumber()).error(
1926 "ERROR getting 'android:value' or "
Maurice Chu76327312013-10-16 18:28:46 -07001927 "'android:resource' attribute for "
Adam Lesinski10de3af12016-07-13 10:14:03 -07001928 "meta-data: %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001929 goto bail;
1930 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001931 }
Maurice Chu76327312013-10-16 18:28:46 -07001932 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001933 } else if (withinSupportsInput && tag == "input-type") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001934 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001935 if (name != "" && error == "") {
1936 supportedInput.add(name);
1937 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001938 SourcePos(manifestFile, tree.getLineNumber()).error(
1939 "ERROR getting 'android:name' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001940 error.string());
1941 goto bail;
1942 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001943 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001944 } else if (withinFeatureGroup && tag == "uses-feature") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001945 const String8 androidSchema("http://schemas.android.com/apk/res/android");
Adam Lesinski2c72b682014-06-24 09:56:01 -07001946 FeatureGroup& top = featureGroups.editTop();
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001947
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001948 String8 name = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001949 if (name != "" && error == "") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001950 Feature feature(true);
1951
1952 int32_t featureVers = AaptXml::getIntegerAttribute(
1953 tree, androidSchema.string(), "version", 0, &error);
1954 if (error == "") {
1955 feature.version = featureVers;
1956 } else {
1957 SourcePos(manifestFile, tree.getLineNumber()).error(
1958 "failed to read attribute 'android:version': %s",
1959 error.string());
1960 goto bail;
1961 }
1962
1963 top.features.add(name, feature);
Adam Lesinskid3edfde2014-08-08 17:32:44 -07001964 addParentFeatures(&top, name);
Adam Lesinski694d0a72016-04-06 16:12:04 -07001965
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001966 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001967 int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR,
1968 &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001969 if (error == "") {
1970 if (vers > top.openGLESVersion) {
1971 top.openGLESVersion = vers;
1972 }
1973 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001974 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001975 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001976 } else if (depth == 4) {
1977 if (tag == "intent-filter") {
1978 hasIntentFilter = true;
1979 withinIntentFilter = true;
1980 actMainActivity = false;
1981 actWidgetReceivers = false;
1982 actImeService = false;
1983 actWallpaperService = false;
1984 actAccessibilityService = false;
1985 actPrintService = false;
1986 actDeviceAdminEnabled = false;
1987 actHostApduService = false;
1988 actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001989 actDocumentsProvider = false;
1990 actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001991 actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001992 actCamera = false;
1993 actCameraSecure = false;
1994 catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001995 } else if (withinService && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001996 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001997 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001998 SourcePos(manifestFile, tree.getLineNumber()).error(
1999 "ERROR getting 'android:name' attribute for "
2000 "meta-data tag in service '%s': %s", serviceName.string(),
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002001 error.string());
Adam Lesinski94fc9122013-09-30 17:16:09 -07002002 goto bail;
2003 }
2004
2005 if (name == "android.nfc.cardemulation.host_apdu_service" ||
2006 name == "android.nfc.cardemulation.off_host_apdu_service") {
2007 bool offHost = true;
2008 if (name == "android.nfc.cardemulation.host_apdu_service") {
2009 offHost = false;
2010 }
2011
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07002012 String8 xmlPath = AaptXml::getResolvedAttribute(res, tree,
2013 RESOURCE_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07002014 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07002015 SourcePos(manifestFile, tree.getLineNumber()).error(
2016 "ERROR getting 'android:resource' attribute for "
2017 "meta-data tag in service '%s': %s",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002018 serviceName.string(), error.string());
Adam Lesinski94fc9122013-09-30 17:16:09 -07002019 goto bail;
2020 }
2021
2022 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
2023 offHost, &error);
2024 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07002025 SourcePos(manifestFile, tree.getLineNumber()).error(
2026 "ERROR getting AID category for service '%s'",
Adam Lesinski94fc9122013-09-30 17:16:09 -07002027 serviceName.string());
2028 goto bail;
2029 }
2030
2031 const size_t catLen = categories.size();
2032 for (size_t i = 0; i < catLen; i++) {
2033 bool paymentCategory = (categories[i] == "payment");
2034 if (offHost) {
2035 hasMetaOffHostPaymentCategory |= paymentCategory;
2036 } else {
2037 hasMetaHostPaymentCategory |= paymentCategory;
2038 }
2039 }
2040 }
2041 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002042 } else if ((depth == 5) && withinIntentFilter) {
2043 String8 action;
2044 if (tag == "action") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07002045 action = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07002046 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07002047 SourcePos(manifestFile, tree.getLineNumber()).error(
2048 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07002049 goto bail;
Michael Wrightec4fdec2013-09-06 16:50:52 -07002050 }
2051
Adam Lesinskia5018c92013-09-30 16:23:15 -07002052 if (withinActivity) {
2053 if (action == "android.intent.action.MAIN") {
2054 isMainActivity = true;
2055 actMainActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002056 } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
2057 action == "android.media.action.VIDEO_CAMERA") {
2058 actCamera = true;
2059 } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
2060 actCameraSecure = true;
Michael Wrightec4fdec2013-09-06 16:50:52 -07002061 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002062 } else if (withinReceiver) {
2063 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
2064 actWidgetReceivers = true;
2065 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
2066 actDeviceAdminEnabled = true;
2067 }
2068 } else if (withinService) {
2069 if (action == "android.view.InputMethod") {
2070 actImeService = true;
2071 } else if (action == "android.service.wallpaper.WallpaperService") {
2072 actWallpaperService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002073 } else if (action ==
2074 "android.accessibilityservice.AccessibilityService") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07002075 actAccessibilityService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002076 } else if (action =="android.printservice.PrintService") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07002077 actPrintService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002078 } else if (action ==
2079 "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07002080 actHostApduService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002081 } else if (action ==
2082 "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07002083 actOffHostApduService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002084 } else if (action ==
2085 "android.service.notification.NotificationListenerService") {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002086 actNotificationListenerService = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04002087 } else if (action == "android.service.dreams.DreamService") {
2088 actDreamService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002089 }
2090 } else if (withinProvider) {
2091 if (action == "android.content.action.DOCUMENTS_PROVIDER") {
2092 actDocumentsProvider = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07002093 }
2094 }
2095 if (action == "android.intent.action.SEARCH") {
2096 isSearchable = true;
2097 }
2098 }
2099
2100 if (tag == "category") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07002101 String8 category = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07002102 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07002103 SourcePos(manifestFile, tree.getLineNumber()).error(
2104 "ERROR getting 'name' attribute: %s", error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07002105 goto bail;
2106 }
2107 if (withinActivity) {
2108 if (category == "android.intent.category.LAUNCHER") {
2109 isLauncherActivity = true;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08002110 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
2111 isLeanbackLauncherActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002112 } else if (category == "android.intent.category.HOME") {
2113 catLauncher = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08002114 }
2115 }
2116 }
2117 }
2118 }
2119
2120 // Pre-1.6 implicitly granted permission compatibility logic
2121 if (targetSdk < 4) {
2122 if (!hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002123 printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
2124 printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
2125 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002126 hasWriteExternalStoragePermission = true;
2127 }
2128 if (!hasReadPhoneStatePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002129 printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
2130 printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
2131 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002132 }
2133 }
2134
2135 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
2136 // force them to always take READ_EXTERNAL_STORAGE as well. We always
2137 // do this (regardless of target API version) because we can't have
2138 // an app with write permission but not read permission.
2139 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
Adam Lesinski2386df22016-12-28 15:08:58 -05002140 printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
2141 false /* optional */, writeExternalStoragePermissionMaxSdkVersion);
Adam Lesinski58f1f362013-11-12 12:59:08 -08002142 printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
Adam Lesinski2386df22016-12-28 15:08:58 -05002143 String8("requested WRITE_EXTERNAL_STORAGE"),
2144 writeExternalStoragePermissionMaxSdkVersion);
Adam Lesinski282e1812014-01-23 18:17:42 -08002145 }
2146
2147 // Pre-JellyBean call log permission compatibility.
2148 if (targetSdk < 16) {
2149 if (!hasReadCallLogPermission && hasReadContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002150 printUsesPermission(String8("android.permission.READ_CALL_LOG"));
2151 printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
2152 String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002153 }
2154 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002155 printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
2156 printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
2157 String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002158 }
2159 }
2160
Adam Lesinskica955a42016-08-01 16:44:29 -07002161 // If the app hasn't declared the touchscreen as a feature requirement (either
2162 // directly or implied, required or not), then the faketouch feature is implied.
2163 if (!hasFeature("android.hardware.touchscreen", commonFeatures, impliedFeatures)) {
2164 addImpliedFeature(&impliedFeatures, "android.hardware.faketouch",
Adam Lesinski43158772015-11-11 15:13:55 -08002165 String8("default feature for all apps"), false);
Adam Lesinskica955a42016-08-01 16:44:29 -07002166 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07002167
2168 const size_t numFeatureGroups = featureGroups.size();
2169 if (numFeatureGroups == 0) {
2170 // If no <feature-group> tags were defined, apply auto-implied features.
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002171 printDefaultFeatureGroup(commonFeatures, impliedFeatures);
Adam Lesinski2c72b682014-06-24 09:56:01 -07002172
2173 } else {
2174 // <feature-group> tags are defined, so we ignore implied features and
2175 for (size_t i = 0; i < numFeatureGroups; i++) {
2176 FeatureGroup& grp = featureGroups.editItemAt(i);
2177
Adam Lesinskid7a94da2014-07-25 14:38:54 -07002178 if (commonFeatures.openGLESVersion > grp.openGLESVersion) {
2179 grp.openGLESVersion = commonFeatures.openGLESVersion;
2180 }
2181
Adam Lesinski2c72b682014-06-24 09:56:01 -07002182 // Merge the features defined in the top level (not inside a <feature-group>)
2183 // with this feature group.
2184 const size_t numCommonFeatures = commonFeatures.features.size();
2185 for (size_t j = 0; j < numCommonFeatures; j++) {
2186 if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07002187 grp.features.add(commonFeatures.features.keyAt(j),
2188 commonFeatures.features[j]);
Adam Lesinski2c72b682014-06-24 09:56:01 -07002189 }
2190 }
2191
Adam Lesinski73a05112014-12-08 12:53:17 -08002192 if (!grp.features.isEmpty()) {
Adam Lesinski2c72b682014-06-24 09:56:01 -07002193 printFeatureGroup(grp);
Adam Lesinski282e1812014-01-23 18:17:42 -08002194 }
2195 }
2196 }
2197
Adam Lesinski282e1812014-01-23 18:17:42 -08002198
Adam Lesinski282e1812014-01-23 18:17:42 -08002199 if (hasWidgetReceivers) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002200 printComponentPresence("app-widget");
Adam Lesinski282e1812014-01-23 18:17:42 -08002201 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002202 if (hasDeviceAdminReceiver) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002203 printComponentPresence("device-admin");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002204 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002205 if (hasImeService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002206 printComponentPresence("ime");
Adam Lesinski282e1812014-01-23 18:17:42 -08002207 }
2208 if (hasWallpaperService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002209 printComponentPresence("wallpaper");
Adam Lesinski282e1812014-01-23 18:17:42 -08002210 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002211 if (hasAccessibilityService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002212 printComponentPresence("accessibility");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002213 }
2214 if (hasPrintService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002215 printComponentPresence("print-service");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002216 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07002217 if (hasPaymentService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002218 printComponentPresence("payment");
2219 }
2220 if (isSearchable) {
2221 printComponentPresence("search");
2222 }
2223 if (hasDocumentsProvider) {
2224 printComponentPresence("document-provider");
2225 }
2226 if (hasLauncher) {
2227 printComponentPresence("launcher");
2228 }
2229 if (hasNotificationListenerService) {
2230 printComponentPresence("notification-listener");
2231 }
John Spurlockeb8d1be2014-06-25 17:46:15 -04002232 if (hasDreamService) {
2233 printComponentPresence("dream");
2234 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002235 if (hasCameraActivity) {
2236 printComponentPresence("camera");
2237 }
2238 if (hasCameraSecureActivity) {
2239 printComponentPresence("camera-secure");
2240 }
2241
2242 if (hasMainActivity) {
2243 printf("main\n");
Adam Lesinski94fc9122013-09-30 17:16:09 -07002244 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002245 if (hasOtherActivities) {
2246 printf("other-activities\n");
2247 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002248 if (hasOtherReceivers) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002249 printf("other-receivers\n");
2250 }
2251 if (hasOtherServices) {
2252 printf("other-services\n");
2253 }
2254
2255 // For modern apps, if screen size buckets haven't been specified
2256 // but the new width ranges have, then infer the buckets from them.
2257 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
2258 && requiresSmallestWidthDp > 0) {
2259 int compatWidth = compatibleWidthLimitDp;
2260 if (compatWidth <= 0) {
2261 compatWidth = requiresSmallestWidthDp;
2262 }
2263 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
2264 smallScreen = -1;
2265 } else {
2266 smallScreen = 0;
2267 }
2268 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
2269 normalScreen = -1;
2270 } else {
2271 normalScreen = 0;
2272 }
2273 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
2274 largeScreen = -1;
2275 } else {
2276 largeScreen = 0;
2277 }
2278 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
2279 xlargeScreen = -1;
2280 } else {
2281 xlargeScreen = 0;
2282 }
2283 }
2284
2285 // Determine default values for any unspecified screen sizes,
2286 // based on the target SDK of the package. As of 4 (donut)
2287 // the screen size support was introduced, so all default to
2288 // enabled.
2289 if (smallScreen > 0) {
2290 smallScreen = targetSdk >= 4 ? -1 : 0;
2291 }
2292 if (normalScreen > 0) {
2293 normalScreen = -1;
2294 }
2295 if (largeScreen > 0) {
2296 largeScreen = targetSdk >= 4 ? -1 : 0;
2297 }
2298 if (xlargeScreen > 0) {
2299 // Introduced in Gingerbread.
2300 xlargeScreen = targetSdk >= 9 ? -1 : 0;
2301 }
2302 if (anyDensity > 0) {
2303 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
2304 || compatibleWidthLimitDp > 0) ? -1 : 0;
2305 }
2306 printf("supports-screens:");
2307 if (smallScreen != 0) {
2308 printf(" 'small'");
2309 }
2310 if (normalScreen != 0) {
2311 printf(" 'normal'");
2312 }
2313 if (largeScreen != 0) {
2314 printf(" 'large'");
2315 }
2316 if (xlargeScreen != 0) {
2317 printf(" 'xlarge'");
2318 }
2319 printf("\n");
2320 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
2321 if (requiresSmallestWidthDp > 0) {
2322 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
2323 }
2324 if (compatibleWidthLimitDp > 0) {
2325 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
2326 }
2327 if (largestWidthLimitDp > 0) {
2328 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
2329 }
2330
2331 printf("locales:");
2332 const size_t NL = locales.size();
2333 for (size_t i=0; i<NL; i++) {
2334 const char* localeStr = locales[i].string();
2335 if (localeStr == NULL || strlen(localeStr) == 0) {
2336 localeStr = "--_--";
2337 }
2338 printf(" '%s'", localeStr);
2339 }
2340 printf("\n");
2341
2342 printf("densities:");
2343 const size_t ND = densities.size();
2344 for (size_t i=0; i<ND; i++) {
2345 printf(" '%d'", densities[i]);
2346 }
2347 printf("\n");
2348
2349 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
2350 if (dir != NULL) {
2351 if (dir->getFileCount() > 0) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002352 SortedVector<String8> architectures;
Adam Lesinski282e1812014-01-23 18:17:42 -08002353 for (size_t i=0; i<dir->getFileCount(); i++) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002354 architectures.add(ResTable::normalizeForOutput(
2355 dir->getFileName(i).string()));
Adam Lesinski282e1812014-01-23 18:17:42 -08002356 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07002357
2358 bool outputAltNativeCode = false;
2359 // A multiArch package is one that contains 64-bit and
2360 // 32-bit versions of native code and expects 3rd-party
2361 // apps to load these native code libraries. Since most
2362 // 64-bit systems also support 32-bit apps, the apps
2363 // loading this multiArch package's code may be either
2364 // 32-bit or 64-bit.
2365 if (hasMultiArch) {
2366 // If this is a multiArch package, report the 64-bit
2367 // version only. Then as a separate entry, report the
2368 // rest.
2369 //
2370 // If we report the 32-bit architecture, this APK will
2371 // be installed on a 32-bit device, causing a large waste
2372 // of bandwidth and disk space. This assumes that
2373 // the developer of the multiArch package has also
2374 // made a version that is 32-bit only.
2375 String8 intel64("x86_64");
2376 String8 arm64("arm64-v8a");
2377 ssize_t index = architectures.indexOf(intel64);
2378 if (index < 0) {
2379 index = architectures.indexOf(arm64);
2380 }
2381
2382 if (index >= 0) {
2383 printf("native-code: '%s'\n", architectures[index].string());
2384 architectures.removeAt(index);
2385 outputAltNativeCode = true;
2386 }
2387 }
2388
2389 const size_t archCount = architectures.size();
2390 if (archCount > 0) {
2391 if (outputAltNativeCode) {
2392 printf("alt-");
2393 }
2394 printf("native-code:");
2395 for (size_t i = 0; i < archCount; i++) {
2396 printf(" '%s'", architectures[i].string());
2397 }
2398 printf("\n");
2399 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002400 }
2401 delete dir;
2402 }
2403 } else if (strcmp("badger", option) == 0) {
2404 printf("%s", CONSOLE_DATA);
2405 } else if (strcmp("configurations", option) == 0) {
2406 Vector<ResTable_config> configs;
2407 res.getConfigurations(&configs);
2408 const size_t N = configs.size();
2409 for (size_t i=0; i<N; i++) {
2410 printf("%s\n", configs[i].toString().string());
2411 }
2412 } else {
2413 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2414 goto bail;
2415 }
2416 }
2417
2418 result = NO_ERROR;
2419
2420bail:
Adam Lesinski10de3af12016-07-13 10:14:03 -07002421 if (SourcePos::hasErrors()) {
2422 SourcePos::printErrors(stderr);
2423 }
2424
Adam Lesinski282e1812014-01-23 18:17:42 -08002425 if (asset) {
2426 delete asset;
2427 }
2428 return (result != NO_ERROR);
2429}
2430
2431
2432/*
2433 * Handle the "add" command, which wants to add files to a new or
2434 * pre-existing archive.
2435 */
2436int doAdd(Bundle* bundle)
2437{
2438 ZipFile* zip = NULL;
2439 status_t result = UNKNOWN_ERROR;
2440 const char* zipFileName;
2441
2442 if (bundle->getUpdate()) {
2443 /* avoid confusion */
2444 fprintf(stderr, "ERROR: can't use '-u' with add\n");
2445 goto bail;
2446 }
2447
2448 if (bundle->getFileSpecCount() < 1) {
2449 fprintf(stderr, "ERROR: must specify zip file name\n");
2450 goto bail;
2451 }
2452 zipFileName = bundle->getFileSpecEntry(0);
2453
2454 if (bundle->getFileSpecCount() < 2) {
2455 fprintf(stderr, "NOTE: nothing to do\n");
2456 goto bail;
2457 }
2458
2459 zip = openReadWrite(zipFileName, true);
2460 if (zip == NULL) {
2461 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2462 goto bail;
2463 }
2464
2465 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2466 const char* fileName = bundle->getFileSpecEntry(i);
2467
2468 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
2469 printf(" '%s'... (from gzip)\n", fileName);
2470 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
2471 } else {
2472 if (bundle->getJunkPath()) {
2473 String8 storageName = String8(fileName).getPathLeaf();
Maurice Chu2675f762013-10-22 17:33:11 -07002474 printf(" '%s' as '%s'...\n", fileName,
2475 ResTable::normalizeForOutput(storageName.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08002476 result = zip->add(fileName, storageName.string(),
2477 bundle->getCompressionMethod(), NULL);
2478 } else {
2479 printf(" '%s'...\n", fileName);
2480 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2481 }
2482 }
2483 if (result != NO_ERROR) {
2484 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2485 if (result == NAME_NOT_FOUND) {
2486 fprintf(stderr, ": file not found\n");
2487 } else if (result == ALREADY_EXISTS) {
2488 fprintf(stderr, ": already exists in archive\n");
2489 } else {
2490 fprintf(stderr, "\n");
2491 }
2492 goto bail;
2493 }
2494 }
2495
2496 result = NO_ERROR;
2497
2498bail:
2499 delete zip;
2500 return (result != NO_ERROR);
2501}
2502
2503
2504/*
2505 * Delete files from an existing archive.
2506 */
2507int doRemove(Bundle* bundle)
2508{
2509 ZipFile* zip = NULL;
2510 status_t result = UNKNOWN_ERROR;
2511 const char* zipFileName;
2512
2513 if (bundle->getFileSpecCount() < 1) {
2514 fprintf(stderr, "ERROR: must specify zip file name\n");
2515 goto bail;
2516 }
2517 zipFileName = bundle->getFileSpecEntry(0);
2518
2519 if (bundle->getFileSpecCount() < 2) {
2520 fprintf(stderr, "NOTE: nothing to do\n");
2521 goto bail;
2522 }
2523
2524 zip = openReadWrite(zipFileName, false);
2525 if (zip == NULL) {
2526 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2527 zipFileName);
2528 goto bail;
2529 }
2530
2531 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2532 const char* fileName = bundle->getFileSpecEntry(i);
2533 ZipEntry* entry;
2534
2535 entry = zip->getEntryByName(fileName);
2536 if (entry == NULL) {
2537 printf(" '%s' NOT FOUND\n", fileName);
2538 continue;
2539 }
2540
2541 result = zip->remove(entry);
2542
2543 if (result != NO_ERROR) {
2544 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2545 bundle->getFileSpecEntry(i), zipFileName);
2546 goto bail;
2547 }
2548 }
2549
2550 /* update the archive */
2551 zip->flush();
2552
2553bail:
2554 delete zip;
2555 return (result != NO_ERROR);
2556}
2557
Adam Lesinski3921e872014-05-13 10:56:25 -07002558static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002559 const size_t numDirs = dir->getDirs().size();
2560 for (size_t i = 0; i < numDirs; i++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002561 bool ignore = ignoreConfig;
2562 const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2563 const char* dirStr = subDir->getLeaf().string();
2564 if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2565 ignore = true;
2566 }
2567 status_t err = addResourcesToBuilder(subDir, builder, ignore);
Adam Lesinskifab50872014-04-16 14:40:42 -07002568 if (err != NO_ERROR) {
2569 return err;
2570 }
2571 }
2572
2573 const size_t numFiles = dir->getFiles().size();
2574 for (size_t i = 0; i < numFiles; i++) {
2575 sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2576 const size_t numConfigs = gp->getFiles().size();
2577 for (size_t j = 0; j < numConfigs; j++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002578 status_t err = NO_ERROR;
Adam Lesinskic7614e52017-03-16 16:54:23 -07002579 if (ignoreConfig) {
Guang Zhu8c2df712017-03-21 03:53:43 +00002580 err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
Adam Lesinskic7614e52017-03-16 16:54:23 -07002581 } else {
Guang Zhu8c2df712017-03-21 03:53:43 +00002582 err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
Adam Lesinskic7614e52017-03-16 16:54:23 -07002583 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002584 if (err != NO_ERROR) {
2585 fprintf(stderr, "Failed to add %s (%s) to builder.\n",
Guang Zhu8c2df712017-03-21 03:53:43 +00002586 gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
Adam Lesinskifab50872014-04-16 14:40:42 -07002587 return err;
2588 }
2589 }
2590 }
2591 return NO_ERROR;
2592}
2593
2594static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2595 if (split->isBase()) {
2596 return original;
2597 }
2598
2599 String8 ext(original.getPathExtension());
2600 if (ext == String8(".apk")) {
2601 return String8::format("%s_%s%s",
2602 original.getBasePath().string(),
2603 split->getDirectorySafeName().string(),
2604 ext.string());
2605 }
2606
2607 return String8::format("%s_%s", original.string(),
2608 split->getDirectorySafeName().string());
2609}
Adam Lesinski282e1812014-01-23 18:17:42 -08002610
2611/*
2612 * Package up an asset directory and associated application files.
2613 */
2614int doPackage(Bundle* bundle)
2615{
2616 const char* outputAPKFile;
2617 int retVal = 1;
2618 status_t err;
2619 sp<AaptAssets> assets;
2620 int N;
2621 FILE* fp;
2622 String8 dependencyFile;
Adam Lesinskifab50872014-04-16 14:40:42 -07002623 sp<ApkBuilder> builder;
Adam Lesinski282e1812014-01-23 18:17:42 -08002624
Anton Krumina2ef5c02014-03-12 14:46:44 -07002625 // -c en_XA or/and ar_XB means do pseudolocalization
Adam Lesinskifab50872014-04-16 14:40:42 -07002626 sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2627 err = configFilter->parse(bundle->getConfigurations());
Adam Lesinski282e1812014-01-23 18:17:42 -08002628 if (err != NO_ERROR) {
2629 goto bail;
2630 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002631 if (configFilter->containsPseudo()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002632 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2633 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002634 if (configFilter->containsPseudoBidi()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002635 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
Adam Lesinski282e1812014-01-23 18:17:42 -08002636 }
2637
2638 N = bundle->getFileSpecCount();
2639 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
Adam Lesinski09384302014-01-22 16:07:42 -08002640 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002641 fprintf(stderr, "ERROR: no input files\n");
2642 goto bail;
2643 }
2644
2645 outputAPKFile = bundle->getOutputAPKFile();
2646
2647 // Make sure the filenames provided exist and are of the appropriate type.
2648 if (outputAPKFile) {
2649 FileType type;
2650 type = getFileType(outputAPKFile);
2651 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2652 fprintf(stderr,
2653 "ERROR: output file '%s' exists but is not regular file\n",
2654 outputAPKFile);
2655 goto bail;
2656 }
2657 }
2658
2659 // Load the assets.
2660 assets = new AaptAssets();
2661
2662 // Set up the resource gathering in assets if we're going to generate
2663 // dependency files. Every time we encounter a resource while slurping
2664 // the tree, we'll add it to these stores so we have full resource paths
2665 // to write to a dependency file.
2666 if (bundle->getGenDependencies()) {
2667 sp<FilePathStore> resPathStore = new FilePathStore;
2668 assets->setFullResPaths(resPathStore);
2669 sp<FilePathStore> assetPathStore = new FilePathStore;
2670 assets->setFullAssetPaths(assetPathStore);
2671 }
2672
2673 err = assets->slurpFromArgs(bundle);
2674 if (err < 0) {
2675 goto bail;
2676 }
2677
2678 if (bundle->getVerbose()) {
2679 assets->print(String8());
2680 }
2681
Adam Lesinskifab50872014-04-16 14:40:42 -07002682 // Create the ApkBuilder, which will collect the compiled files
2683 // to write to the final APK (or sets of APKs if we are building
2684 // a Split APK.
2685 builder = new ApkBuilder(configFilter);
2686
2687 // If we are generating a Split APK, find out which configurations to split on.
2688 if (bundle->getSplitConfigurations().size() > 0) {
2689 const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2690 const size_t numSplits = splitStrs.size();
2691 for (size_t i = 0; i < numSplits; i++) {
2692 std::set<ConfigDescription> configs;
2693 if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2694 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2695 goto bail;
2696 }
2697
2698 err = builder->createSplitForConfigs(configs);
2699 if (err != NO_ERROR) {
2700 goto bail;
2701 }
2702 }
2703 }
2704
Adam Lesinski282e1812014-01-23 18:17:42 -08002705 // If they asked for any fileAs that need to be compiled, do so.
2706 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002707 err = buildResources(bundle, assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002708 if (err != 0) {
2709 goto bail;
2710 }
2711 }
2712
2713 // At this point we've read everything and processed everything. From here
2714 // on out it's just writing output files.
2715 if (SourcePos::hasErrors()) {
2716 goto bail;
2717 }
2718
2719 // Update symbols with information about which ones are needed as Java symbols.
2720 assets->applyJavaSymbols();
2721 if (SourcePos::hasErrors()) {
2722 goto bail;
2723 }
2724
2725 // If we've been asked to generate a dependency file, do that here
2726 if (bundle->getGenDependencies()) {
2727 // If this is the packaging step, generate the dependency file next to
2728 // the output apk (e.g. bin/resources.ap_.d)
2729 if (outputAPKFile) {
2730 dependencyFile = String8(outputAPKFile);
2731 // Add the .d extension to the dependency file.
2732 dependencyFile.append(".d");
2733 } else {
2734 // Else if this is the R.java dependency generation step,
2735 // generate the dependency file in the R.java package subdirectory
2736 // e.g. gen/com/foo/app/R.java.d
2737 dependencyFile = String8(bundle->getRClassDir());
2738 dependencyFile.appendPath("R.java.d");
2739 }
2740 // Make sure we have a clean dependency file to start with
2741 fp = fopen(dependencyFile, "w");
2742 fclose(fp);
2743 }
2744
2745 // Write out R.java constants
2746 if (!assets->havePrivateSymbols()) {
2747 if (bundle->getCustomPackage() == NULL) {
2748 // Write the R.java file into the appropriate class directory
2749 // e.g. gen/com/foo/app/R.java
Adam Lesinski1e4663852014-08-15 14:47:28 -07002750 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002751 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002752 } else {
2753 const String8 customPkg(bundle->getCustomPackage());
Adam Lesinski1e4663852014-08-15 14:47:28 -07002754 err = writeResourceSymbols(bundle, assets, customPkg, true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002755 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002756 }
2757 if (err < 0) {
2758 goto bail;
2759 }
2760 // If we have library files, we're going to write our R.java file into
2761 // the appropriate class directory for those libraries as well.
2762 // e.g. gen/com/foo/app/lib/R.java
2763 if (bundle->getExtraPackages() != NULL) {
2764 // Split on colon
2765 String8 libs(bundle->getExtraPackages());
2766 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2767 while (packageString != NULL) {
2768 // Write the R.java file out with the correct package name
Marcin Kosiba0f3a5a62014-09-11 13:48:48 +01002769 err = writeResourceSymbols(bundle, assets, String8(packageString), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002770 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002771 if (err < 0) {
2772 goto bail;
2773 }
2774 packageString = strtok(NULL, ":");
2775 }
2776 libs.unlockBuffer();
2777 }
2778 } else {
Adam Lesinski1e4663852014-08-15 14:47:28 -07002779 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002780 if (err < 0) {
2781 goto bail;
2782 }
Adam Lesinski1e4663852014-08-15 14:47:28 -07002783 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002784 if (err < 0) {
2785 goto bail;
2786 }
2787 }
2788
2789 // Write out the ProGuard file
2790 err = writeProguardFile(bundle, assets);
2791 if (err < 0) {
2792 goto bail;
2793 }
2794
Rohit Agrawal86229cb2016-04-21 16:29:58 -07002795 // Write out the Main Dex ProGuard file
2796 err = writeMainDexProguardFile(bundle, assets);
2797 if (err < 0) {
2798 goto bail;
2799 }
2800
Adam Lesinski282e1812014-01-23 18:17:42 -08002801 // Write the apk
2802 if (outputAPKFile) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002803 // Gather all resources and add them to the APK Builder. The builder will then
2804 // figure out which Split they belong in.
2805 err = addResourcesToBuilder(assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002806 if (err != NO_ERROR) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002807 goto bail;
2808 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002809
2810 const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2811 const size_t numSplits = splits.size();
2812 for (size_t i = 0; i < numSplits; i++) {
2813 const sp<ApkSplit>& split = splits[i];
2814 String8 outputPath = buildApkName(String8(outputAPKFile), split);
2815 err = writeAPK(bundle, outputPath, split);
2816 if (err != NO_ERROR) {
2817 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2818 goto bail;
2819 }
2820 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002821 }
2822
2823 // If we've been asked to generate a dependency file, we need to finish up here.
2824 // the writeResourceSymbols and writeAPK functions have already written the target
2825 // half of the dependency file, now we need to write the prerequisites. (files that
2826 // the R.java file or .ap_ file depend on)
2827 if (bundle->getGenDependencies()) {
2828 // Now that writeResourceSymbols or writeAPK has taken care of writing
2829 // the targets to our dependency file, we'll write the prereqs
2830 fp = fopen(dependencyFile, "a+");
2831 fprintf(fp, " : ");
2832 bool includeRaw = (outputAPKFile != NULL);
2833 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2834 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2835 // and therefore was not added to our pathstores during slurping
2836 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2837 fclose(fp);
2838 }
2839
2840 retVal = 0;
2841bail:
2842 if (SourcePos::hasErrors()) {
2843 SourcePos::printErrors(stderr);
2844 }
2845 return retVal;
2846}
2847
2848/*
2849 * Do PNG Crunching
2850 * PRECONDITIONS
2851 * -S flag points to a source directory containing drawable* folders
2852 * -C flag points to destination directory. The folder structure in the
2853 * source directory will be mirrored to the destination (cache) directory
2854 *
2855 * POSTCONDITIONS
2856 * Destination directory will be updated to match the PNG files in
Maurice Chu2675f762013-10-22 17:33:11 -07002857 * the source directory.
Adam Lesinski282e1812014-01-23 18:17:42 -08002858 */
2859int doCrunch(Bundle* bundle)
2860{
2861 fprintf(stdout, "Crunching PNG Files in ");
2862 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2863 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2864
2865 updatePreProcessedCache(bundle);
2866
2867 return NO_ERROR;
2868}
2869
2870/*
2871 * Do PNG Crunching on a single flag
2872 * -i points to a single png file
2873 * -o points to a single png output file
2874 */
2875int doSingleCrunch(Bundle* bundle)
2876{
2877 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2878 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2879
2880 String8 input(bundle->getSingleCrunchInputFile());
2881 String8 output(bundle->getSingleCrunchOutputFile());
2882
2883 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2884 // we can't return the status_t as it gets truncate to the lower 8 bits.
2885 return 42;
2886 }
2887
2888 return NO_ERROR;
2889}
2890
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002891int runInDaemonMode(Bundle* bundle) {
2892 std::cout << "Ready" << std::endl;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002893 for (std::string cmd; std::getline(std::cin, cmd);) {
2894 if (cmd == "quit") {
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002895 return NO_ERROR;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002896 } else if (cmd == "s") {
2897 // Two argument crunch
2898 std::string inputFile, outputFile;
2899 std::getline(std::cin, inputFile);
2900 std::getline(std::cin, outputFile);
2901 bundle->setSingleCrunchInputFile(inputFile.c_str());
2902 bundle->setSingleCrunchOutputFile(outputFile.c_str());
2903 std::cout << "Crunching " << inputFile << std::endl;
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002904 if (doSingleCrunch(bundle) != NO_ERROR) {
2905 std::cout << "Error" << std::endl;
2906 }
2907 std::cout << "Done" << std::endl;
2908 } else {
2909 // in case of invalid command, just bail out.
2910 std::cerr << "Unknown command" << std::endl;
2911 return -1;
2912 }
2913 }
2914 return -1;
2915}
2916
Adam Lesinski282e1812014-01-23 18:17:42 -08002917char CONSOLE_DATA[2925] = {
2918 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2919 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2920 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2921 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2922 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2923 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2924 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2925 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2926 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2927 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2928 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2929 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2930 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2931 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2932 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2933 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2934 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2935 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2936 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2937 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2938 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2939 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2940 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2941 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2942 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2943 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2944 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2945 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2946 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2947 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2948 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2949 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2950 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2951 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2952 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2953 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2954 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2955 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2956 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2957 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2958 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2959 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2960 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2961 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2962 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2963 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2964 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2965 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2966 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2967 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2968 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2969 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2970 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2971 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2972 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2973 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2974 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2975 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2976 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2977 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2978 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2979 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2980 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2981 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2982 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2983 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2984 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2985 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2986 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2987 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2988 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2989 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2990 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2991 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2992 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2993 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2994 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2995 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2996 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2997 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2998 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2999 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
3000 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
3001 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3002 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
3003 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
3004 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
3005 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
3006 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
3007 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3008 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3009 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
3010 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
3011 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3012 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3013 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
3014 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3015 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
3016 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
3017 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
3018 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3019 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3020 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
3021 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
3022 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
3023 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3024 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
3025 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3026 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3027 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
3028 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
3029 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3030 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3031 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
3032 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3033 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
3034 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
3035 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
3036 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3037 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3038 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
3039 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
3040 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
3041 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
3042 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
3043 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
3044 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
3045 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
3046 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
3047 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3048 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3049 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
3050 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
3051 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
3052 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
3053 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
3054 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3055 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3056 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
3057 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
3058 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
3059 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3060 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
3061 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3062 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
3063 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
3064 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
3065 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3066 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3067 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
3068 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
3069 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
3070 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
3071 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
3072 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3073 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3074 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
3075 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
3076 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3077 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3078 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3079 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3080 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
3081 };