blob: 5a06b102592a094922ae17080dec5f058625ecec [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);
Tomasz Wasilczykade06312023-08-10 23:54:44 +0000243 printf("%s='%s'", attrLabel.c_str(),
244 ResTable::normalizeForOutput(result.c_str()).c_str());
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) {
Tomasz Wasilczykade06312023-08-10 23:54:44 +0000247 printf("%s='%d'", attrLabel.c_str(), value.data);
Maurice Chu76327312013-10-16 18:28:46 -0700248 } else {
Tomasz Wasilczykade06312023-08-10 23:54:44 +0000249 printf("%s='0x%x'", attrLabel.c_str(), (int)value.data);
Maurice Chu76327312013-10-16 18:28:46 -0700250 }
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,
Tomasz Wasilczyk5f004aa2023-08-14 16:26:24 +0000356 const String8& requiredFeature = String8(),
357 const String8& requiredNotFeature = String8()) {
Tomasz Wasilczykade06312023-08-10 23:54:44 +0000358 printf("uses-permission: name='%s'", ResTable::normalizeForOutput(name.c_str()).c_str());
Adam Lesinski58f1f362013-11-12 12:59:08 -0800359 if (maxSdkVersion != -1) {
360 printf(" maxSdkVersion='%d'", maxSdkVersion);
361 }
Dianne Hackborncd154e92017-02-28 17:37:35 -0800362 if (requiredFeature.length() > 0) {
Tomasz Wasilczykade06312023-08-10 23:54:44 +0000363 printf(" requiredFeature='%s'", requiredFeature.c_str());
Dianne Hackborncd154e92017-02-28 17:37:35 -0800364 }
365 if (requiredNotFeature.length() > 0) {
Tomasz Wasilczykade06312023-08-10 23:54:44 +0000366 printf(" requiredNotFeature='%s'", requiredNotFeature.c_str());
Dianne Hackborncd154e92017-02-28 17:37:35 -0800367 }
Adam Lesinski58f1f362013-11-12 12:59:08 -0800368 printf("\n");
369
370 if (optional) {
371 printf("optional-permission: name='%s'",
Tomasz Wasilczykade06312023-08-10 23:54:44 +0000372 ResTable::normalizeForOutput(name.c_str()).c_str());
Adam Lesinski58f1f362013-11-12 12:59:08 -0800373 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
Tomasz Wasilczykade06312023-08-10 23:54:44 +0000383 printf("name='%s'", ResTable::normalizeForOutput(name.c_str()).c_str());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800384 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'",
Tomasz Wasilczykade06312023-08-10 23:54:44 +0000393 ResTable::normalizeForOutput(name.c_str()).c_str());
Adam Lesinski2386df22016-12-28 15:08:58 -0500394 if (maxSdkVersion != -1) {
395 printf(" maxSdkVersion='%d'", maxSdkVersion);
396 }
Tomasz Wasilczykade06312023-08-10 23:54:44 +0000397 printf(" reason='%s'\n", ResTable::normalizeForOutput(reason.c_str()).c_str());
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) {
Tomasz Wasilczykade06312023-08-10 23:54:44 +0000559 printf("feature-group: label='%s'\n", grp.label.c_str());
Adam Lesinski2c72b682014-06-24 09:56:01 -0700560
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"),
Tomasz Wasilczykade06312023-08-10 23:54:44 +0000573 ResTable::normalizeForOutput(featureName.c_str()).c_str());
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(
Tomasz Wasilczykade06312023-08-10 23:54:44 +0000592 impliedFeature.name.c_str()));
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800593 const char* sdk23Suffix = impliedFeature.impliedBySdk23 ? "-sdk-23" : "";
594
Tomasz Wasilczykade06312023-08-10 23:54:44 +0000595 printf(" uses-feature%s: name='%s'\n", sdk23Suffix, printableFeatureName.c_str());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800596 printf(" uses-implied-feature%s: name='%s' reason='", sdk23Suffix,
Tomasz Wasilczykade06312023-08-10 23:54:44 +0000597 printableFeatureName.c_str());
Adam Lesinski2c72b682014-06-24 09:56:01 -0700598 const size_t numReasons = impliedFeature.reasons.size();
599 for (size_t j = 0; j < numReasons; j++) {
Tomasz Wasilczykade06312023-08-10 23:54:44 +0000600 printf("%s", impliedFeature.reasons[j].c_str());
Adam Lesinski2c72b682014-06-24 09:56:01 -0700601 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",
Tomasz Wasilczykade06312023-08-10 23:54:44 +0000652 String8::format("requested %s permission", name.c_str()),
Adam Lesinski43158772015-11-11 15:13:55 -0800653 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",
Tomasz Wasilczykade06312023-08-10 23:54:44 +0000657 String8::format("requested %s permission", name.c_str()),
Adam Lesinski43158772015-11-11 15:13:55 -0800658 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",
Tomasz Wasilczykade06312023-08-10 23:54:44 +0000664 String8::format("requested %s permission", name.c_str()),
Adam Lesinski43158772015-11-11 15:13:55 -0800665 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",
Tomasz Wasilczykade06312023-08-10 23:54:44 +0000669 String8::format("requested %s permission", name.c_str()),
Adam Lesinski43158772015-11-11 15:13:55 -0800670 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",
Tomasz Wasilczykade06312023-08-10 23:54:44 +0000676 String8::format("requested %s permission", name.c_str()),
Adam Lesinski43158772015-11-11 15:13:55 -0800677 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",
Tomasz Wasilczykade06312023-08-10 23:54:44 +0000682 String8::format("requested %s permission", name.c_str()),
Adam Lesinski43158772015-11-11 15:13:55 -0800683 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",
Tomasz Wasilczykade06312023-08-10 23:54:44 +0000688 String8::format("requested %s permission", name.c_str()),
Adam Lesinski43158772015-11-11 15:13:55 -0800689 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",
Tomasz Wasilczykade06312023-08-10 23:54:44 +0000696 String8::format("requested %s permission", name.c_str()),
Adam Lesinski43158772015-11-11 15:13:55 -0800697 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",
Tomasz Wasilczykade06312023-08-10 23:54:44 +0000702 String8::format("requested %s permission", name.c_str()),
Adam Lesinski43158772015-11-11 15:13:55 -0800703 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)) {
Tomasz Wasilczykade06312023-08-10 23:54:44 +0000749 fprintf(stderr, "ERROR: included asset path %s could not be loaded\n", assetPath.c_str());
Adam Lesinski57fe4832017-05-10 15:42:22 -0700750 return 1;
751 }
752 }
753
Donald Chaid1ac6e12017-10-12 21:00:45 -0700754 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
755 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
756 return 1;
757 }
758
Adam Lesinski282e1812014-01-23 18:17:42 -0800759 // Make a dummy config for retrieving resources... we need to supply
760 // non-default values for some configs so that we can retrieve resources
761 // in the app that don't have a default. The most important of these is
762 // the API version because key resources like icons will have an implicit
763 // version if they are using newer config types like density.
764 ResTable_config config;
Narayan Kamath91447d82014-01-21 15:32:36 +0000765 memset(&config, 0, sizeof(ResTable_config));
Adam Lesinski282e1812014-01-23 18:17:42 -0800766 config.language[0] = 'e';
767 config.language[1] = 'n';
768 config.country[0] = 'U';
769 config.country[1] = 'S';
770 config.orientation = ResTable_config::ORIENTATION_PORT;
771 config.density = ResTable_config::DENSITY_MEDIUM;
Jackal Guo201a60a2021-08-31 12:37:30 +0800772 config.sdkVersion = SDK_CUR_DEVELOPMENT; // Very high.
Adam Lesinski282e1812014-01-23 18:17:42 -0800773 config.screenWidthDp = 320;
774 config.screenHeightDp = 480;
775 config.smallestScreenWidthDp = 320;
Adam Lesinskic2dea8d2014-08-04 16:40:41 -0700776 config.screenLayout |= ResTable_config::SCREENSIZE_NORMAL;
Adam Lesinski282e1812014-01-23 18:17:42 -0800777 assets.setConfiguration(config);
778
779 const ResTable& res = assets.getResources(false);
Dan Albert68001652014-09-09 09:51:01 -0700780 if (res.getError() != NO_ERROR) {
Adam Lesinski25e9d552014-05-19 15:01:43 -0700781 fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
Adam Lesinski63e646e2014-07-30 11:40:39 -0700782 return 1;
Adam Lesinski282e1812014-01-23 18:17:42 -0800783 }
784
Adam Lesinski694d0a72016-04-06 16:12:04 -0700785 // Source for AndroidManifest.xml
Adam Lesinski10de3af12016-07-13 10:14:03 -0700786 const String8 manifestFile("AndroidManifest.xml");
Adam Lesinski694d0a72016-04-06 16:12:04 -0700787
Adam Lesinski2cb761e2014-08-15 13:59:02 -0700788 // The dynamicRefTable can be null if there are no resources for this asset cookie.
789 // This fine.
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700790 auto noop_destructor = [](const DynamicRefTable* /*ref_table */) { };
791 auto dynamicRefTable = std::shared_ptr<const DynamicRefTable>(
792 res.getDynamicRefTableForCookie(assetsCookie), noop_destructor);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700793
794 Asset* asset = NULL;
795
Adam Lesinski282e1812014-01-23 18:17:42 -0800796 if (strcmp("resources", option) == 0) {
Elliott Hughesba3fe562015-08-12 14:49:53 -0700797#ifndef __ANDROID__
Adam Lesinski282e1812014-01-23 18:17:42 -0800798 res.print(bundle->getValues());
799#endif
800
801 } else if (strcmp("strings", option) == 0) {
802 const ResStringPool* pool = res.getTableStringBlock(0);
803 printStringPool(pool);
804
805 } else if (strcmp("xmltree", option) == 0) {
806 if (bundle->getFileSpecCount() < 3) {
807 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
808 goto bail;
809 }
810
811 for (int i=2; i<bundle->getFileSpecCount(); i++) {
812 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700813 ResXMLTree tree(dynamicRefTable);
814 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800815 if (asset == NULL) {
Adam Lesinskifcb5f7b2016-11-02 13:17:10 -0700816 fprintf(stderr, "ERROR: dump failed because resource %s not found\n", resname);
Adam Lesinski282e1812014-01-23 18:17:42 -0800817 goto bail;
818 }
819
820 if (tree.setTo(asset->getBuffer(true),
821 asset->getLength()) != NO_ERROR) {
822 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
823 goto bail;
824 }
825 tree.restart();
826 printXMLBlock(&tree);
827 tree.uninit();
828 delete asset;
829 asset = NULL;
830 }
831
832 } else if (strcmp("xmlstrings", option) == 0) {
833 if (bundle->getFileSpecCount() < 3) {
834 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
835 goto bail;
836 }
837
838 for (int i=2; i<bundle->getFileSpecCount(); i++) {
839 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700840 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800841 if (asset == NULL) {
842 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
843 goto bail;
844 }
845
Adam Lesinski63e646e2014-07-30 11:40:39 -0700846 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800847 if (tree.setTo(asset->getBuffer(true),
848 asset->getLength()) != NO_ERROR) {
849 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
850 goto bail;
851 }
852 printStringPool(&tree.getStrings());
853 delete asset;
854 asset = NULL;
855 }
856
857 } else {
Adam Lesinski63e646e2014-07-30 11:40:39 -0700858 asset = assets.openNonAsset(assetsCookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800859 if (asset == NULL) {
860 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
861 goto bail;
862 }
863
Adam Lesinski63e646e2014-07-30 11:40:39 -0700864 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800865 if (tree.setTo(asset->getBuffer(true),
866 asset->getLength()) != NO_ERROR) {
867 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
868 goto bail;
869 }
870 tree.restart();
871
872 if (strcmp("permissions", option) == 0) {
873 size_t len;
874 ResXMLTree::event_code_t code;
875 int depth = 0;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800876 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
877 code != ResXMLTree::BAD_DOCUMENT) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800878 if (code == ResXMLTree::END_TAG) {
879 depth--;
880 continue;
881 }
882 if (code != ResXMLTree::START_TAG) {
883 continue;
884 }
885 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700886 const char16_t* ctag16 = tree.getElementName(&len);
887 if (ctag16 == NULL) {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700888 SourcePos(manifestFile, tree.getLineNumber()).error(
889 "ERROR: failed to get XML element name (bad string pool)");
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700890 goto bail;
891 }
892 String8 tag(ctag16);
Tomasz Wasilczykade06312023-08-10 23:54:44 +0000893 //printf("Depth %d tag %s\n", depth, tag.c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -0800894 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);
Tomasz Wasilczykade06312023-08-10 23:54:44 +0000901 printf("package: %s\n", ResTable::normalizeForOutput(pkg.c_str()).c_str());
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(
Tomasz Wasilczykade06312023-08-10 23:54:44 +0000908 "ERROR getting 'android:name': %s", error.c_str());
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",
Tomasz Wasilczykade06312023-08-10 23:54:44 +0000918 ResTable::normalizeForOutput(name.c_str()).c_str());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800919 } 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(
Tomasz Wasilczykade06312023-08-10 23:54:44 +0000924 "ERROR getting 'android:name' attribute: %s", error.c_str());
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(
Tomasz Wasilczykade06312023-08-10 23:54:44 +0000941 "ERROR getting 'android:name' attribute: %s", error.c_str());
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(
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001141 supportedInput[i].c_str()).c_str());
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' ",
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001160 ResTable::normalizeForOutput(aName.c_str()).c_str());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001161 }
1162 printf(" label='%s' icon='%s'\n",
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001163 ResTable::normalizeForOutput(activityLabel.c_str())
1164 .c_str(),
1165 ResTable::normalizeForOutput(activityIcon.c_str())
1166 .c_str());
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' ",
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001172 ResTable::normalizeForOutput(aName.c_str()).c_str());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001173 }
1174 printf(" label='%s' icon='%s' banner='%s'\n",
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001175 ResTable::normalizeForOutput(activityLabel.c_str())
1176 .c_str(),
1177 ResTable::normalizeForOutput(activityIcon.c_str())
1178 .c_str(),
1179 ResTable::normalizeForOutput(activityBanner.c_str())
1180 .c_str());
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);
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001268 //printf("Depth %d, %s\n", depth, tag.c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001269 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' ",
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001277 ResTable::normalizeForOutput(pkg.c_str()).c_str());
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",
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001283 error.c_str());
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",
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001296 error.c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001297 goto bail;
1298 }
Adam Lesinski25d35a92014-08-11 09:41:56 -07001299 printf("versionName='%s'",
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001300 ResTable::normalizeForOutput(versionName.c_str()).c_str());
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(
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001305 splitName.c_str()).c_str());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001306 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001307
Jackal Guo201a60a2021-08-31 12:37:30 +08001308 // For 'platformBuildVersionName', using both string and int type as a fallback
1309 // since it may be the code name of Android or the API level.
Alan Viverette11be9312017-11-09 15:41:44 -05001310 String8 platformBuildVersionName = AaptXml::getAttribute(tree, NULL,
Adam Lesinski5283fab2014-08-29 11:23:55 -07001311 "platformBuildVersionName");
Jackal Guo201a60a2021-08-31 12:37:30 +08001312 int32_t platformBuildVersionNameInt =
1313 AaptXml::getIntegerAttribute(tree, NULL, "platformBuildVersionName", 0,
1314 NULL);
Alan Viverette11be9312017-11-09 15:41:44 -05001315 if (platformBuildVersionName != "") {
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001316 printf(" platformBuildVersionName='%s'", platformBuildVersionName.c_str());
Jackal Guo201a60a2021-08-31 12:37:30 +08001317 } else if (platformBuildVersionNameInt > 0) {
1318 printf(" platformBuildVersionName='%d'", platformBuildVersionNameInt);
Alan Viverette11be9312017-11-09 15:41:44 -05001319 }
1320
Jackal Guo201a60a2021-08-31 12:37:30 +08001321 // For 'platformBuildVersionCode', using both string and int type as a fallback
1322 // since it may be the code name of Android or the API level.
Alan Viverette11be9312017-11-09 15:41:44 -05001323 String8 platformBuildVersionCode = AaptXml::getAttribute(tree, NULL,
1324 "platformBuildVersionCode");
Jackal Guo201a60a2021-08-31 12:37:30 +08001325 int32_t platformBuildVersionCodeInt =
1326 AaptXml::getIntegerAttribute(tree, NULL, "platformBuildVersionCode", 0,
1327 NULL);
Alan Viverette11be9312017-11-09 15:41:44 -05001328 if (platformBuildVersionCode != "") {
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001329 printf(" platformBuildVersionCode='%s'", platformBuildVersionCode.c_str());
Jackal Guo201a60a2021-08-31 12:37:30 +08001330 } else if (platformBuildVersionCodeInt > 0) {
1331 printf(" platformBuildVersionCode='%d'", platformBuildVersionCodeInt);
Alan Viverette11be9312017-11-09 15:41:44 -05001332 }
1333
1334 int32_t compileSdkVersion = AaptXml::getIntegerAttribute(tree,
1335 COMPILE_SDK_VERSION_ATTR, &error);
1336 if (error != "") {
1337 SourcePos(manifestFile, tree.getLineNumber()).error(
1338 "ERROR getting 'android:compileSdkVersion' attribute: %s",
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001339 error.c_str());
Alan Viverette11be9312017-11-09 15:41:44 -05001340 goto bail;
1341 }
1342 if (compileSdkVersion > 0) {
1343 printf(" compileSdkVersion='%d'", compileSdkVersion);
1344 }
1345
1346 String8 compileSdkVersionCodename = AaptXml::getResolvedAttribute(res, tree,
1347 COMPILE_SDK_VERSION_CODENAME_ATTR, &error);
1348 if (compileSdkVersionCodename != "") {
1349 printf(" compileSdkVersionCodename='%s'", ResTable::normalizeForOutput(
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001350 compileSdkVersionCodename.c_str()).c_str());
Alan Viverette11be9312017-11-09 15:41:44 -05001351 }
1352
Adam Lesinski25d35a92014-08-11 09:41:56 -07001353 printf("\n");
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001354
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001355 int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
1356 INSTALL_LOCATION_ATTR, &error);
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001357 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001358 SourcePos(manifestFile, tree.getLineNumber()).error(
1359 "ERROR getting 'android:installLocation' attribute: %s",
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001360 error.c_str());
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001361 goto bail;
1362 }
1363
1364 if (installLocation >= 0) {
1365 printf("install-location:'");
1366 switch (installLocation) {
1367 case 0:
1368 printf("auto");
1369 break;
1370 case 1:
1371 printf("internalOnly");
1372 break;
1373 case 2:
1374 printf("preferExternal");
1375 break;
1376 default:
1377 fprintf(stderr, "Invalid installLocation %d\n", installLocation);
1378 goto bail;
1379 }
1380 printf("'\n");
1381 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001382 } else if (depth == 2) {
1383 withinApplication = false;
1384 if (tag == "application") {
1385 withinApplication = true;
1386
1387 String8 label;
1388 const size_t NL = locales.size();
1389 for (size_t i=0; i<NL; i++) {
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001390 const char* localeStr = locales[i].c_str();
Adam Lesinskia77685f2016-10-03 16:26:28 -07001391 assets.setConfiguration(config, localeStr != NULL ? localeStr : "");
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001392 String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1393 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001394 if (llabel != "") {
1395 if (localeStr == NULL || strlen(localeStr) == 0) {
1396 label = llabel;
Maurice Chu2675f762013-10-22 17:33:11 -07001397 printf("application-label:'%s'\n",
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001398 ResTable::normalizeForOutput(llabel.c_str()).c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001399 } else {
1400 if (label == "") {
1401 label = llabel;
1402 }
1403 printf("application-label-%s:'%s'\n", localeStr,
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001404 ResTable::normalizeForOutput(llabel.c_str()).c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001405 }
1406 }
1407 }
1408
1409 ResTable_config tmpConfig = config;
1410 const size_t ND = densities.size();
1411 for (size_t i=0; i<ND; i++) {
1412 tmpConfig.density = densities[i];
1413 assets.setConfiguration(tmpConfig);
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001414 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1415 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001416 if (icon != "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001417 printf("application-icon-%d:'%s'\n", densities[i],
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001418 ResTable::normalizeForOutput(icon.c_str()).c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001419 }
1420 }
1421 assets.setConfiguration(config);
1422
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001423 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001424 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001425 SourcePos(manifestFile, tree.getLineNumber()).error(
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001426 "ERROR getting 'android:icon' attribute: %s", error.c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001427 goto bail;
1428 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001429 int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0,
1430 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001431 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001432 SourcePos(manifestFile, tree.getLineNumber()).error(
1433 "ERROR getting 'android:testOnly' attribute: %s",
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001434 error.c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001435 goto bail;
1436 }
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001437
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001438 String8 banner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1439 &error);
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001440 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001441 SourcePos(manifestFile, tree.getLineNumber()).error(
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001442 "ERROR getting 'android:banner' attribute: %s", error.c_str());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001443 goto bail;
1444 }
Maurice Chu2675f762013-10-22 17:33:11 -07001445 printf("application: label='%s' ",
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001446 ResTable::normalizeForOutput(label.c_str()).c_str());
1447 printf("icon='%s'", ResTable::normalizeForOutput(icon.c_str()).c_str());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001448 if (banner != "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001449 printf(" banner='%s'",
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001450 ResTable::normalizeForOutput(banner.c_str()).c_str());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001451 }
1452 printf("\n");
Adam Lesinski282e1812014-01-23 18:17:42 -08001453 if (testOnly != 0) {
1454 printf("testOnly='%d'\n", testOnly);
1455 }
1456
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001457 int32_t isGame = AaptXml::getResolvedIntegerAttribute(res, tree,
1458 ISGAME_ATTR, 0, &error);
1459 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001460 SourcePos(manifestFile, tree.getLineNumber()).error(
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001461 "ERROR getting 'android:isGame' attribute: %s", error.c_str());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001462 goto bail;
1463 }
1464 if (isGame != 0) {
1465 printf("application-isGame\n");
1466 }
1467
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001468 int32_t debuggable = AaptXml::getResolvedIntegerAttribute(res, tree,
1469 DEBUGGABLE_ATTR, 0, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001470 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001471 SourcePos(manifestFile, tree.getLineNumber()).error(
1472 "ERROR getting 'android:debuggable' attribute: %s",
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001473 error.c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001474 goto bail;
1475 }
1476 if (debuggable != 0) {
1477 printf("application-debuggable\n");
1478 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07001479
1480 // We must search by name because the multiArch flag hasn't been API
1481 // frozen yet.
1482 int32_t multiArchIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
1483 "multiArch");
1484 if (multiArchIndex >= 0) {
1485 Res_value value;
1486 if (tree.getAttributeValue(multiArchIndex, &value) != NO_ERROR) {
1487 if (value.dataType >= Res_value::TYPE_FIRST_INT &&
1488 value.dataType <= Res_value::TYPE_LAST_INT) {
1489 hasMultiArch = value.data;
1490 }
1491 }
1492 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001493 } else if (tag == "uses-sdk") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001494 int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR,
1495 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001496 if (error != "") {
1497 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001498 String8 name = AaptXml::getResolvedAttribute(res, tree,
1499 MIN_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001500 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001501 SourcePos(manifestFile, tree.getLineNumber()).error(
1502 "ERROR getting 'android:minSdkVersion' attribute: %s",
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001503 error.c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001504 goto bail;
1505 }
Jackal Guo201a60a2021-08-31 12:37:30 +08001506 if (name == "Donut") targetSdk = SDK_DONUT;
Maurice Chu2675f762013-10-22 17:33:11 -07001507 printf("sdkVersion:'%s'\n",
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001508 ResTable::normalizeForOutput(name.c_str()).c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001509 } else if (code != -1) {
1510 targetSdk = code;
1511 printf("sdkVersion:'%d'\n", code);
1512 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001513 code = AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -08001514 if (code != -1) {
1515 printf("maxSdkVersion:'%d'\n", code);
1516 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001517 code = AaptXml::getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001518 if (error != "") {
1519 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001520 String8 name = AaptXml::getResolvedAttribute(res, tree,
1521 TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001522 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001523 SourcePos(manifestFile, tree.getLineNumber()).error(
1524 "ERROR getting 'android:targetSdkVersion' attribute: %s",
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001525 error.c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001526 goto bail;
1527 }
Jackal Guo201a60a2021-08-31 12:37:30 +08001528 if (name == "Donut" && targetSdk < SDK_DONUT) {
1529 targetSdk = SDK_DONUT;
1530 } else if (name != "" && targetSdk == 0) {
1531 // Bump to current development version
1532 targetSdk = SDK_CUR_DEVELOPMENT;
1533 }
Maurice Chu2675f762013-10-22 17:33:11 -07001534 printf("targetSdkVersion:'%s'\n",
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001535 ResTable::normalizeForOutput(name.c_str()).c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001536 } else if (code != -1) {
1537 if (targetSdk < code) {
1538 targetSdk = code;
1539 }
1540 printf("targetSdkVersion:'%d'\n", code);
1541 }
1542 } else if (tag == "uses-configuration") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001543 int32_t reqTouchScreen = AaptXml::getIntegerAttribute(tree,
1544 REQ_TOUCH_SCREEN_ATTR, 0);
1545 int32_t reqKeyboardType = AaptXml::getIntegerAttribute(tree,
1546 REQ_KEYBOARD_TYPE_ATTR, 0);
1547 int32_t reqHardKeyboard = AaptXml::getIntegerAttribute(tree,
1548 REQ_HARD_KEYBOARD_ATTR, 0);
1549 int32_t reqNavigation = AaptXml::getIntegerAttribute(tree,
1550 REQ_NAVIGATION_ATTR, 0);
1551 int32_t reqFiveWayNav = AaptXml::getIntegerAttribute(tree,
1552 REQ_FIVE_WAY_NAV_ATTR, 0);
Adam Lesinski282e1812014-01-23 18:17:42 -08001553 printf("uses-configuration:");
1554 if (reqTouchScreen != 0) {
1555 printf(" reqTouchScreen='%d'", reqTouchScreen);
1556 }
1557 if (reqKeyboardType != 0) {
1558 printf(" reqKeyboardType='%d'", reqKeyboardType);
1559 }
1560 if (reqHardKeyboard != 0) {
1561 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1562 }
1563 if (reqNavigation != 0) {
1564 printf(" reqNavigation='%d'", reqNavigation);
1565 }
1566 if (reqFiveWayNav != 0) {
1567 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1568 }
1569 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001570 } else if (tag == "supports-input") {
1571 withinSupportsInput = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001572 } else if (tag == "supports-screens") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001573 smallScreen = AaptXml::getIntegerAttribute(tree,
1574 SMALL_SCREEN_ATTR, 1);
1575 normalScreen = AaptXml::getIntegerAttribute(tree,
1576 NORMAL_SCREEN_ATTR, 1);
1577 largeScreen = AaptXml::getIntegerAttribute(tree,
1578 LARGE_SCREEN_ATTR, 1);
1579 xlargeScreen = AaptXml::getIntegerAttribute(tree,
1580 XLARGE_SCREEN_ATTR, 1);
1581 anyDensity = AaptXml::getIntegerAttribute(tree,
1582 ANY_DENSITY_ATTR, 1);
1583 requiresSmallestWidthDp = AaptXml::getIntegerAttribute(tree,
1584 REQUIRES_SMALLEST_WIDTH_DP_ATTR, 0);
1585 compatibleWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1586 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, 0);
1587 largestWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1588 LARGEST_WIDTH_LIMIT_DP_ATTR, 0);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001589 } else if (tag == "feature-group") {
1590 withinFeatureGroup = true;
1591 FeatureGroup group;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001592 group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001593 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001594 SourcePos(manifestFile, tree.getLineNumber()).error(
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001595 "ERROR getting 'android:label' attribute: %s", error.c_str());
Adam Lesinski2c72b682014-06-24 09:56:01 -07001596 goto bail;
1597 }
1598 featureGroups.add(group);
1599
Adam Lesinski282e1812014-01-23 18:17:42 -08001600 } else if (tag == "uses-feature") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001601 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001602 if (name != "" && error == "") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001603 const char* androidSchema =
1604 "http://schemas.android.com/apk/res/android";
Adam Lesinski282e1812014-01-23 18:17:42 -08001605
Adam Lesinski694d0a72016-04-06 16:12:04 -07001606 int32_t req = AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1,
1607 &error);
1608 if (error != "") {
1609 SourcePos(manifestFile, tree.getLineNumber()).error(
1610 "failed to read attribute 'android:required': %s",
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001611 error.c_str());
Adam Lesinski694d0a72016-04-06 16:12:04 -07001612 goto bail;
1613 }
1614
1615 int32_t version = AaptXml::getIntegerAttribute(tree, androidSchema,
1616 "version", 0, &error);
1617 if (error != "") {
1618 SourcePos(manifestFile, tree.getLineNumber()).error(
1619 "failed to read attribute 'android:version': %s",
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001620 error.c_str());
Adam Lesinski694d0a72016-04-06 16:12:04 -07001621 goto bail;
1622 }
1623
1624 commonFeatures.features.add(name, Feature(req != 0, version));
Adam Lesinski2c72b682014-06-24 09:56:01 -07001625 if (req) {
1626 addParentFeatures(&commonFeatures, name);
Adam Lesinski282e1812014-01-23 18:17:42 -08001627 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001628 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001629 int vers = AaptXml::getIntegerAttribute(tree,
Adam Lesinski282e1812014-01-23 18:17:42 -08001630 GL_ES_VERSION_ATTR, &error);
1631 if (error == "") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001632 if (vers > commonFeatures.openGLESVersion) {
1633 commonFeatures.openGLESVersion = vers;
1634 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001635 }
1636 }
1637 } else if (tag == "uses-permission") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001638 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001639 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001640 SourcePos(manifestFile, tree.getLineNumber()).error(
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001641 "ERROR getting 'android:name' attribute: %s", error.c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001642 goto bail;
1643 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001644
1645 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001646 SourcePos(manifestFile, tree.getLineNumber()).error(
1647 "ERROR: missing 'android:name' for uses-permission");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001648 goto bail;
1649 }
1650
1651 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, false);
1652
Adam Lesinski2386df22016-12-28 15:08:58 -05001653 const int32_t maxSdkVersion =
1654 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, -1);
Dianne Hackborncd154e92017-02-28 17:37:35 -08001655 const String8 requiredFeature = AaptXml::getAttribute(tree,
1656 REQUIRED_FEATURE_ATTR, &error);
1657 const String8 requiredNotFeature = AaptXml::getAttribute(tree,
1658 REQUIRED_NOT_FEATURE_ATTR, &error);
Adam Lesinski2386df22016-12-28 15:08:58 -05001659
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001660 if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1661 hasWriteExternalStoragePermission = true;
Adam Lesinski2386df22016-12-28 15:08:58 -05001662 writeExternalStoragePermissionMaxSdkVersion = maxSdkVersion;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001663 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1664 hasReadExternalStoragePermission = true;
1665 } else if (name == "android.permission.READ_PHONE_STATE") {
1666 hasReadPhoneStatePermission = true;
1667 } else if (name == "android.permission.READ_CONTACTS") {
1668 hasReadContactsPermission = true;
1669 } else if (name == "android.permission.WRITE_CONTACTS") {
1670 hasWriteContactsPermission = true;
1671 } else if (name == "android.permission.READ_CALL_LOG") {
1672 hasReadCallLogPermission = true;
1673 } else if (name == "android.permission.WRITE_CALL_LOG") {
1674 hasWriteCallLogPermission = true;
1675 }
1676
1677 printUsesPermission(name,
1678 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
Dianne Hackborncd154e92017-02-28 17:37:35 -08001679 maxSdkVersion, requiredFeature, requiredNotFeature);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001680
1681 } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
1682 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1683 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001684 SourcePos(manifestFile, tree.getLineNumber()).error(
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001685 "ERROR getting 'android:name' attribute: %s", error.c_str());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001686 goto bail;
1687 }
1688
1689 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001690 SourcePos(manifestFile, tree.getLineNumber()).error(
1691 "ERROR: missing 'android:name' for uses-permission-sdk-23");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001692 goto bail;
1693 }
1694
1695 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, true);
1696
1697 printUsesPermissionSdk23(
1698 name, AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
1699
Adam Lesinski282e1812014-01-23 18:17:42 -08001700 } else if (tag == "uses-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001701 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001702 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001703 printf("uses-package:'%s'\n",
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001704 ResTable::normalizeForOutput(name.c_str()).c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001705 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001706 SourcePos(manifestFile, tree.getLineNumber()).error(
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001707 "ERROR getting 'android:name' attribute: %s", error.c_str());
Adam Lesinski10de3af12016-07-13 10:14:03 -07001708 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001709 }
1710 } else if (tag == "original-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001711 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001712 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001713 printf("original-package:'%s'\n",
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001714 ResTable::normalizeForOutput(name.c_str()).c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001715 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001716 SourcePos(manifestFile, tree.getLineNumber()).error(
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001717 "ERROR getting 'android:name' attribute: %s", error.c_str());
Adam Lesinski10de3af12016-07-13 10:14:03 -07001718 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001719 }
1720 } else if (tag == "supports-gl-texture") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001721 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001722 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001723 printf("supports-gl-texture:'%s'\n",
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001724 ResTable::normalizeForOutput(name.c_str()).c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001725 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001726 SourcePos(manifestFile, tree.getLineNumber()).error(
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001727 "ERROR getting 'android:name' attribute: %s", error.c_str());
Adam Lesinski10de3af12016-07-13 10:14:03 -07001728 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001729 }
1730 } else if (tag == "compatible-screens") {
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001731 printCompatibleScreens(tree, &error);
1732 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001733 SourcePos(manifestFile, tree.getLineNumber()).error(
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001734 "ERROR getting compatible screens: %s", error.c_str());
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001735 goto bail;
1736 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001737 depth--;
1738 } else if (tag == "package-verifier") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001739 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001740 if (name != "" && error == "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001741 String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR,
1742 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001743 if (publicKey != "" && error == "") {
1744 printf("package-verifier: name='%s' publicKey='%s'\n",
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001745 ResTable::normalizeForOutput(name.c_str()).c_str(),
1746 ResTable::normalizeForOutput(publicKey.c_str()).c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001747 }
1748 }
1749 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001750 } else if (depth == 3) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001751 withinActivity = false;
1752 withinReceiver = false;
1753 withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001754 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001755 hasIntentFilter = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001756 hasMetaHostPaymentCategory = false;
1757 hasMetaOffHostPaymentCategory = false;
1758 hasBindDeviceAdminPermission = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001759 hasBindAccessibilityServicePermission = false;
1760 hasBindPrintServicePermission = false;
1761 hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001762 hasRequiredSafAttributes = false;
1763 hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001764 hasBindDreamServicePermission = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001765 if (withinApplication) {
1766 if(tag == "activity") {
1767 withinActivity = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001768 activityName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001769 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001770 SourcePos(manifestFile, tree.getLineNumber()).error(
1771 "ERROR getting 'android:name' attribute: %s",
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001772 error.c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001773 goto bail;
1774 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001775
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001776 activityLabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1777 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001778 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001779 SourcePos(manifestFile, tree.getLineNumber()).error(
1780 "ERROR getting 'android:label' attribute: %s",
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001781 error.c_str());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001782 goto bail;
1783 }
1784
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001785 activityIcon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1786 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001787 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001788 SourcePos(manifestFile, tree.getLineNumber()).error(
1789 "ERROR getting 'android:icon' attribute: %s",
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001790 error.c_str());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001791 goto bail;
1792 }
1793
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001794 activityBanner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1795 &error);
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001796 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001797 SourcePos(manifestFile, tree.getLineNumber()).error(
1798 "ERROR getting 'android:banner' attribute: %s",
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001799 error.c_str());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001800 goto bail;
1801 }
1802
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001803 int32_t orien = AaptXml::getResolvedIntegerAttribute(res, tree,
Michael Wrightec4fdec2013-09-06 16:50:52 -07001804 SCREEN_ORIENTATION_ATTR, &error);
1805 if (error == "") {
1806 if (orien == 0 || orien == 6 || orien == 8) {
1807 // Requests landscape, sensorLandscape, or reverseLandscape.
Adam Lesinski43158772015-11-11 15:13:55 -08001808 addImpliedFeature(
1809 &impliedFeatures, "android.hardware.screen.landscape",
1810 String8("one or more activities have specified a "
1811 "landscape orientation"),
1812 false);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001813 } else if (orien == 1 || orien == 7 || orien == 9) {
1814 // Requests portrait, sensorPortrait, or reversePortrait.
Adam Lesinski43158772015-11-11 15:13:55 -08001815 addImpliedFeature(
1816 &impliedFeatures, "android.hardware.screen.portrait",
1817 String8("one or more activities have specified a "
1818 "portrait orientation"),
1819 false);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001820 }
1821 }
1822 } else if (tag == "uses-library") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001823 String8 libraryName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001824 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001825 SourcePos(manifestFile, tree.getLineNumber()).error(
Michael Wrightec4fdec2013-09-06 16:50:52 -07001826 "ERROR getting 'android:name' attribute for uses-library"
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001827 " %s", error.c_str());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001828 goto bail;
1829 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001830 int req = AaptXml::getIntegerAttribute(tree,
1831 REQUIRED_ATTR, 1);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001832 printf("uses-library%s:'%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001833 req ? "" : "-not-required", ResTable::normalizeForOutput(
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001834 libraryName.c_str()).c_str());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001835 } else if (tag == "receiver") {
1836 withinReceiver = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001837 receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001838
1839 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001840 SourcePos(manifestFile, tree.getLineNumber()).error(
Michael Wrightec4fdec2013-09-06 16:50:52 -07001841 "ERROR getting 'android:name' attribute for receiver:"
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001842 " %s", error.c_str());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001843 goto bail;
1844 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001845
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001846 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1847 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001848 if (error == "") {
1849 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1850 hasBindDeviceAdminPermission = true;
1851 }
1852 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001853 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001854 "ERROR getting 'android:permission' attribute for"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001855 " receiver '%s': %s",
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001856 receiverName.c_str(), error.c_str());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001857 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001858 } else if (tag == "service") {
1859 withinService = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001860 serviceName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001861
1862 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001863 SourcePos(manifestFile, tree.getLineNumber()).error(
1864 "ERROR getting 'android:name' attribute for "
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001865 "service:%s", error.c_str());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001866 goto bail;
1867 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001868
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001869 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1870 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001871 if (error == "") {
Yi Kongb172c312022-07-20 15:15:33 +08001872 if (permission ==
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001873 "android.permission.BIND_ACCESSIBILITY_SERVICE") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001874 hasBindAccessibilityServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001875 } else if (permission ==
1876 "android.permission.BIND_PRINT_SERVICE") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001877 hasBindPrintServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001878 } else if (permission ==
1879 "android.permission.BIND_NFC_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07001880 hasBindNfcServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001881 } else if (permission ==
1882 "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001883 hasBindNotificationListenerServicePermission = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001884 } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
1885 hasBindDreamServicePermission = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001886 }
1887 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001888 SourcePos(manifestFile, tree.getLineNumber()).error(
1889 "ERROR getting 'android:permission' attribute for "
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001890 "service '%s': %s", serviceName.c_str(), error.c_str());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001891 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001892 } else if (tag == "provider") {
1893 withinProvider = true;
1894
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001895 bool exported = AaptXml::getResolvedIntegerAttribute(res, tree,
1896 EXPORTED_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001897 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001898 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001899 "ERROR getting 'android:exported' attribute for provider:"
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001900 " %s", error.c_str());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001901 goto bail;
1902 }
1903
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001904 bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute(
1905 res, tree, GRANT_URI_PERMISSIONS_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001906 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001907 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001908 "ERROR getting 'android:grantUriPermissions' attribute for "
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001909 "provider: %s", error.c_str());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001910 goto bail;
1911 }
1912
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001913 String8 permission = AaptXml::getResolvedAttribute(res, tree,
1914 PERMISSION_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001915 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001916 SourcePos(manifestFile, tree.getLineNumber()).error(
1917 "ERROR getting 'android:permission' attribute for "
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001918 "provider: %s", error.c_str());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001919 goto bail;
1920 }
1921
1922 hasRequiredSafAttributes |= exported && grantUriPermissions &&
1923 permission == "android.permission.MANAGE_DOCUMENTS";
1924
Michael Wrightec4fdec2013-09-06 16:50:52 -07001925 } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001926 String8 metaDataName = AaptXml::getResolvedAttribute(res, tree,
1927 NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001928 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001929 SourcePos(manifestFile, tree.getLineNumber()).error(
1930 "ERROR getting 'android:name' attribute for "
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001931 "meta-data: %s", error.c_str());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001932 goto bail;
1933 }
Maurice Chu2675f762013-10-22 17:33:11 -07001934 printf("meta-data: name='%s' ",
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001935 ResTable::normalizeForOutput(metaDataName.c_str()).c_str());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001936 printResolvedResourceAttribute(res, tree, VALUE_ATTR, String8("value"),
Maurice Chu76327312013-10-16 18:28:46 -07001937 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001938 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001939 // Try looking for a RESOURCE_ATTR
1940 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001941 printResolvedResourceAttribute(res, tree, RESOURCE_ATTR,
Maurice Chu76327312013-10-16 18:28:46 -07001942 String8("resource"), &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001943 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001944 SourcePos(manifestFile, tree.getLineNumber()).error(
1945 "ERROR getting 'android:value' or "
Maurice Chu76327312013-10-16 18:28:46 -07001946 "'android:resource' attribute for "
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001947 "meta-data: %s", error.c_str());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001948 goto bail;
1949 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001950 }
Maurice Chu76327312013-10-16 18:28:46 -07001951 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001952 } else if (withinSupportsInput && tag == "input-type") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001953 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001954 if (name != "" && error == "") {
1955 supportedInput.add(name);
1956 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001957 SourcePos(manifestFile, tree.getLineNumber()).error(
1958 "ERROR getting 'android:name' attribute: %s",
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001959 error.c_str());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001960 goto bail;
1961 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001962 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001963 } else if (withinFeatureGroup && tag == "uses-feature") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001964 const String8 androidSchema("http://schemas.android.com/apk/res/android");
Adam Lesinski2c72b682014-06-24 09:56:01 -07001965 FeatureGroup& top = featureGroups.editTop();
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001966
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001967 String8 name = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001968 if (name != "" && error == "") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001969 Feature feature(true);
1970
1971 int32_t featureVers = AaptXml::getIntegerAttribute(
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001972 tree, androidSchema.c_str(), "version", 0, &error);
Adam Lesinski694d0a72016-04-06 16:12:04 -07001973 if (error == "") {
1974 feature.version = featureVers;
1975 } else {
1976 SourcePos(manifestFile, tree.getLineNumber()).error(
1977 "failed to read attribute 'android:version': %s",
Tomasz Wasilczykade06312023-08-10 23:54:44 +00001978 error.c_str());
Adam Lesinski694d0a72016-04-06 16:12:04 -07001979 goto bail;
1980 }
1981
1982 top.features.add(name, feature);
Adam Lesinskid3edfde2014-08-08 17:32:44 -07001983 addParentFeatures(&top, name);
Adam Lesinski694d0a72016-04-06 16:12:04 -07001984
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001985 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001986 int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR,
1987 &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001988 if (error == "") {
1989 if (vers > top.openGLESVersion) {
1990 top.openGLESVersion = vers;
1991 }
1992 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001993 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001994 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001995 } else if (depth == 4) {
1996 if (tag == "intent-filter") {
1997 hasIntentFilter = true;
1998 withinIntentFilter = true;
1999 actMainActivity = false;
2000 actWidgetReceivers = false;
2001 actImeService = false;
2002 actWallpaperService = false;
2003 actAccessibilityService = false;
2004 actPrintService = false;
2005 actDeviceAdminEnabled = false;
2006 actHostApduService = false;
2007 actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002008 actDocumentsProvider = false;
2009 actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04002010 actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002011 actCamera = false;
2012 actCameraSecure = false;
2013 catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07002014 } else if (withinService && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07002015 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07002016 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07002017 SourcePos(manifestFile, tree.getLineNumber()).error(
2018 "ERROR getting 'android:name' attribute for "
Tomasz Wasilczykade06312023-08-10 23:54:44 +00002019 "meta-data tag in service '%s': %s", serviceName.c_str(),
2020 error.c_str());
Adam Lesinski94fc9122013-09-30 17:16:09 -07002021 goto bail;
2022 }
2023
2024 if (name == "android.nfc.cardemulation.host_apdu_service" ||
2025 name == "android.nfc.cardemulation.off_host_apdu_service") {
2026 bool offHost = true;
2027 if (name == "android.nfc.cardemulation.host_apdu_service") {
2028 offHost = false;
2029 }
2030
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07002031 String8 xmlPath = AaptXml::getResolvedAttribute(res, tree,
2032 RESOURCE_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07002033 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07002034 SourcePos(manifestFile, tree.getLineNumber()).error(
2035 "ERROR getting 'android:resource' attribute for "
2036 "meta-data tag in service '%s': %s",
Tomasz Wasilczykade06312023-08-10 23:54:44 +00002037 serviceName.c_str(), error.c_str());
Adam Lesinski94fc9122013-09-30 17:16:09 -07002038 goto bail;
2039 }
2040
2041 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
2042 offHost, &error);
2043 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07002044 SourcePos(manifestFile, tree.getLineNumber()).error(
2045 "ERROR getting AID category for service '%s'",
Tomasz Wasilczykade06312023-08-10 23:54:44 +00002046 serviceName.c_str());
Adam Lesinski94fc9122013-09-30 17:16:09 -07002047 goto bail;
2048 }
2049
2050 const size_t catLen = categories.size();
2051 for (size_t i = 0; i < catLen; i++) {
2052 bool paymentCategory = (categories[i] == "payment");
2053 if (offHost) {
2054 hasMetaOffHostPaymentCategory |= paymentCategory;
2055 } else {
2056 hasMetaHostPaymentCategory |= paymentCategory;
2057 }
2058 }
2059 }
2060 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002061 } else if ((depth == 5) && withinIntentFilter) {
2062 String8 action;
2063 if (tag == "action") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07002064 action = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07002065 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07002066 SourcePos(manifestFile, tree.getLineNumber()).error(
Tomasz Wasilczykade06312023-08-10 23:54:44 +00002067 "ERROR getting 'android:name' attribute: %s", error.c_str());
Adam Lesinskia5018c92013-09-30 16:23:15 -07002068 goto bail;
Michael Wrightec4fdec2013-09-06 16:50:52 -07002069 }
2070
Adam Lesinskia5018c92013-09-30 16:23:15 -07002071 if (withinActivity) {
2072 if (action == "android.intent.action.MAIN") {
2073 isMainActivity = true;
2074 actMainActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002075 } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
2076 action == "android.media.action.VIDEO_CAMERA") {
2077 actCamera = true;
2078 } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
2079 actCameraSecure = true;
Michael Wrightec4fdec2013-09-06 16:50:52 -07002080 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002081 } else if (withinReceiver) {
2082 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
2083 actWidgetReceivers = true;
2084 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
2085 actDeviceAdminEnabled = true;
2086 }
2087 } else if (withinService) {
2088 if (action == "android.view.InputMethod") {
2089 actImeService = true;
2090 } else if (action == "android.service.wallpaper.WallpaperService") {
2091 actWallpaperService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002092 } else if (action ==
2093 "android.accessibilityservice.AccessibilityService") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07002094 actAccessibilityService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002095 } else if (action =="android.printservice.PrintService") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07002096 actPrintService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002097 } else if (action ==
2098 "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07002099 actHostApduService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002100 } else if (action ==
2101 "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07002102 actOffHostApduService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002103 } else if (action ==
2104 "android.service.notification.NotificationListenerService") {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002105 actNotificationListenerService = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04002106 } else if (action == "android.service.dreams.DreamService") {
2107 actDreamService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002108 }
2109 } else if (withinProvider) {
2110 if (action == "android.content.action.DOCUMENTS_PROVIDER") {
2111 actDocumentsProvider = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07002112 }
2113 }
2114 if (action == "android.intent.action.SEARCH") {
2115 isSearchable = true;
2116 }
2117 }
2118
2119 if (tag == "category") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07002120 String8 category = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07002121 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07002122 SourcePos(manifestFile, tree.getLineNumber()).error(
Tomasz Wasilczykade06312023-08-10 23:54:44 +00002123 "ERROR getting 'name' attribute: %s", error.c_str());
Adam Lesinskia5018c92013-09-30 16:23:15 -07002124 goto bail;
2125 }
2126 if (withinActivity) {
2127 if (category == "android.intent.category.LAUNCHER") {
2128 isLauncherActivity = true;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08002129 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
2130 isLeanbackLauncherActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002131 } else if (category == "android.intent.category.HOME") {
2132 catLauncher = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08002133 }
2134 }
2135 }
2136 }
2137 }
2138
2139 // Pre-1.6 implicitly granted permission compatibility logic
Jackal Guo201a60a2021-08-31 12:37:30 +08002140 if (targetSdk < SDK_DONUT) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002141 if (!hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002142 printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
2143 printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
2144 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002145 hasWriteExternalStoragePermission = true;
2146 }
2147 if (!hasReadPhoneStatePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002148 printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
2149 printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
2150 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002151 }
2152 }
2153
2154 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
2155 // force them to always take READ_EXTERNAL_STORAGE as well. We always
2156 // do this (regardless of target API version) because we can't have
2157 // an app with write permission but not read permission.
2158 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
Adam Lesinski2386df22016-12-28 15:08:58 -05002159 printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
2160 false /* optional */, writeExternalStoragePermissionMaxSdkVersion);
Adam Lesinski58f1f362013-11-12 12:59:08 -08002161 printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
Adam Lesinski2386df22016-12-28 15:08:58 -05002162 String8("requested WRITE_EXTERNAL_STORAGE"),
2163 writeExternalStoragePermissionMaxSdkVersion);
Adam Lesinski282e1812014-01-23 18:17:42 -08002164 }
2165
2166 // Pre-JellyBean call log permission compatibility.
Jackal Guo201a60a2021-08-31 12:37:30 +08002167 if (targetSdk < SDK_JELLY_BEAN) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002168 if (!hasReadCallLogPermission && hasReadContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002169 printUsesPermission(String8("android.permission.READ_CALL_LOG"));
2170 printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
2171 String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002172 }
2173 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002174 printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
2175 printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
2176 String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002177 }
2178 }
2179
Adam Lesinskica955a42016-08-01 16:44:29 -07002180 // If the app hasn't declared the touchscreen as a feature requirement (either
2181 // directly or implied, required or not), then the faketouch feature is implied.
2182 if (!hasFeature("android.hardware.touchscreen", commonFeatures, impliedFeatures)) {
2183 addImpliedFeature(&impliedFeatures, "android.hardware.faketouch",
Adam Lesinski43158772015-11-11 15:13:55 -08002184 String8("default feature for all apps"), false);
Adam Lesinskica955a42016-08-01 16:44:29 -07002185 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07002186
2187 const size_t numFeatureGroups = featureGroups.size();
2188 if (numFeatureGroups == 0) {
2189 // If no <feature-group> tags were defined, apply auto-implied features.
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002190 printDefaultFeatureGroup(commonFeatures, impliedFeatures);
Adam Lesinski2c72b682014-06-24 09:56:01 -07002191
2192 } else {
2193 // <feature-group> tags are defined, so we ignore implied features and
2194 for (size_t i = 0; i < numFeatureGroups; i++) {
2195 FeatureGroup& grp = featureGroups.editItemAt(i);
2196
Adam Lesinskid7a94da2014-07-25 14:38:54 -07002197 if (commonFeatures.openGLESVersion > grp.openGLESVersion) {
2198 grp.openGLESVersion = commonFeatures.openGLESVersion;
2199 }
2200
Adam Lesinski2c72b682014-06-24 09:56:01 -07002201 // Merge the features defined in the top level (not inside a <feature-group>)
2202 // with this feature group.
2203 const size_t numCommonFeatures = commonFeatures.features.size();
2204 for (size_t j = 0; j < numCommonFeatures; j++) {
2205 if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07002206 grp.features.add(commonFeatures.features.keyAt(j),
2207 commonFeatures.features[j]);
Adam Lesinski2c72b682014-06-24 09:56:01 -07002208 }
2209 }
2210
Adam Lesinski73a05112014-12-08 12:53:17 -08002211 if (!grp.features.isEmpty()) {
Adam Lesinski2c72b682014-06-24 09:56:01 -07002212 printFeatureGroup(grp);
Adam Lesinski282e1812014-01-23 18:17:42 -08002213 }
2214 }
2215 }
2216
Adam Lesinski282e1812014-01-23 18:17:42 -08002217
Adam Lesinski282e1812014-01-23 18:17:42 -08002218 if (hasWidgetReceivers) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002219 printComponentPresence("app-widget");
Adam Lesinski282e1812014-01-23 18:17:42 -08002220 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002221 if (hasDeviceAdminReceiver) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002222 printComponentPresence("device-admin");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002223 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002224 if (hasImeService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002225 printComponentPresence("ime");
Adam Lesinski282e1812014-01-23 18:17:42 -08002226 }
2227 if (hasWallpaperService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002228 printComponentPresence("wallpaper");
Adam Lesinski282e1812014-01-23 18:17:42 -08002229 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002230 if (hasAccessibilityService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002231 printComponentPresence("accessibility");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002232 }
2233 if (hasPrintService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002234 printComponentPresence("print-service");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002235 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07002236 if (hasPaymentService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002237 printComponentPresence("payment");
2238 }
2239 if (isSearchable) {
2240 printComponentPresence("search");
2241 }
2242 if (hasDocumentsProvider) {
2243 printComponentPresence("document-provider");
2244 }
2245 if (hasLauncher) {
2246 printComponentPresence("launcher");
2247 }
2248 if (hasNotificationListenerService) {
2249 printComponentPresence("notification-listener");
2250 }
John Spurlockeb8d1be2014-06-25 17:46:15 -04002251 if (hasDreamService) {
2252 printComponentPresence("dream");
2253 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002254 if (hasCameraActivity) {
2255 printComponentPresence("camera");
2256 }
2257 if (hasCameraSecureActivity) {
2258 printComponentPresence("camera-secure");
2259 }
2260
2261 if (hasMainActivity) {
2262 printf("main\n");
Adam Lesinski94fc9122013-09-30 17:16:09 -07002263 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002264 if (hasOtherActivities) {
2265 printf("other-activities\n");
2266 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002267 if (hasOtherReceivers) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002268 printf("other-receivers\n");
2269 }
2270 if (hasOtherServices) {
2271 printf("other-services\n");
2272 }
2273
2274 // For modern apps, if screen size buckets haven't been specified
2275 // but the new width ranges have, then infer the buckets from them.
2276 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
2277 && requiresSmallestWidthDp > 0) {
2278 int compatWidth = compatibleWidthLimitDp;
2279 if (compatWidth <= 0) {
2280 compatWidth = requiresSmallestWidthDp;
2281 }
2282 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
2283 smallScreen = -1;
2284 } else {
2285 smallScreen = 0;
2286 }
2287 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
2288 normalScreen = -1;
2289 } else {
2290 normalScreen = 0;
2291 }
2292 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
2293 largeScreen = -1;
2294 } else {
2295 largeScreen = 0;
2296 }
2297 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
2298 xlargeScreen = -1;
2299 } else {
2300 xlargeScreen = 0;
2301 }
2302 }
2303
2304 // Determine default values for any unspecified screen sizes,
2305 // based on the target SDK of the package. As of 4 (donut)
2306 // the screen size support was introduced, so all default to
2307 // enabled.
2308 if (smallScreen > 0) {
Jackal Guo201a60a2021-08-31 12:37:30 +08002309 smallScreen = targetSdk >= SDK_DONUT ? -1 : 0;
Adam Lesinski282e1812014-01-23 18:17:42 -08002310 }
2311 if (normalScreen > 0) {
2312 normalScreen = -1;
2313 }
2314 if (largeScreen > 0) {
Jackal Guo201a60a2021-08-31 12:37:30 +08002315 largeScreen = targetSdk >= SDK_DONUT ? -1 : 0;
Adam Lesinski282e1812014-01-23 18:17:42 -08002316 }
2317 if (xlargeScreen > 0) {
2318 // Introduced in Gingerbread.
Jackal Guo201a60a2021-08-31 12:37:30 +08002319 xlargeScreen = targetSdk >= SDK_GINGERBREAD ? -1 : 0;
Adam Lesinski282e1812014-01-23 18:17:42 -08002320 }
2321 if (anyDensity > 0) {
Jackal Guo201a60a2021-08-31 12:37:30 +08002322 anyDensity = (targetSdk >= SDK_DONUT || requiresSmallestWidthDp > 0 ||
2323 compatibleWidthLimitDp > 0)
2324 ? -1
2325 : 0;
Adam Lesinski282e1812014-01-23 18:17:42 -08002326 }
2327 printf("supports-screens:");
2328 if (smallScreen != 0) {
2329 printf(" 'small'");
2330 }
2331 if (normalScreen != 0) {
2332 printf(" 'normal'");
2333 }
2334 if (largeScreen != 0) {
2335 printf(" 'large'");
2336 }
2337 if (xlargeScreen != 0) {
2338 printf(" 'xlarge'");
2339 }
2340 printf("\n");
2341 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
2342 if (requiresSmallestWidthDp > 0) {
2343 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
2344 }
2345 if (compatibleWidthLimitDp > 0) {
2346 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
2347 }
2348 if (largestWidthLimitDp > 0) {
2349 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
2350 }
2351
2352 printf("locales:");
2353 const size_t NL = locales.size();
2354 for (size_t i=0; i<NL; i++) {
Tomasz Wasilczykade06312023-08-10 23:54:44 +00002355 const char* localeStr = locales[i].c_str();
Adam Lesinski282e1812014-01-23 18:17:42 -08002356 if (localeStr == NULL || strlen(localeStr) == 0) {
2357 localeStr = "--_--";
2358 }
2359 printf(" '%s'", localeStr);
2360 }
2361 printf("\n");
2362
2363 printf("densities:");
2364 const size_t ND = densities.size();
2365 for (size_t i=0; i<ND; i++) {
2366 printf(" '%d'", densities[i]);
2367 }
2368 printf("\n");
2369
2370 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
2371 if (dir != NULL) {
2372 if (dir->getFileCount() > 0) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002373 SortedVector<String8> architectures;
Adam Lesinski282e1812014-01-23 18:17:42 -08002374 for (size_t i=0; i<dir->getFileCount(); i++) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002375 architectures.add(ResTable::normalizeForOutput(
Tomasz Wasilczykade06312023-08-10 23:54:44 +00002376 dir->getFileName(i).c_str()));
Adam Lesinski282e1812014-01-23 18:17:42 -08002377 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07002378
2379 bool outputAltNativeCode = false;
2380 // A multiArch package is one that contains 64-bit and
2381 // 32-bit versions of native code and expects 3rd-party
2382 // apps to load these native code libraries. Since most
2383 // 64-bit systems also support 32-bit apps, the apps
2384 // loading this multiArch package's code may be either
2385 // 32-bit or 64-bit.
2386 if (hasMultiArch) {
2387 // If this is a multiArch package, report the 64-bit
2388 // version only. Then as a separate entry, report the
2389 // rest.
2390 //
2391 // If we report the 32-bit architecture, this APK will
2392 // be installed on a 32-bit device, causing a large waste
2393 // of bandwidth and disk space. This assumes that
2394 // the developer of the multiArch package has also
2395 // made a version that is 32-bit only.
2396 String8 intel64("x86_64");
2397 String8 arm64("arm64-v8a");
2398 ssize_t index = architectures.indexOf(intel64);
2399 if (index < 0) {
2400 index = architectures.indexOf(arm64);
2401 }
2402
2403 if (index >= 0) {
Tomasz Wasilczykade06312023-08-10 23:54:44 +00002404 printf("native-code: '%s'\n", architectures[index].c_str());
Adam Lesinskie47fd122014-08-15 22:25:36 -07002405 architectures.removeAt(index);
2406 outputAltNativeCode = true;
2407 }
2408 }
2409
2410 const size_t archCount = architectures.size();
2411 if (archCount > 0) {
2412 if (outputAltNativeCode) {
2413 printf("alt-");
2414 }
2415 printf("native-code:");
2416 for (size_t i = 0; i < archCount; i++) {
Tomasz Wasilczykade06312023-08-10 23:54:44 +00002417 printf(" '%s'", architectures[i].c_str());
Adam Lesinskie47fd122014-08-15 22:25:36 -07002418 }
2419 printf("\n");
2420 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002421 }
2422 delete dir;
2423 }
2424 } else if (strcmp("badger", option) == 0) {
2425 printf("%s", CONSOLE_DATA);
2426 } else if (strcmp("configurations", option) == 0) {
2427 Vector<ResTable_config> configs;
2428 res.getConfigurations(&configs);
2429 const size_t N = configs.size();
2430 for (size_t i=0; i<N; i++) {
Tomasz Wasilczykade06312023-08-10 23:54:44 +00002431 printf("%s\n", configs[i].toString().c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08002432 }
2433 } else {
2434 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2435 goto bail;
2436 }
2437 }
2438
2439 result = NO_ERROR;
2440
2441bail:
Adam Lesinski10de3af12016-07-13 10:14:03 -07002442 if (SourcePos::hasErrors()) {
2443 SourcePos::printErrors(stderr);
2444 }
2445
Adam Lesinski282e1812014-01-23 18:17:42 -08002446 if (asset) {
2447 delete asset;
2448 }
2449 return (result != NO_ERROR);
2450}
2451
2452
2453/*
2454 * Handle the "add" command, which wants to add files to a new or
2455 * pre-existing archive.
2456 */
2457int doAdd(Bundle* bundle)
2458{
2459 ZipFile* zip = NULL;
2460 status_t result = UNKNOWN_ERROR;
2461 const char* zipFileName;
2462
2463 if (bundle->getUpdate()) {
2464 /* avoid confusion */
2465 fprintf(stderr, "ERROR: can't use '-u' with add\n");
2466 goto bail;
2467 }
2468
2469 if (bundle->getFileSpecCount() < 1) {
2470 fprintf(stderr, "ERROR: must specify zip file name\n");
2471 goto bail;
2472 }
2473 zipFileName = bundle->getFileSpecEntry(0);
2474
2475 if (bundle->getFileSpecCount() < 2) {
2476 fprintf(stderr, "NOTE: nothing to do\n");
2477 goto bail;
2478 }
2479
2480 zip = openReadWrite(zipFileName, true);
2481 if (zip == NULL) {
2482 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2483 goto bail;
2484 }
2485
2486 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2487 const char* fileName = bundle->getFileSpecEntry(i);
2488
Tomasz Wasilczykade06312023-08-10 23:54:44 +00002489 if (strcasecmp(String8(fileName).getPathExtension().c_str(), ".gz") == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002490 printf(" '%s'... (from gzip)\n", fileName);
Tomasz Wasilczykade06312023-08-10 23:54:44 +00002491 result = zip->addGzip(fileName, String8(fileName).getBasePath().c_str(), NULL);
Adam Lesinski282e1812014-01-23 18:17:42 -08002492 } else {
2493 if (bundle->getJunkPath()) {
2494 String8 storageName = String8(fileName).getPathLeaf();
Maurice Chu2675f762013-10-22 17:33:11 -07002495 printf(" '%s' as '%s'...\n", fileName,
Tomasz Wasilczykade06312023-08-10 23:54:44 +00002496 ResTable::normalizeForOutput(storageName.c_str()).c_str());
2497 result = zip->add(fileName, storageName.c_str(),
Adam Lesinski282e1812014-01-23 18:17:42 -08002498 bundle->getCompressionMethod(), NULL);
2499 } else {
2500 printf(" '%s'...\n", fileName);
2501 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2502 }
2503 }
2504 if (result != NO_ERROR) {
2505 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2506 if (result == NAME_NOT_FOUND) {
2507 fprintf(stderr, ": file not found\n");
2508 } else if (result == ALREADY_EXISTS) {
2509 fprintf(stderr, ": already exists in archive\n");
2510 } else {
2511 fprintf(stderr, "\n");
2512 }
2513 goto bail;
2514 }
2515 }
2516
2517 result = NO_ERROR;
2518
2519bail:
2520 delete zip;
2521 return (result != NO_ERROR);
2522}
2523
2524
2525/*
2526 * Delete files from an existing archive.
2527 */
2528int doRemove(Bundle* bundle)
2529{
2530 ZipFile* zip = NULL;
2531 status_t result = UNKNOWN_ERROR;
2532 const char* zipFileName;
2533
2534 if (bundle->getFileSpecCount() < 1) {
2535 fprintf(stderr, "ERROR: must specify zip file name\n");
2536 goto bail;
2537 }
2538 zipFileName = bundle->getFileSpecEntry(0);
2539
2540 if (bundle->getFileSpecCount() < 2) {
2541 fprintf(stderr, "NOTE: nothing to do\n");
2542 goto bail;
2543 }
2544
2545 zip = openReadWrite(zipFileName, false);
2546 if (zip == NULL) {
2547 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2548 zipFileName);
2549 goto bail;
2550 }
2551
2552 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2553 const char* fileName = bundle->getFileSpecEntry(i);
2554 ZipEntry* entry;
2555
2556 entry = zip->getEntryByName(fileName);
2557 if (entry == NULL) {
2558 printf(" '%s' NOT FOUND\n", fileName);
2559 continue;
2560 }
2561
2562 result = zip->remove(entry);
2563
2564 if (result != NO_ERROR) {
2565 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2566 bundle->getFileSpecEntry(i), zipFileName);
2567 goto bail;
2568 }
2569 }
2570
2571 /* update the archive */
2572 zip->flush();
2573
2574bail:
2575 delete zip;
2576 return (result != NO_ERROR);
2577}
2578
Adam Lesinski3921e872014-05-13 10:56:25 -07002579static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002580 const size_t numDirs = dir->getDirs().size();
2581 for (size_t i = 0; i < numDirs; i++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002582 bool ignore = ignoreConfig;
2583 const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
Tomasz Wasilczykade06312023-08-10 23:54:44 +00002584 const char* dirStr = subDir->getLeaf().c_str();
Adam Lesinski3921e872014-05-13 10:56:25 -07002585 if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2586 ignore = true;
2587 }
2588 status_t err = addResourcesToBuilder(subDir, builder, ignore);
Adam Lesinskifab50872014-04-16 14:40:42 -07002589 if (err != NO_ERROR) {
2590 return err;
2591 }
2592 }
2593
2594 const size_t numFiles = dir->getFiles().size();
2595 for (size_t i = 0; i < numFiles; i++) {
2596 sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2597 const size_t numConfigs = gp->getFiles().size();
2598 for (size_t j = 0; j < numConfigs; j++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002599 status_t err = NO_ERROR;
Adam Lesinskic7614e52017-03-16 16:54:23 -07002600 if (ignoreConfig) {
Guang Zhu8c2df712017-03-21 03:53:43 +00002601 err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
Adam Lesinskic7614e52017-03-16 16:54:23 -07002602 } else {
Guang Zhu8c2df712017-03-21 03:53:43 +00002603 err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
Adam Lesinskic7614e52017-03-16 16:54:23 -07002604 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002605 if (err != NO_ERROR) {
2606 fprintf(stderr, "Failed to add %s (%s) to builder.\n",
Tomasz Wasilczykade06312023-08-10 23:54:44 +00002607 gp->getPath().c_str(), gp->getFiles()[j]->getPrintableSource().c_str());
Adam Lesinskifab50872014-04-16 14:40:42 -07002608 return err;
2609 }
2610 }
2611 }
2612 return NO_ERROR;
2613}
2614
2615static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2616 if (split->isBase()) {
2617 return original;
2618 }
2619
2620 String8 ext(original.getPathExtension());
2621 if (ext == String8(".apk")) {
2622 return String8::format("%s_%s%s",
Tomasz Wasilczykade06312023-08-10 23:54:44 +00002623 original.getBasePath().c_str(),
2624 split->getDirectorySafeName().c_str(),
2625 ext.c_str());
Adam Lesinskifab50872014-04-16 14:40:42 -07002626 }
2627
Tomasz Wasilczykade06312023-08-10 23:54:44 +00002628 return String8::format("%s_%s", original.c_str(),
2629 split->getDirectorySafeName().c_str());
Adam Lesinskifab50872014-04-16 14:40:42 -07002630}
Adam Lesinski282e1812014-01-23 18:17:42 -08002631
2632/*
2633 * Package up an asset directory and associated application files.
2634 */
2635int doPackage(Bundle* bundle)
2636{
2637 const char* outputAPKFile;
2638 int retVal = 1;
2639 status_t err;
2640 sp<AaptAssets> assets;
2641 int N;
2642 FILE* fp;
2643 String8 dependencyFile;
Adam Lesinskifab50872014-04-16 14:40:42 -07002644 sp<ApkBuilder> builder;
Adam Lesinski282e1812014-01-23 18:17:42 -08002645
Anton Krumina2ef5c02014-03-12 14:46:44 -07002646 // -c en_XA or/and ar_XB means do pseudolocalization
Adam Lesinskifab50872014-04-16 14:40:42 -07002647 sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2648 err = configFilter->parse(bundle->getConfigurations());
Adam Lesinski282e1812014-01-23 18:17:42 -08002649 if (err != NO_ERROR) {
2650 goto bail;
2651 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002652 if (configFilter->containsPseudo()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002653 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2654 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002655 if (configFilter->containsPseudoBidi()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002656 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
Adam Lesinski282e1812014-01-23 18:17:42 -08002657 }
2658
2659 N = bundle->getFileSpecCount();
2660 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
Adam Lesinski09384302014-01-22 16:07:42 -08002661 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002662 fprintf(stderr, "ERROR: no input files\n");
2663 goto bail;
2664 }
2665
2666 outputAPKFile = bundle->getOutputAPKFile();
2667
2668 // Make sure the filenames provided exist and are of the appropriate type.
2669 if (outputAPKFile) {
2670 FileType type;
2671 type = getFileType(outputAPKFile);
2672 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2673 fprintf(stderr,
2674 "ERROR: output file '%s' exists but is not regular file\n",
2675 outputAPKFile);
2676 goto bail;
2677 }
2678 }
2679
2680 // Load the assets.
2681 assets = new AaptAssets();
2682
2683 // Set up the resource gathering in assets if we're going to generate
2684 // dependency files. Every time we encounter a resource while slurping
2685 // the tree, we'll add it to these stores so we have full resource paths
2686 // to write to a dependency file.
2687 if (bundle->getGenDependencies()) {
2688 sp<FilePathStore> resPathStore = new FilePathStore;
2689 assets->setFullResPaths(resPathStore);
2690 sp<FilePathStore> assetPathStore = new FilePathStore;
2691 assets->setFullAssetPaths(assetPathStore);
2692 }
2693
2694 err = assets->slurpFromArgs(bundle);
2695 if (err < 0) {
2696 goto bail;
2697 }
2698
2699 if (bundle->getVerbose()) {
2700 assets->print(String8());
2701 }
2702
Adam Lesinskifab50872014-04-16 14:40:42 -07002703 // Create the ApkBuilder, which will collect the compiled files
2704 // to write to the final APK (or sets of APKs if we are building
2705 // a Split APK.
2706 builder = new ApkBuilder(configFilter);
2707
2708 // If we are generating a Split APK, find out which configurations to split on.
2709 if (bundle->getSplitConfigurations().size() > 0) {
2710 const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2711 const size_t numSplits = splitStrs.size();
2712 for (size_t i = 0; i < numSplits; i++) {
2713 std::set<ConfigDescription> configs;
2714 if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
Tomasz Wasilczykade06312023-08-10 23:54:44 +00002715 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].c_str());
Adam Lesinskifab50872014-04-16 14:40:42 -07002716 goto bail;
2717 }
2718
2719 err = builder->createSplitForConfigs(configs);
2720 if (err != NO_ERROR) {
2721 goto bail;
2722 }
2723 }
2724 }
2725
Adam Lesinski282e1812014-01-23 18:17:42 -08002726 // If they asked for any fileAs that need to be compiled, do so.
2727 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002728 err = buildResources(bundle, assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002729 if (err != 0) {
2730 goto bail;
2731 }
2732 }
2733
2734 // At this point we've read everything and processed everything. From here
2735 // on out it's just writing output files.
2736 if (SourcePos::hasErrors()) {
2737 goto bail;
2738 }
2739
2740 // Update symbols with information about which ones are needed as Java symbols.
2741 assets->applyJavaSymbols();
2742 if (SourcePos::hasErrors()) {
2743 goto bail;
2744 }
2745
2746 // If we've been asked to generate a dependency file, do that here
2747 if (bundle->getGenDependencies()) {
2748 // If this is the packaging step, generate the dependency file next to
2749 // the output apk (e.g. bin/resources.ap_.d)
2750 if (outputAPKFile) {
2751 dependencyFile = String8(outputAPKFile);
2752 // Add the .d extension to the dependency file.
2753 dependencyFile.append(".d");
2754 } else {
2755 // Else if this is the R.java dependency generation step,
2756 // generate the dependency file in the R.java package subdirectory
2757 // e.g. gen/com/foo/app/R.java.d
2758 dependencyFile = String8(bundle->getRClassDir());
2759 dependencyFile.appendPath("R.java.d");
2760 }
2761 // Make sure we have a clean dependency file to start with
2762 fp = fopen(dependencyFile, "w");
2763 fclose(fp);
2764 }
2765
2766 // Write out R.java constants
2767 if (!assets->havePrivateSymbols()) {
2768 if (bundle->getCustomPackage() == NULL) {
2769 // Write the R.java file into the appropriate class directory
2770 // e.g. gen/com/foo/app/R.java
Adam Lesinski1e4663852014-08-15 14:47:28 -07002771 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002772 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002773 } else {
2774 const String8 customPkg(bundle->getCustomPackage());
Adam Lesinski1e4663852014-08-15 14:47:28 -07002775 err = writeResourceSymbols(bundle, assets, customPkg, true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002776 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002777 }
2778 if (err < 0) {
2779 goto bail;
2780 }
2781 // If we have library files, we're going to write our R.java file into
2782 // the appropriate class directory for those libraries as well.
2783 // e.g. gen/com/foo/app/lib/R.java
2784 if (bundle->getExtraPackages() != NULL) {
2785 // Split on colon
2786 String8 libs(bundle->getExtraPackages());
2787 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2788 while (packageString != NULL) {
2789 // Write the R.java file out with the correct package name
Marcin Kosiba0f3a5a62014-09-11 13:48:48 +01002790 err = writeResourceSymbols(bundle, assets, String8(packageString), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002791 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002792 if (err < 0) {
2793 goto bail;
2794 }
2795 packageString = strtok(NULL, ":");
2796 }
2797 libs.unlockBuffer();
2798 }
2799 } else {
Adam Lesinski1e4663852014-08-15 14:47:28 -07002800 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002801 if (err < 0) {
2802 goto bail;
2803 }
Adam Lesinski1e4663852014-08-15 14:47:28 -07002804 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002805 if (err < 0) {
2806 goto bail;
2807 }
2808 }
2809
2810 // Write out the ProGuard file
2811 err = writeProguardFile(bundle, assets);
2812 if (err < 0) {
2813 goto bail;
2814 }
2815
Rohit Agrawal86229cb2016-04-21 16:29:58 -07002816 // Write out the Main Dex ProGuard file
2817 err = writeMainDexProguardFile(bundle, assets);
2818 if (err < 0) {
2819 goto bail;
2820 }
2821
Adam Lesinski282e1812014-01-23 18:17:42 -08002822 // Write the apk
2823 if (outputAPKFile) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002824 // Gather all resources and add them to the APK Builder. The builder will then
2825 // figure out which Split they belong in.
2826 err = addResourcesToBuilder(assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002827 if (err != NO_ERROR) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002828 goto bail;
2829 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002830
2831 const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2832 const size_t numSplits = splits.size();
2833 for (size_t i = 0; i < numSplits; i++) {
2834 const sp<ApkSplit>& split = splits[i];
2835 String8 outputPath = buildApkName(String8(outputAPKFile), split);
2836 err = writeAPK(bundle, outputPath, split);
2837 if (err != NO_ERROR) {
Tomasz Wasilczykade06312023-08-10 23:54:44 +00002838 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.c_str());
Adam Lesinskifab50872014-04-16 14:40:42 -07002839 goto bail;
2840 }
2841 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002842 }
2843
2844 // If we've been asked to generate a dependency file, we need to finish up here.
2845 // the writeResourceSymbols and writeAPK functions have already written the target
2846 // half of the dependency file, now we need to write the prerequisites. (files that
2847 // the R.java file or .ap_ file depend on)
2848 if (bundle->getGenDependencies()) {
2849 // Now that writeResourceSymbols or writeAPK has taken care of writing
2850 // the targets to our dependency file, we'll write the prereqs
2851 fp = fopen(dependencyFile, "a+");
2852 fprintf(fp, " : ");
2853 bool includeRaw = (outputAPKFile != NULL);
2854 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2855 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2856 // and therefore was not added to our pathstores during slurping
2857 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2858 fclose(fp);
2859 }
2860
2861 retVal = 0;
2862bail:
2863 if (SourcePos::hasErrors()) {
2864 SourcePos::printErrors(stderr);
2865 }
2866 return retVal;
2867}
2868
2869/*
2870 * Do PNG Crunching
2871 * PRECONDITIONS
2872 * -S flag points to a source directory containing drawable* folders
2873 * -C flag points to destination directory. The folder structure in the
2874 * source directory will be mirrored to the destination (cache) directory
2875 *
2876 * POSTCONDITIONS
2877 * Destination directory will be updated to match the PNG files in
Maurice Chu2675f762013-10-22 17:33:11 -07002878 * the source directory.
Adam Lesinski282e1812014-01-23 18:17:42 -08002879 */
2880int doCrunch(Bundle* bundle)
2881{
2882 fprintf(stdout, "Crunching PNG Files in ");
2883 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2884 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2885
2886 updatePreProcessedCache(bundle);
2887
2888 return NO_ERROR;
2889}
2890
2891/*
2892 * Do PNG Crunching on a single flag
2893 * -i points to a single png file
2894 * -o points to a single png output file
2895 */
2896int doSingleCrunch(Bundle* bundle)
2897{
2898 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2899 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2900
2901 String8 input(bundle->getSingleCrunchInputFile());
2902 String8 output(bundle->getSingleCrunchOutputFile());
2903
2904 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2905 // we can't return the status_t as it gets truncate to the lower 8 bits.
2906 return 42;
2907 }
2908
2909 return NO_ERROR;
2910}
2911
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002912int runInDaemonMode(Bundle* bundle) {
2913 std::cout << "Ready" << std::endl;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002914 for (std::string cmd; std::getline(std::cin, cmd);) {
2915 if (cmd == "quit") {
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002916 return NO_ERROR;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002917 } else if (cmd == "s") {
2918 // Two argument crunch
2919 std::string inputFile, outputFile;
2920 std::getline(std::cin, inputFile);
2921 std::getline(std::cin, outputFile);
2922 bundle->setSingleCrunchInputFile(inputFile.c_str());
2923 bundle->setSingleCrunchOutputFile(outputFile.c_str());
2924 std::cout << "Crunching " << inputFile << std::endl;
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002925 if (doSingleCrunch(bundle) != NO_ERROR) {
2926 std::cout << "Error" << std::endl;
2927 }
2928 std::cout << "Done" << std::endl;
2929 } else {
2930 // in case of invalid command, just bail out.
2931 std::cerr << "Unknown command" << std::endl;
2932 return -1;
2933 }
2934 }
2935 return -1;
2936}
2937
Adam Lesinski282e1812014-01-23 18:17:42 -08002938char CONSOLE_DATA[2925] = {
2939 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2940 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2941 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2942 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2943 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2944 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2945 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2946 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2947 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2948 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2949 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2950 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2951 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2952 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2953 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2954 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2955 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2956 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2957 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2958 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2959 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2960 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2961 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2962 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2963 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2964 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2965 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2966 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2967 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2968 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2969 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2970 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2971 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2972 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2973 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2974 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2975 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2976 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2977 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2978 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2979 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2980 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2981 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2982 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2983 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2984 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2985 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2986 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2987 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2988 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2989 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2990 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2991 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2992 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2993 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2994 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2995 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2996 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2997 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2998 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2999 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
3000 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
3001 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
3002 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
3003 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
3004 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
3005 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
3006 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
3007 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
3008 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
3009 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
3010 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
3011 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3012 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
3013 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
3014 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
3015 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3016 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
3017 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
3018 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
3019 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
3020 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
3021 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
3022 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3023 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
3024 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
3025 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
3026 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
3027 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
3028 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3029 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3030 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
3031 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
3032 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3033 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3034 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
3035 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3036 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
3037 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
3038 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
3039 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3040 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3041 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
3042 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
3043 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
3044 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3045 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
3046 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3047 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3048 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
3049 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
3050 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3051 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3052 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
3053 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3054 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
3055 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
3056 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
3057 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3058 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3059 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
3060 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
3061 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
3062 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
3063 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
3064 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
3065 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
3066 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
3067 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
3068 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3069 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3070 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
3071 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
3072 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
3073 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
3074 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
3075 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3076 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3077 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
3078 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
3079 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
3080 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3081 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
3082 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3083 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
3084 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
3085 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
3086 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3087 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3088 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
3089 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
3090 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
3091 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
3092 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
3093 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3094 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3095 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
3096 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
3097 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3098 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3099 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3100 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3101 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
3102 };