blob: 800466aa587f49bd06be0b737d2da73cf1543b64 [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
Tomasz Wasilczyk804e8192023-08-23 02:22:53 +000015#include <androidfw/PathUtils.h>
Adam Lesinski282e1812014-01-23 18:17:42 -080016#include <utils/Errors.h>
Adam Lesinski2c72b682014-06-24 09:56:01 -070017#include <utils/KeyedVector.h>
18#include <utils/List.h>
19#include <utils/Log.h>
20#include <utils/SortedVector.h>
21#include <utils/threads.h>
22#include <utils/Vector.h>
Adam Lesinski282e1812014-01-23 18:17:42 -080023
Adam Lesinski282e1812014-01-23 18:17:42 -080024#include <errno.h>
Adam Lesinski2c72b682014-06-24 09:56:01 -070025#include <fcntl.h>
Adam Lesinski282e1812014-01-23 18:17:42 -080026
Jerome Dochez6f1280c2014-09-26 10:21:21 -070027#include <iostream>
28#include <string>
29#include <sstream>
30
Adam Lesinski282e1812014-01-23 18:17:42 -080031using namespace android;
32
Adam Lesinski282e1812014-01-23 18:17:42 -080033/*
34 * Open the file read only. The call fails if the file doesn't exist.
35 *
36 * Returns NULL on failure.
37 */
38ZipFile* openReadOnly(const char* fileName)
39{
40 ZipFile* zip;
41 status_t result;
42
43 zip = new ZipFile;
44 result = zip->open(fileName, ZipFile::kOpenReadOnly);
45 if (result != NO_ERROR) {
46 if (result == NAME_NOT_FOUND) {
47 fprintf(stderr, "ERROR: '%s' not found\n", fileName);
48 } else if (result == PERMISSION_DENIED) {
49 fprintf(stderr, "ERROR: '%s' access denied\n", fileName);
50 } else {
51 fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n",
52 fileName);
53 }
54 delete zip;
55 return NULL;
56 }
57
58 return zip;
59}
60
61/*
62 * Open the file read-write. The file will be created if it doesn't
63 * already exist and "okayToCreate" is set.
64 *
65 * Returns NULL on failure.
66 */
67ZipFile* openReadWrite(const char* fileName, bool okayToCreate)
68{
69 ZipFile* zip = NULL;
70 status_t result;
71 int flags;
72
73 flags = ZipFile::kOpenReadWrite;
74 if (okayToCreate) {
75 flags |= ZipFile::kOpenCreate;
76 }
77
78 zip = new ZipFile;
79 result = zip->open(fileName, flags);
80 if (result != NO_ERROR) {
81 delete zip;
82 zip = NULL;
83 goto bail;
84 }
85
86bail:
87 return zip;
88}
89
90
91/*
92 * Return a short string describing the compression method.
93 */
94const char* compressionName(int method)
95{
96 if (method == ZipEntry::kCompressStored) {
97 return "Stored";
98 } else if (method == ZipEntry::kCompressDeflated) {
99 return "Deflated";
100 } else {
101 return "Unknown";
102 }
103}
104
105/*
106 * Return the percent reduction in size (0% == no compression).
107 */
108int calcPercent(long uncompressedLen, long compressedLen)
109{
110 if (!uncompressedLen) {
111 return 0;
112 } else {
113 return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5);
114 }
115}
116
117/*
118 * Handle the "list" command, which can be a simple file dump or
119 * a verbose listing.
120 *
121 * The verbose listing closely matches the output of the Info-ZIP "unzip"
122 * command.
123 */
124int doList(Bundle* bundle)
125{
126 int result = 1;
127 ZipFile* zip = NULL;
128 const ZipEntry* entry;
129 long totalUncLen, totalCompLen;
130 const char* zipFileName;
131
132 if (bundle->getFileSpecCount() != 1) {
133 fprintf(stderr, "ERROR: specify zip file name (only)\n");
134 goto bail;
135 }
136 zipFileName = bundle->getFileSpecEntry(0);
137
138 zip = openReadOnly(zipFileName);
139 if (zip == NULL) {
140 goto bail;
141 }
142
143 int count, i;
144
145 if (bundle->getVerbose()) {
146 printf("Archive: %s\n", zipFileName);
147 printf(
148 " Length Method Size Ratio Offset Date Time CRC-32 Name\n");
149 printf(
150 "-------- ------ ------- ----- ------- ---- ---- ------ ----\n");
151 }
152
153 totalUncLen = totalCompLen = 0;
154
155 count = zip->getNumEntries();
156 for (i = 0; i < count; i++) {
157 entry = zip->getEntryByIndex(i);
158 if (bundle->getVerbose()) {
159 char dateBuf[32];
160 time_t when;
161
162 when = entry->getModWhen();
163 strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M",
164 localtime(&when));
165
166 printf("%8ld %-7.7s %7ld %3d%% %8zd %s %08lx %s\n",
167 (long) entry->getUncompressedLen(),
168 compressionName(entry->getCompressionMethod()),
169 (long) entry->getCompressedLen(),
170 calcPercent(entry->getUncompressedLen(),
171 entry->getCompressedLen()),
172 (size_t) entry->getLFHOffset(),
173 dateBuf,
174 entry->getCRC32(),
175 entry->getFileName());
176 } else {
177 printf("%s\n", entry->getFileName());
178 }
179
180 totalUncLen += entry->getUncompressedLen();
181 totalCompLen += entry->getCompressedLen();
182 }
183
184 if (bundle->getVerbose()) {
185 printf(
186 "-------- ------- --- -------\n");
187 printf("%8ld %7ld %2d%% %d files\n",
188 totalUncLen,
189 totalCompLen,
190 calcPercent(totalUncLen, totalCompLen),
191 zip->getNumEntries());
192 }
193
194 if (bundle->getAndroidList()) {
195 AssetManager assets;
196 if (!assets.addAssetPath(String8(zipFileName), NULL)) {
197 fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n");
198 goto bail;
199 }
200
Elliott Hughesba3fe562015-08-12 14:49:53 -0700201#ifdef __ANDROID__
Andreas Gampeb8dc7bc2014-10-01 19:07:51 -0700202 static const bool kHaveAndroidOs = true;
203#else
204 static const bool kHaveAndroidOs = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800205#endif
Andreas Gampeb8dc7bc2014-10-01 19:07:51 -0700206 const ResTable& res = assets.getResources(false);
207 if (!kHaveAndroidOs) {
208 printf("\nResource table:\n");
209 res.print(false);
210 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800211
212 Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
213 Asset::ACCESS_BUFFER);
214 if (manifestAsset == NULL) {
215 printf("\nNo AndroidManifest.xml found.\n");
216 } else {
217 printf("\nAndroid manifest:\n");
218 ResXMLTree tree;
219 tree.setTo(manifestAsset->getBuffer(true),
220 manifestAsset->getLength());
221 printXMLBlock(&tree);
222 }
223 delete manifestAsset;
224 }
225
226 result = 0;
227
228bail:
229 delete zip;
230 return result;
231}
232
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700233static void printResolvedResourceAttribute(const ResTable& resTable, const ResXMLTree& tree,
Chih-Hung Hsieh9b8528f2016-08-10 14:15:30 -0700234 uint32_t attrRes, const String8& attrLabel, String8* outError)
Maurice Chu76327312013-10-16 18:28:46 -0700235{
236 Res_value value;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700237 AaptXml::getResolvedResourceAttribute(resTable, tree, attrRes, &value, outError);
Maurice Chu76327312013-10-16 18:28:46 -0700238 if (*outError != "") {
239 *outError = "error print resolved resource attribute";
240 return;
241 }
242 if (value.dataType == Res_value::TYPE_STRING) {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700243 String8 result = AaptXml::getResolvedAttribute(resTable, tree, attrRes, outError);
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000244 printf("%s='%s'", attrLabel.c_str(),
245 ResTable::normalizeForOutput(result.c_str()).c_str());
Maurice Chu76327312013-10-16 18:28:46 -0700246 } else if (Res_value::TYPE_FIRST_INT <= value.dataType &&
247 value.dataType <= Res_value::TYPE_LAST_INT) {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000248 printf("%s='%d'", attrLabel.c_str(), value.data);
Maurice Chu76327312013-10-16 18:28:46 -0700249 } else {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000250 printf("%s='0x%x'", attrLabel.c_str(), (int)value.data);
Maurice Chu76327312013-10-16 18:28:46 -0700251 }
252}
253
Adam Lesinski282e1812014-01-23 18:17:42 -0800254// These are attribute resource constants for the platform, as found
255// in android.R.attr
256enum {
257 LABEL_ATTR = 0x01010001,
258 ICON_ATTR = 0x01010002,
259 NAME_ATTR = 0x01010003,
Adam Lesinskia5018c92013-09-30 16:23:15 -0700260 PERMISSION_ATTR = 0x01010006,
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700261 EXPORTED_ATTR = 0x01010010,
262 GRANT_URI_PERMISSIONS_ATTR = 0x0101001b,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700263 RESOURCE_ATTR = 0x01010025,
Adam Lesinski282e1812014-01-23 18:17:42 -0800264 DEBUGGABLE_ATTR = 0x0101000f,
265 VALUE_ATTR = 0x01010024,
266 VERSION_CODE_ATTR = 0x0101021b,
267 VERSION_NAME_ATTR = 0x0101021c,
268 SCREEN_ORIENTATION_ATTR = 0x0101001e,
269 MIN_SDK_VERSION_ATTR = 0x0101020c,
270 MAX_SDK_VERSION_ATTR = 0x01010271,
271 REQ_TOUCH_SCREEN_ATTR = 0x01010227,
272 REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
273 REQ_HARD_KEYBOARD_ATTR = 0x01010229,
274 REQ_NAVIGATION_ATTR = 0x0101022a,
275 REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
276 TARGET_SDK_VERSION_ATTR = 0x01010270,
277 TEST_ONLY_ATTR = 0x01010272,
278 ANY_DENSITY_ATTR = 0x0101026c,
279 GL_ES_VERSION_ATTR = 0x01010281,
280 SMALL_SCREEN_ATTR = 0x01010284,
281 NORMAL_SCREEN_ATTR = 0x01010285,
282 LARGE_SCREEN_ATTR = 0x01010286,
283 XLARGE_SCREEN_ATTR = 0x010102bf,
284 REQUIRED_ATTR = 0x0101028e,
Adam Lesinskicaf797c2014-08-22 12:56:26 -0700285 INSTALL_LOCATION_ATTR = 0x010102b7,
Adam Lesinski282e1812014-01-23 18:17:42 -0800286 SCREEN_SIZE_ATTR = 0x010102ca,
287 SCREEN_DENSITY_ATTR = 0x010102cb,
288 REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
289 COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
290 LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
291 PUBLIC_KEY_ATTR = 0x010103a6,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700292 CATEGORY_ATTR = 0x010103e8,
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800293 BANNER_ATTR = 0x10103f2,
Tim Kilbournd9b1cad2014-10-24 12:43:41 -0700294 ISGAME_ATTR = 0x10103f4,
Dianne Hackborncd154e92017-02-28 17:37:35 -0800295 REQUIRED_FEATURE_ATTR = 0x1010557,
296 REQUIRED_NOT_FEATURE_ATTR = 0x1010558,
Alan Viverette11be9312017-11-09 15:41:44 -0500297 COMPILE_SDK_VERSION_ATTR = 0x01010572, // NOT FINALIZED
298 COMPILE_SDK_VERSION_CODENAME_ATTR = 0x01010573, // NOT FINALIZED
Adam Lesinski282e1812014-01-23 18:17:42 -0800299};
300
Maurice Chu2675f762013-10-22 17:33:11 -0700301String8 getComponentName(String8 &pkgName, String8 &componentName) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800302 ssize_t idx = componentName.find(".");
303 String8 retStr(pkgName);
304 if (idx == 0) {
305 retStr += componentName;
306 } else if (idx < 0) {
307 retStr += ".";
308 retStr += componentName;
309 } else {
Maurice Chu2675f762013-10-22 17:33:11 -0700310 return componentName;
Adam Lesinski282e1812014-01-23 18:17:42 -0800311 }
Maurice Chu2675f762013-10-22 17:33:11 -0700312 return retStr;
Adam Lesinski282e1812014-01-23 18:17:42 -0800313}
314
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700315static void printCompatibleScreens(ResXMLTree& tree, String8* outError) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800316 size_t len;
317 ResXMLTree::event_code_t code;
318 int depth = 0;
319 bool first = true;
320 printf("compatible-screens:");
321 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
322 if (code == ResXMLTree::END_TAG) {
323 depth--;
324 if (depth < 0) {
325 break;
326 }
327 continue;
328 }
329 if (code != ResXMLTree::START_TAG) {
330 continue;
331 }
332 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700333 const char16_t* ctag16 = tree.getElementName(&len);
334 if (ctag16 == NULL) {
335 *outError = "failed to get XML element name (bad string pool)";
336 return;
337 }
338 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800339 if (tag == "screen") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700340 int32_t screenSize = AaptXml::getIntegerAttribute(tree,
341 SCREEN_SIZE_ATTR);
342 int32_t screenDensity = AaptXml::getIntegerAttribute(tree,
343 SCREEN_DENSITY_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -0800344 if (screenSize > 0 && screenDensity > 0) {
345 if (!first) {
346 printf(",");
347 }
348 first = false;
349 printf("'%d/%d'", screenSize, screenDensity);
350 }
351 }
352 }
353 printf("\n");
354}
355
Dianne Hackborncd154e92017-02-28 17:37:35 -0800356static void printUsesPermission(const String8& name, bool optional=false, int maxSdkVersion=-1,
Tomasz Wasilczyk5f004aa2023-08-14 16:26:24 +0000357 const String8& requiredFeature = String8(),
358 const String8& requiredNotFeature = String8()) {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000359 printf("uses-permission: name='%s'", ResTable::normalizeForOutput(name.c_str()).c_str());
Adam Lesinski58f1f362013-11-12 12:59:08 -0800360 if (maxSdkVersion != -1) {
361 printf(" maxSdkVersion='%d'", maxSdkVersion);
362 }
Dianne Hackborncd154e92017-02-28 17:37:35 -0800363 if (requiredFeature.length() > 0) {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000364 printf(" requiredFeature='%s'", requiredFeature.c_str());
Dianne Hackborncd154e92017-02-28 17:37:35 -0800365 }
366 if (requiredNotFeature.length() > 0) {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000367 printf(" requiredNotFeature='%s'", requiredNotFeature.c_str());
Dianne Hackborncd154e92017-02-28 17:37:35 -0800368 }
Adam Lesinski58f1f362013-11-12 12:59:08 -0800369 printf("\n");
370
371 if (optional) {
372 printf("optional-permission: name='%s'",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000373 ResTable::normalizeForOutput(name.c_str()).c_str());
Adam Lesinski58f1f362013-11-12 12:59:08 -0800374 if (maxSdkVersion != -1) {
375 printf(" maxSdkVersion='%d'", maxSdkVersion);
376 }
377 printf("\n");
378 }
379}
380
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800381static void printUsesPermissionSdk23(const String8& name, int maxSdkVersion=-1) {
382 printf("uses-permission-sdk-23: ");
383
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000384 printf("name='%s'", ResTable::normalizeForOutput(name.c_str()).c_str());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800385 if (maxSdkVersion != -1) {
386 printf(" maxSdkVersion='%d'", maxSdkVersion);
387 }
388 printf("\n");
389}
390
Adam Lesinski2386df22016-12-28 15:08:58 -0500391static void printUsesImpliedPermission(const String8& name, const String8& reason,
392 const int32_t maxSdkVersion = -1) {
393 printf("uses-implied-permission: name='%s'",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000394 ResTable::normalizeForOutput(name.c_str()).c_str());
Adam Lesinski2386df22016-12-28 15:08:58 -0500395 if (maxSdkVersion != -1) {
396 printf(" maxSdkVersion='%d'", maxSdkVersion);
397 }
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000398 printf(" reason='%s'\n", ResTable::normalizeForOutput(reason.c_str()).c_str());
Adam Lesinski58f1f362013-11-12 12:59:08 -0800399}
400
Chih-Hung Hsieh9b8528f2016-08-10 14:15:30 -0700401Vector<String8> getNfcAidCategories(AssetManager& assets, const String8& xmlPath, bool offHost,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700402 String8 *outError = NULL)
403{
404 Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER);
405 if (aidAsset == NULL) {
406 if (outError != NULL) *outError = "xml resource does not exist";
407 return Vector<String8>();
408 }
409
410 const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service");
411
412 bool withinApduService = false;
413 Vector<String8> categories;
414
415 String8 error;
416 ResXMLTree tree;
417 tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength());
418
419 size_t len;
420 int depth = 0;
421 ResXMLTree::event_code_t code;
422 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
423 if (code == ResXMLTree::END_TAG) {
424 depth--;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700425 const char16_t* ctag16 = tree.getElementName(&len);
426 if (ctag16 == NULL) {
427 *outError = "failed to get XML element name (bad string pool)";
428 return Vector<String8>();
429 }
430 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700431
432 if (depth == 0 && tag == serviceTagName) {
433 withinApduService = false;
434 }
435
436 } else if (code == ResXMLTree::START_TAG) {
437 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700438 const char16_t* ctag16 = tree.getElementName(&len);
439 if (ctag16 == NULL) {
440 *outError = "failed to get XML element name (bad string pool)";
441 return Vector<String8>();
442 }
443 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700444
445 if (depth == 1) {
446 if (tag == serviceTagName) {
447 withinApduService = true;
448 }
449 } else if (depth == 2 && withinApduService) {
450 if (tag == "aid-group") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700451 String8 category = AaptXml::getAttribute(tree, CATEGORY_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700452 if (error != "") {
453 if (outError != NULL) *outError = error;
454 return Vector<String8>();
455 }
456
457 categories.add(category);
458 }
459 }
460 }
461 }
462 aidAsset->close();
463 return categories;
464}
465
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700466static void printComponentPresence(const char* componentName) {
467 printf("provides-component:'%s'\n", componentName);
468}
469
Adam Lesinski2c72b682014-06-24 09:56:01 -0700470/**
471 * Represents a feature that has been automatically added due to
472 * a pre-requisite or some other reason.
473 */
474struct ImpliedFeature {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800475 ImpliedFeature() : impliedBySdk23(false) {}
476 ImpliedFeature(const String8& n, bool sdk23) : name(n), impliedBySdk23(sdk23) {}
477
Adam Lesinski2c72b682014-06-24 09:56:01 -0700478 /**
479 * Name of the implied feature.
480 */
481 String8 name;
482
483 /**
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800484 * Was this implied by a permission from SDK 23 (<uses-permission-sdk-23 />)?
485 */
486 bool impliedBySdk23;
487
488 /**
Adam Lesinski2c72b682014-06-24 09:56:01 -0700489 * List of human-readable reasons for why this feature was implied.
490 */
491 SortedVector<String8> reasons;
492};
493
Adam Lesinski694d0a72016-04-06 16:12:04 -0700494struct Feature {
495 Feature() : required(false), version(-1) {}
Chih-Hung Hsiehd53e3be2016-05-03 10:02:51 -0700496 explicit Feature(bool required, int32_t version = -1) : required(required), version(version) {}
Adam Lesinski694d0a72016-04-06 16:12:04 -0700497
498 /**
499 * Whether the feature is required.
500 */
501 bool required;
502
503 /**
504 * What version of the feature is requested.
505 */
506 int32_t version;
507};
508
Adam Lesinski2c72b682014-06-24 09:56:01 -0700509/**
510 * Represents a <feature-group> tag in the AndroidManifest.xml
511 */
512struct FeatureGroup {
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700513 FeatureGroup() : openGLESVersion(-1) {}
514
Adam Lesinski2c72b682014-06-24 09:56:01 -0700515 /**
516 * Human readable label
517 */
518 String8 label;
519
520 /**
521 * Explicit features defined in the group
522 */
Adam Lesinski694d0a72016-04-06 16:12:04 -0700523 KeyedVector<String8, Feature> features;
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700524
525 /**
526 * OpenGL ES version required
527 */
528 int openGLESVersion;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700529};
530
Adam Lesinskica955a42016-08-01 16:44:29 -0700531static bool hasFeature(const char* name, const FeatureGroup& grp,
532 const KeyedVector<String8, ImpliedFeature>& implied) {
533 String8 name8(name);
534 ssize_t idx = grp.features.indexOfKey(name8);
535 if (idx < 0) {
536 idx = implied.indexOfKey(name8);
537 }
538 return idx >= 0;
539}
540
Adam Lesinski2c72b682014-06-24 09:56:01 -0700541static void addImpliedFeature(KeyedVector<String8, ImpliedFeature>* impliedFeatures,
Adam Lesinski43158772015-11-11 15:13:55 -0800542 const char* name, const String8& reason, bool sdk23) {
Adam Lesinski2c72b682014-06-24 09:56:01 -0700543 String8 name8(name);
544 ssize_t idx = impliedFeatures->indexOfKey(name8);
545 if (idx < 0) {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800546 idx = impliedFeatures->add(name8, ImpliedFeature(name8, sdk23));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700547 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800548
549 ImpliedFeature* feature = &impliedFeatures->editValueAt(idx);
550
551 // A non-sdk 23 implied feature takes precedence.
552 if (feature->impliedBySdk23 && !sdk23) {
553 feature->impliedBySdk23 = false;
554 }
Adam Lesinski43158772015-11-11 15:13:55 -0800555 feature->reasons.add(reason);
Adam Lesinski2c72b682014-06-24 09:56:01 -0700556}
557
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800558static void printFeatureGroupImpl(const FeatureGroup& grp,
559 const KeyedVector<String8, ImpliedFeature>* impliedFeatures) {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000560 printf("feature-group: label='%s'\n", grp.label.c_str());
Adam Lesinski2c72b682014-06-24 09:56:01 -0700561
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700562 if (grp.openGLESVersion > 0) {
563 printf(" uses-gl-es: '0x%x'\n", grp.openGLESVersion);
564 }
565
Adam Lesinski2c72b682014-06-24 09:56:01 -0700566 const size_t numFeatures = grp.features.size();
567 for (size_t i = 0; i < numFeatures; i++) {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700568 const Feature& feature = grp.features[i];
569 const bool required = feature.required;
570 const int32_t version = feature.version;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700571
572 const String8& featureName = grp.features.keyAt(i);
Adam Lesinski694d0a72016-04-06 16:12:04 -0700573 printf(" uses-feature%s: name='%s'", (required ? "" : "-not-required"),
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000574 ResTable::normalizeForOutput(featureName.c_str()).c_str());
Adam Lesinski694d0a72016-04-06 16:12:04 -0700575
576 if (version > 0) {
577 printf(" version='%d'", version);
578 }
579 printf("\n");
Adam Lesinski2c72b682014-06-24 09:56:01 -0700580 }
581
582 const size_t numImpliedFeatures =
583 (impliedFeatures != NULL) ? impliedFeatures->size() : 0;
584 for (size_t i = 0; i < numImpliedFeatures; i++) {
585 const ImpliedFeature& impliedFeature = impliedFeatures->valueAt(i);
586 if (grp.features.indexOfKey(impliedFeature.name) >= 0) {
587 // The feature is explicitly set, no need to use implied
588 // definition.
589 continue;
590 }
591
592 String8 printableFeatureName(ResTable::normalizeForOutput(
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000593 impliedFeature.name.c_str()));
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800594 const char* sdk23Suffix = impliedFeature.impliedBySdk23 ? "-sdk-23" : "";
595
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000596 printf(" uses-feature%s: name='%s'\n", sdk23Suffix, printableFeatureName.c_str());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800597 printf(" uses-implied-feature%s: name='%s' reason='", sdk23Suffix,
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000598 printableFeatureName.c_str());
Adam Lesinski2c72b682014-06-24 09:56:01 -0700599 const size_t numReasons = impliedFeature.reasons.size();
600 for (size_t j = 0; j < numReasons; j++) {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000601 printf("%s", impliedFeature.reasons[j].c_str());
Adam Lesinski2c72b682014-06-24 09:56:01 -0700602 if (j + 2 < numReasons) {
603 printf(", ");
604 } else if (j + 1 < numReasons) {
605 printf(", and ");
606 }
607 }
608 printf("'\n");
609 }
610}
611
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800612static void printFeatureGroup(const FeatureGroup& grp) {
613 printFeatureGroupImpl(grp, NULL);
614}
615
616static void printDefaultFeatureGroup(const FeatureGroup& grp,
617 const KeyedVector<String8, ImpliedFeature>& impliedFeatures) {
618 printFeatureGroupImpl(grp, &impliedFeatures);
619}
620
Adam Lesinski2c72b682014-06-24 09:56:01 -0700621static void addParentFeatures(FeatureGroup* grp, const String8& name) {
622 if (name == "android.hardware.camera.autofocus" ||
623 name == "android.hardware.camera.flash") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700624 grp->features.add(String8("android.hardware.camera"), Feature(true));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700625 } else if (name == "android.hardware.location.gps" ||
626 name == "android.hardware.location.network") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700627 grp->features.add(String8("android.hardware.location"), Feature(true));
Adam Lesinskica955a42016-08-01 16:44:29 -0700628 } else if (name == "android.hardware.faketouch.multitouch") {
629 grp->features.add(String8("android.hardware.faketouch"), Feature(true));
630 } else if (name == "android.hardware.faketouch.multitouch.distinct" ||
631 name == "android.hardware.faketouch.multitouch.jazzhands") {
632 grp->features.add(String8("android.hardware.faketouch.multitouch"), Feature(true));
633 grp->features.add(String8("android.hardware.faketouch"), Feature(true));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700634 } else if (name == "android.hardware.touchscreen.multitouch") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700635 grp->features.add(String8("android.hardware.touchscreen"), Feature(true));
Adam Lesinskica955a42016-08-01 16:44:29 -0700636 } else if (name == "android.hardware.touchscreen.multitouch.distinct" ||
637 name == "android.hardware.touchscreen.multitouch.jazzhands") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700638 grp->features.add(String8("android.hardware.touchscreen.multitouch"), Feature(true));
639 grp->features.add(String8("android.hardware.touchscreen"), Feature(true));
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700640 } else if (name == "android.hardware.opengles.aep") {
641 const int openGLESVersion31 = 0x00030001;
642 if (openGLESVersion31 > grp->openGLESVersion) {
643 grp->openGLESVersion = openGLESVersion31;
644 }
Adam Lesinski2c72b682014-06-24 09:56:01 -0700645 }
646}
647
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800648static void addImpliedFeaturesForPermission(const int targetSdk, const String8& name,
649 KeyedVector<String8, ImpliedFeature>* impliedFeatures,
650 bool impliedBySdk23Permission) {
651 if (name == "android.permission.CAMERA") {
652 addImpliedFeature(impliedFeatures, "android.hardware.camera",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000653 String8::format("requested %s permission", name.c_str()),
Adam Lesinski43158772015-11-11 15:13:55 -0800654 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800655 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
Adam Lesinski43158772015-11-11 15:13:55 -0800656 if (targetSdk < SDK_LOLLIPOP) {
657 addImpliedFeature(impliedFeatures, "android.hardware.location.gps",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000658 String8::format("requested %s permission", name.c_str()),
Adam Lesinski43158772015-11-11 15:13:55 -0800659 impliedBySdk23Permission);
660 addImpliedFeature(impliedFeatures, "android.hardware.location.gps",
661 String8::format("targetSdkVersion < %d", SDK_LOLLIPOP),
662 impliedBySdk23Permission);
663 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800664 addImpliedFeature(impliedFeatures, "android.hardware.location",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000665 String8::format("requested %s permission", name.c_str()),
Adam Lesinski43158772015-11-11 15:13:55 -0800666 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800667 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
Adam Lesinski43158772015-11-11 15:13:55 -0800668 if (targetSdk < SDK_LOLLIPOP) {
669 addImpliedFeature(impliedFeatures, "android.hardware.location.network",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000670 String8::format("requested %s permission", name.c_str()),
Adam Lesinski43158772015-11-11 15:13:55 -0800671 impliedBySdk23Permission);
672 addImpliedFeature(impliedFeatures, "android.hardware.location.network",
673 String8::format("targetSdkVersion < %d", SDK_LOLLIPOP),
674 impliedBySdk23Permission);
675 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800676 addImpliedFeature(impliedFeatures, "android.hardware.location",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000677 String8::format("requested %s permission", name.c_str()),
Adam Lesinski43158772015-11-11 15:13:55 -0800678 impliedBySdk23Permission);
679 } else if (name == "android.permission.ACCESS_MOCK_LOCATION" ||
680 name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800681 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
682 addImpliedFeature(impliedFeatures, "android.hardware.location",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000683 String8::format("requested %s permission", name.c_str()),
Adam Lesinski43158772015-11-11 15:13:55 -0800684 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800685 } else if (name == "android.permission.BLUETOOTH" ||
686 name == "android.permission.BLUETOOTH_ADMIN") {
Adam Lesinski43158772015-11-11 15:13:55 -0800687 if (targetSdk > SDK_DONUT) {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800688 addImpliedFeature(impliedFeatures, "android.hardware.bluetooth",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000689 String8::format("requested %s permission", name.c_str()),
Adam Lesinski43158772015-11-11 15:13:55 -0800690 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800691 addImpliedFeature(impliedFeatures, "android.hardware.bluetooth",
Adam Lesinski43158772015-11-11 15:13:55 -0800692 String8::format("targetSdkVersion > %d", SDK_DONUT),
693 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800694 }
695 } else if (name == "android.permission.RECORD_AUDIO") {
696 addImpliedFeature(impliedFeatures, "android.hardware.microphone",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000697 String8::format("requested %s permission", name.c_str()),
Adam Lesinski43158772015-11-11 15:13:55 -0800698 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800699 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
700 name == "android.permission.CHANGE_WIFI_STATE" ||
701 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
702 addImpliedFeature(impliedFeatures, "android.hardware.wifi",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000703 String8::format("requested %s permission", name.c_str()),
Adam Lesinski43158772015-11-11 15:13:55 -0800704 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800705 } else if (name == "android.permission.CALL_PHONE" ||
706 name == "android.permission.CALL_PRIVILEGED" ||
707 name == "android.permission.MODIFY_PHONE_STATE" ||
708 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
709 name == "android.permission.READ_SMS" ||
710 name == "android.permission.RECEIVE_SMS" ||
711 name == "android.permission.RECEIVE_MMS" ||
712 name == "android.permission.RECEIVE_WAP_PUSH" ||
713 name == "android.permission.SEND_SMS" ||
714 name == "android.permission.WRITE_APN_SETTINGS" ||
715 name == "android.permission.WRITE_SMS") {
716 addImpliedFeature(impliedFeatures, "android.hardware.telephony",
Adam Lesinski43158772015-11-11 15:13:55 -0800717 String8("requested a telephony permission"),
718 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800719 }
720}
721
Adam Lesinski282e1812014-01-23 18:17:42 -0800722/*
723 * Handle the "dump" command, to extract select data from an archive.
724 */
725extern char CONSOLE_DATA[2925]; // see EOF
726int doDump(Bundle* bundle)
727{
728 status_t result = UNKNOWN_ERROR;
Adam Lesinski282e1812014-01-23 18:17:42 -0800729
730 if (bundle->getFileSpecCount() < 1) {
731 fprintf(stderr, "ERROR: no dump option specified\n");
732 return 1;
733 }
734
735 if (bundle->getFileSpecCount() < 2) {
736 fprintf(stderr, "ERROR: no dump file specified\n");
737 return 1;
738 }
739
740 const char* option = bundle->getFileSpecEntry(0);
741 const char* filename = bundle->getFileSpecEntry(1);
742
743 AssetManager assets;
Narayan Kamathf85e41f2014-01-24 14:14:30 +0000744 int32_t assetsCookie;
Adam Lesinski282e1812014-01-23 18:17:42 -0800745
Donald Chaid1ac6e12017-10-12 21:00:45 -0700746 // Add any dependencies passed in.
Adam Lesinski57fe4832017-05-10 15:42:22 -0700747 for (size_t i = 0; i < bundle->getPackageIncludes().size(); i++) {
748 const String8& assetPath = bundle->getPackageIncludes()[i];
749 if (!assets.addAssetPath(assetPath, NULL)) {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000750 fprintf(stderr, "ERROR: included asset path %s could not be loaded\n", assetPath.c_str());
Adam Lesinski57fe4832017-05-10 15:42:22 -0700751 return 1;
752 }
753 }
754
Donald Chaid1ac6e12017-10-12 21:00:45 -0700755 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
756 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
757 return 1;
758 }
759
Adam Lesinski282e1812014-01-23 18:17:42 -0800760 // Make a dummy config for retrieving resources... we need to supply
761 // non-default values for some configs so that we can retrieve resources
762 // in the app that don't have a default. The most important of these is
763 // the API version because key resources like icons will have an implicit
764 // version if they are using newer config types like density.
765 ResTable_config config;
Narayan Kamath91447d82014-01-21 15:32:36 +0000766 memset(&config, 0, sizeof(ResTable_config));
Adam Lesinski282e1812014-01-23 18:17:42 -0800767 config.language[0] = 'e';
768 config.language[1] = 'n';
769 config.country[0] = 'U';
770 config.country[1] = 'S';
771 config.orientation = ResTable_config::ORIENTATION_PORT;
772 config.density = ResTable_config::DENSITY_MEDIUM;
Jackal Guo201a60a2021-08-31 12:37:30 +0800773 config.sdkVersion = SDK_CUR_DEVELOPMENT; // Very high.
Adam Lesinski282e1812014-01-23 18:17:42 -0800774 config.screenWidthDp = 320;
775 config.screenHeightDp = 480;
776 config.smallestScreenWidthDp = 320;
Adam Lesinskic2dea8d2014-08-04 16:40:41 -0700777 config.screenLayout |= ResTable_config::SCREENSIZE_NORMAL;
Adam Lesinski282e1812014-01-23 18:17:42 -0800778 assets.setConfiguration(config);
779
780 const ResTable& res = assets.getResources(false);
Dan Albert68001652014-09-09 09:51:01 -0700781 if (res.getError() != NO_ERROR) {
Adam Lesinski25e9d552014-05-19 15:01:43 -0700782 fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
Adam Lesinski63e646e2014-07-30 11:40:39 -0700783 return 1;
Adam Lesinski282e1812014-01-23 18:17:42 -0800784 }
785
Adam Lesinski694d0a72016-04-06 16:12:04 -0700786 // Source for AndroidManifest.xml
Adam Lesinski10de3af12016-07-13 10:14:03 -0700787 const String8 manifestFile("AndroidManifest.xml");
Adam Lesinski694d0a72016-04-06 16:12:04 -0700788
Adam Lesinski2cb761e2014-08-15 13:59:02 -0700789 // The dynamicRefTable can be null if there are no resources for this asset cookie.
790 // This fine.
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700791 auto noop_destructor = [](const DynamicRefTable* /*ref_table */) { };
792 auto dynamicRefTable = std::shared_ptr<const DynamicRefTable>(
793 res.getDynamicRefTableForCookie(assetsCookie), noop_destructor);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700794
795 Asset* asset = NULL;
796
Adam Lesinski282e1812014-01-23 18:17:42 -0800797 if (strcmp("resources", option) == 0) {
Elliott Hughesba3fe562015-08-12 14:49:53 -0700798#ifndef __ANDROID__
Adam Lesinski282e1812014-01-23 18:17:42 -0800799 res.print(bundle->getValues());
800#endif
801
802 } else if (strcmp("strings", option) == 0) {
803 const ResStringPool* pool = res.getTableStringBlock(0);
804 printStringPool(pool);
805
806 } else if (strcmp("xmltree", option) == 0) {
807 if (bundle->getFileSpecCount() < 3) {
808 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
809 goto bail;
810 }
811
812 for (int i=2; i<bundle->getFileSpecCount(); i++) {
813 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700814 ResXMLTree tree(dynamicRefTable);
815 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800816 if (asset == NULL) {
Adam Lesinskifcb5f7b2016-11-02 13:17:10 -0700817 fprintf(stderr, "ERROR: dump failed because resource %s not found\n", resname);
Adam Lesinski282e1812014-01-23 18:17:42 -0800818 goto bail;
819 }
820
821 if (tree.setTo(asset->getBuffer(true),
822 asset->getLength()) != NO_ERROR) {
823 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
824 goto bail;
825 }
826 tree.restart();
827 printXMLBlock(&tree);
828 tree.uninit();
829 delete asset;
830 asset = NULL;
831 }
832
833 } else if (strcmp("xmlstrings", option) == 0) {
834 if (bundle->getFileSpecCount() < 3) {
835 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
836 goto bail;
837 }
838
839 for (int i=2; i<bundle->getFileSpecCount(); i++) {
840 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700841 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800842 if (asset == NULL) {
843 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
844 goto bail;
845 }
846
Adam Lesinski63e646e2014-07-30 11:40:39 -0700847 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800848 if (tree.setTo(asset->getBuffer(true),
849 asset->getLength()) != NO_ERROR) {
850 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
851 goto bail;
852 }
853 printStringPool(&tree.getStrings());
854 delete asset;
855 asset = NULL;
856 }
857
858 } else {
Adam Lesinski63e646e2014-07-30 11:40:39 -0700859 asset = assets.openNonAsset(assetsCookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800860 if (asset == NULL) {
861 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
862 goto bail;
863 }
864
Adam Lesinski63e646e2014-07-30 11:40:39 -0700865 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800866 if (tree.setTo(asset->getBuffer(true),
867 asset->getLength()) != NO_ERROR) {
868 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
869 goto bail;
870 }
871 tree.restart();
872
873 if (strcmp("permissions", option) == 0) {
874 size_t len;
875 ResXMLTree::event_code_t code;
876 int depth = 0;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800877 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
878 code != ResXMLTree::BAD_DOCUMENT) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800879 if (code == ResXMLTree::END_TAG) {
880 depth--;
881 continue;
882 }
883 if (code != ResXMLTree::START_TAG) {
884 continue;
885 }
886 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700887 const char16_t* ctag16 = tree.getElementName(&len);
888 if (ctag16 == NULL) {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700889 SourcePos(manifestFile, tree.getLineNumber()).error(
890 "ERROR: failed to get XML element name (bad string pool)");
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700891 goto bail;
892 }
893 String8 tag(ctag16);
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000894 //printf("Depth %d tag %s\n", depth, tag.c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -0800895 if (depth == 1) {
896 if (tag != "manifest") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700897 SourcePos(manifestFile, tree.getLineNumber()).error(
898 "ERROR: manifest does not start with <manifest> tag");
Adam Lesinski282e1812014-01-23 18:17:42 -0800899 goto bail;
900 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700901 String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000902 printf("package: %s\n", ResTable::normalizeForOutput(pkg.c_str()).c_str());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800903 } else if (depth == 2) {
904 if (tag == "permission") {
905 String8 error;
906 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
907 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700908 SourcePos(manifestFile, tree.getLineNumber()).error(
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000909 "ERROR getting 'android:name': %s", error.c_str());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800910 goto bail;
911 }
912
913 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700914 SourcePos(manifestFile, tree.getLineNumber()).error(
915 "ERROR: missing 'android:name' for permission");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800916 goto bail;
917 }
918 printf("permission: %s\n",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000919 ResTable::normalizeForOutput(name.c_str()).c_str());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800920 } else if (tag == "uses-permission") {
921 String8 error;
922 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
923 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700924 SourcePos(manifestFile, tree.getLineNumber()).error(
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000925 "ERROR getting 'android:name' attribute: %s", error.c_str());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800926 goto bail;
927 }
928
929 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700930 SourcePos(manifestFile, tree.getLineNumber()).error(
931 "ERROR: missing 'android:name' for uses-permission");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800932 goto bail;
933 }
934 printUsesPermission(name,
935 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
936 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
937 } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
938 String8 error;
939 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
940 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700941 SourcePos(manifestFile, tree.getLineNumber()).error(
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000942 "ERROR getting 'android:name' attribute: %s", error.c_str());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800943 goto bail;
944 }
945
946 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700947 SourcePos(manifestFile, tree.getLineNumber()).error(
948 "ERROR: missing 'android:name' for uses-permission-sdk-23");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800949 goto bail;
950 }
951 printUsesPermissionSdk23(
952 name,
953 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
Adam Lesinski282e1812014-01-23 18:17:42 -0800954 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800955 }
956 }
957 } else if (strcmp("badging", option) == 0) {
958 Vector<String8> locales;
959 res.getLocales(&locales);
960
961 Vector<ResTable_config> configs;
962 res.getConfigurations(&configs);
963 SortedVector<int> densities;
964 const size_t NC = configs.size();
965 for (size_t i=0; i<NC; i++) {
966 int dens = configs[i].density;
967 if (dens == 0) {
968 dens = 160;
969 }
970 densities.add(dens);
971 }
972
Ryan Mitchell424db432021-05-03 11:42:52 -0700973 std::vector<ResXMLParser::ResXMLPosition> tagsToSkip;
974
Adam Lesinski282e1812014-01-23 18:17:42 -0800975 size_t len;
976 ResXMLTree::event_code_t code;
977 int depth = 0;
978 String8 error;
979 bool withinActivity = false;
980 bool isMainActivity = false;
981 bool isLauncherActivity = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800982 bool isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800983 bool isSearchable = false;
984 bool withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700985 bool withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700986 bool withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800987 bool withinReceiver = false;
988 bool withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700989 bool withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800990 bool withinIntentFilter = false;
991 bool hasMainActivity = false;
992 bool hasOtherActivities = false;
993 bool hasOtherReceivers = false;
994 bool hasOtherServices = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700995 bool hasIntentFilter = false;
996
Adam Lesinski282e1812014-01-23 18:17:42 -0800997 bool hasWallpaperService = false;
998 bool hasImeService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700999 bool hasAccessibilityService = false;
1000 bool hasPrintService = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001001 bool hasWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001002 bool hasDeviceAdminReceiver = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001003 bool hasPaymentService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001004 bool hasDocumentsProvider = false;
1005 bool hasCameraActivity = false;
1006 bool hasCameraSecureActivity = false;
1007 bool hasLauncher = false;
1008 bool hasNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001009 bool hasDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001010
Adam Lesinski282e1812014-01-23 18:17:42 -08001011 bool actMainActivity = false;
1012 bool actWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001013 bool actDeviceAdminEnabled = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001014 bool actImeService = false;
1015 bool actWallpaperService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001016 bool actAccessibilityService = false;
1017 bool actPrintService = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001018 bool actHostApduService = false;
1019 bool actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001020 bool actDocumentsProvider = false;
1021 bool actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001022 bool actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001023 bool actCamera = false;
1024 bool actCameraSecure = false;
1025 bool catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001026 bool hasMetaHostPaymentCategory = false;
1027 bool hasMetaOffHostPaymentCategory = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001028
1029 // These permissions are required by services implementing services
1030 // the system binds to (IME, Accessibility, PrintServices, etc.)
1031 bool hasBindDeviceAdminPermission = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001032 bool hasBindAccessibilityServicePermission = false;
1033 bool hasBindPrintServicePermission = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001034 bool hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001035 bool hasRequiredSafAttributes = false;
1036 bool hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001037 bool hasBindDreamServicePermission = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001038
1039 // These two implement the implicit permissions that are granted
1040 // to pre-1.6 applications.
1041 bool hasWriteExternalStoragePermission = false;
Adam Lesinski2386df22016-12-28 15:08:58 -05001042 int32_t writeExternalStoragePermissionMaxSdkVersion = -1;
Adam Lesinski282e1812014-01-23 18:17:42 -08001043 bool hasReadPhoneStatePermission = false;
1044
1045 // If an app requests write storage, they will also get read storage.
1046 bool hasReadExternalStoragePermission = false;
1047
1048 // Implement transition to read and write call log.
1049 bool hasReadContactsPermission = false;
1050 bool hasWriteContactsPermission = false;
1051 bool hasReadCallLogPermission = false;
1052 bool hasWriteCallLogPermission = false;
1053
Adam Lesinskie47fd122014-08-15 22:25:36 -07001054 // If an app declares itself as multiArch, we report the
1055 // native libraries differently.
1056 bool hasMultiArch = false;
1057
Adam Lesinski282e1812014-01-23 18:17:42 -08001058 // This next group of variables is used to implement a group of
1059 // backward-compatibility heuristics necessitated by the addition of
1060 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
1061 // heuristic is "if an app requests a permission but doesn't explicitly
1062 // request the corresponding <uses-feature>, presume it's there anyway".
Adam Lesinski2c72b682014-06-24 09:56:01 -07001063
Adam Lesinski282e1812014-01-23 18:17:42 -08001064 // 2.2 also added some other features that apps can request, but that
1065 // have no corresponding permission, so we cannot implement any
1066 // back-compatibility heuristic for them. The below are thus unnecessary
1067 // (but are retained here for documentary purposes.)
1068 //bool specCompassFeature = false;
1069 //bool specAccelerometerFeature = false;
1070 //bool specProximityFeature = false;
1071 //bool specAmbientLightFeature = false;
1072 //bool specLiveWallpaperFeature = false;
1073
1074 int targetSdk = 0;
1075 int smallScreen = 1;
1076 int normalScreen = 1;
1077 int largeScreen = 1;
1078 int xlargeScreen = 1;
1079 int anyDensity = 1;
1080 int requiresSmallestWidthDp = 0;
1081 int compatibleWidthLimitDp = 0;
1082 int largestWidthLimitDp = 0;
1083 String8 pkg;
1084 String8 activityName;
1085 String8 activityLabel;
1086 String8 activityIcon;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001087 String8 activityBanner;
Adam Lesinski282e1812014-01-23 18:17:42 -08001088 String8 receiverName;
1089 String8 serviceName;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001090 Vector<String8> supportedInput;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001091
1092 FeatureGroup commonFeatures;
1093 Vector<FeatureGroup> featureGroups;
1094 KeyedVector<String8, ImpliedFeature> impliedFeatures;
1095
Ryan Mitchell424db432021-05-03 11:42:52 -07001096 {
1097 int curDepth = 0;
1098 ResXMLParser::ResXMLPosition initialPos;
1099 tree.getPosition(&initialPos);
1100
1101 // Find all of the "uses-sdk" tags within the "manifest" tag.
1102 std::vector<ResXMLParser::ResXMLPosition> usesSdkTagPositions;
1103 ResXMLParser::ResXMLPosition curPos;
1104 while ((code = tree.next()) != ResXMLTree::END_DOCUMENT &&
1105 code != ResXMLTree::BAD_DOCUMENT) {
1106 if (code == ResXMLTree::END_TAG) {
1107 curDepth--;
1108 continue;
1109 }
1110 if (code == ResXMLTree::START_TAG) {
1111 curDepth++;
1112 }
1113 const char16_t* ctag16 = tree.getElementName(&len);
1114 if (ctag16 == NULL || String8(ctag16) != "uses-sdk" || curDepth != 2) {
1115 continue;
1116 }
1117
1118 tree.getPosition(&curPos);
1119 usesSdkTagPositions.emplace_back(curPos);
1120 }
1121
1122 // Skip all "uses-sdk" tags besides the very last tag. The android runtime only uses
1123 // the attribute values from the last defined tag.
Ryan Mitchell957168e2021-05-10 11:46:49 -07001124 for (size_t i = 1; i < usesSdkTagPositions.size(); i++) {
1125 tagsToSkip.emplace_back(usesSdkTagPositions[i - 1]);
Ryan Mitchell424db432021-05-03 11:42:52 -07001126 }
1127
1128 // Reset the position before parsing.
1129 tree.setPosition(initialPos);
1130 }
1131
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001132 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
1133 code != ResXMLTree::BAD_DOCUMENT) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001134 if (code == ResXMLTree::END_TAG) {
1135 depth--;
1136 if (depth < 2) {
Tomasz Wasilczyk7e22cab2023-08-24 19:02:33 +00001137 if (withinSupportsInput && !supportedInput.empty()) {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001138 printf("supports-input: '");
1139 const size_t N = supportedInput.size();
1140 for (size_t i=0; i<N; i++) {
Maurice Chu2675f762013-10-22 17:33:11 -07001141 printf("%s", ResTable::normalizeForOutput(
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001142 supportedInput[i].c_str()).c_str());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001143 if (i != N - 1) {
1144 printf("' '");
1145 } else {
1146 printf("'\n");
1147 }
1148 }
1149 supportedInput.clear();
1150 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001151 withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001152 withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001153 withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001154 } else if (depth < 3) {
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001155 if (withinActivity && isMainActivity) {
Maurice Chu2675f762013-10-22 17:33:11 -07001156 String8 aName(getComponentName(pkg, activityName));
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001157 if (isLauncherActivity) {
1158 printf("launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001159 if (aName.length() > 0) {
1160 printf(" name='%s' ",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001161 ResTable::normalizeForOutput(aName.c_str()).c_str());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001162 }
1163 printf(" label='%s' icon='%s'\n",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001164 ResTable::normalizeForOutput(activityLabel.c_str())
1165 .c_str(),
1166 ResTable::normalizeForOutput(activityIcon.c_str())
1167 .c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001168 }
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001169 if (isLeanbackLauncherActivity) {
1170 printf("leanback-launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001171 if (aName.length() > 0) {
1172 printf(" name='%s' ",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001173 ResTable::normalizeForOutput(aName.c_str()).c_str());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001174 }
1175 printf(" label='%s' icon='%s' banner='%s'\n",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001176 ResTable::normalizeForOutput(activityLabel.c_str())
1177 .c_str(),
1178 ResTable::normalizeForOutput(activityIcon.c_str())
1179 .c_str(),
1180 ResTable::normalizeForOutput(activityBanner.c_str())
1181 .c_str());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001182 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001183 }
1184 if (!hasIntentFilter) {
1185 hasOtherActivities |= withinActivity;
1186 hasOtherReceivers |= withinReceiver;
1187 hasOtherServices |= withinService;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001188 } else {
1189 if (withinService) {
1190 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
1191 hasBindNfcServicePermission);
1192 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
1193 hasBindNfcServicePermission);
1194 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001195 }
1196 withinActivity = false;
1197 withinService = false;
1198 withinReceiver = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001199 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001200 hasIntentFilter = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001201 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001202 } else if (depth < 4) {
1203 if (withinIntentFilter) {
1204 if (withinActivity) {
1205 hasMainActivity |= actMainActivity;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001206 hasLauncher |= catLauncher;
1207 hasCameraActivity |= actCamera;
1208 hasCameraSecureActivity |= actCameraSecure;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001209 hasOtherActivities |=
1210 !actMainActivity && !actCamera && !actCameraSecure;
Adam Lesinski282e1812014-01-23 18:17:42 -08001211 } else if (withinReceiver) {
1212 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001213 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
1214 hasBindDeviceAdminPermission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001215 hasOtherReceivers |=
1216 (!actWidgetReceivers && !actDeviceAdminEnabled);
Adam Lesinski282e1812014-01-23 18:17:42 -08001217 } else if (withinService) {
1218 hasImeService |= actImeService;
1219 hasWallpaperService |= actWallpaperService;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001220 hasAccessibilityService |= (actAccessibilityService &&
1221 hasBindAccessibilityServicePermission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001222 hasPrintService |=
1223 (actPrintService && hasBindPrintServicePermission);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001224 hasNotificationListenerService |= actNotificationListenerService &&
1225 hasBindNotificationListenerServicePermission;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001226 hasDreamService |= actDreamService && hasBindDreamServicePermission;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001227 hasOtherServices |= (!actImeService && !actWallpaperService &&
Adam Lesinski94fc9122013-09-30 17:16:09 -07001228 !actAccessibilityService && !actPrintService &&
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001229 !actHostApduService && !actOffHostApduService &&
1230 !actNotificationListenerService);
1231 } else if (withinProvider) {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001232 hasDocumentsProvider |=
1233 actDocumentsProvider && hasRequiredSafAttributes;
Adam Lesinski282e1812014-01-23 18:17:42 -08001234 }
1235 }
1236 withinIntentFilter = false;
1237 }
1238 continue;
1239 }
1240 if (code != ResXMLTree::START_TAG) {
1241 continue;
1242 }
Ryan Mitchell424db432021-05-03 11:42:52 -07001243
Adam Lesinski282e1812014-01-23 18:17:42 -08001244 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001245
Ryan Mitchell424db432021-05-03 11:42:52 -07001246 // If this tag should be skipped, skip to the end of this tag.
1247 ResXMLParser::ResXMLPosition curPos;
1248 tree.getPosition(&curPos);
1249 if (std::find(tagsToSkip.begin(), tagsToSkip.end(), curPos) != tagsToSkip.end()) {
1250 const int breakDepth = depth - 1;
1251 while ((code = tree.next()) != ResXMLTree::END_DOCUMENT &&
1252 code != ResXMLTree::BAD_DOCUMENT) {
1253 if (code == ResXMLTree::END_TAG && --depth == breakDepth) {
1254 break;
1255 } else if (code == ResXMLTree::START_TAG) {
1256 depth++;
1257 }
1258 }
1259 continue;
1260 }
1261
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001262 const char16_t* ctag16 = tree.getElementName(&len);
1263 if (ctag16 == NULL) {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001264 SourcePos(manifestFile, tree.getLineNumber()).error(
1265 "ERROR: failed to get XML element name (bad string pool)");
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001266 goto bail;
1267 }
1268 String8 tag(ctag16);
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001269 //printf("Depth %d, %s\n", depth, tag.c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001270 if (depth == 1) {
1271 if (tag != "manifest") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001272 SourcePos(manifestFile, tree.getLineNumber()).error(
1273 "ERROR: manifest does not start with <manifest> tag");
Adam Lesinski282e1812014-01-23 18:17:42 -08001274 goto bail;
1275 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001276 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -07001277 printf("package: name='%s' ",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001278 ResTable::normalizeForOutput(pkg.c_str()).c_str());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001279 int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR,
1280 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001281 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001282 SourcePos(manifestFile, tree.getLineNumber()).error(
1283 "ERROR getting 'android:versionCode' attribute: %s",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001284 error.c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001285 goto bail;
1286 }
1287 if (versionCode > 0) {
1288 printf("versionCode='%d' ", versionCode);
1289 } else {
1290 printf("versionCode='' ");
1291 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001292 String8 versionName = AaptXml::getResolvedAttribute(res, tree,
1293 VERSION_NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001294 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001295 SourcePos(manifestFile, tree.getLineNumber()).error(
1296 "ERROR getting 'android:versionName' attribute: %s",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001297 error.c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001298 goto bail;
1299 }
Adam Lesinski25d35a92014-08-11 09:41:56 -07001300 printf("versionName='%s'",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001301 ResTable::normalizeForOutput(versionName.c_str()).c_str());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001302
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001303 String8 splitName = AaptXml::getAttribute(tree, NULL, "split");
Tomasz Wasilczyk7e22cab2023-08-24 19:02:33 +00001304 if (!splitName.empty()) {
Adam Lesinski25d35a92014-08-11 09:41:56 -07001305 printf(" split='%s'", ResTable::normalizeForOutput(
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001306 splitName.c_str()).c_str());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001307 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001308
Jackal Guo201a60a2021-08-31 12:37:30 +08001309 // For 'platformBuildVersionName', using both string and int type as a fallback
1310 // since it may be the code name of Android or the API level.
Alan Viverette11be9312017-11-09 15:41:44 -05001311 String8 platformBuildVersionName = AaptXml::getAttribute(tree, NULL,
Adam Lesinski5283fab2014-08-29 11:23:55 -07001312 "platformBuildVersionName");
Jackal Guo201a60a2021-08-31 12:37:30 +08001313 int32_t platformBuildVersionNameInt =
1314 AaptXml::getIntegerAttribute(tree, NULL, "platformBuildVersionName", 0,
1315 NULL);
Alan Viverette11be9312017-11-09 15:41:44 -05001316 if (platformBuildVersionName != "") {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001317 printf(" platformBuildVersionName='%s'", platformBuildVersionName.c_str());
Jackal Guo201a60a2021-08-31 12:37:30 +08001318 } else if (platformBuildVersionNameInt > 0) {
1319 printf(" platformBuildVersionName='%d'", platformBuildVersionNameInt);
Alan Viverette11be9312017-11-09 15:41:44 -05001320 }
1321
Jackal Guo201a60a2021-08-31 12:37:30 +08001322 // For 'platformBuildVersionCode', using both string and int type as a fallback
1323 // since it may be the code name of Android or the API level.
Alan Viverette11be9312017-11-09 15:41:44 -05001324 String8 platformBuildVersionCode = AaptXml::getAttribute(tree, NULL,
1325 "platformBuildVersionCode");
Jackal Guo201a60a2021-08-31 12:37:30 +08001326 int32_t platformBuildVersionCodeInt =
1327 AaptXml::getIntegerAttribute(tree, NULL, "platformBuildVersionCode", 0,
1328 NULL);
Alan Viverette11be9312017-11-09 15:41:44 -05001329 if (platformBuildVersionCode != "") {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001330 printf(" platformBuildVersionCode='%s'", platformBuildVersionCode.c_str());
Jackal Guo201a60a2021-08-31 12:37:30 +08001331 } else if (platformBuildVersionCodeInt > 0) {
1332 printf(" platformBuildVersionCode='%d'", platformBuildVersionCodeInt);
Alan Viverette11be9312017-11-09 15:41:44 -05001333 }
1334
1335 int32_t compileSdkVersion = AaptXml::getIntegerAttribute(tree,
1336 COMPILE_SDK_VERSION_ATTR, &error);
1337 if (error != "") {
1338 SourcePos(manifestFile, tree.getLineNumber()).error(
1339 "ERROR getting 'android:compileSdkVersion' attribute: %s",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001340 error.c_str());
Alan Viverette11be9312017-11-09 15:41:44 -05001341 goto bail;
1342 }
1343 if (compileSdkVersion > 0) {
1344 printf(" compileSdkVersion='%d'", compileSdkVersion);
1345 }
1346
1347 String8 compileSdkVersionCodename = AaptXml::getResolvedAttribute(res, tree,
1348 COMPILE_SDK_VERSION_CODENAME_ATTR, &error);
1349 if (compileSdkVersionCodename != "") {
1350 printf(" compileSdkVersionCodename='%s'", ResTable::normalizeForOutput(
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001351 compileSdkVersionCodename.c_str()).c_str());
Alan Viverette11be9312017-11-09 15:41:44 -05001352 }
1353
Adam Lesinski25d35a92014-08-11 09:41:56 -07001354 printf("\n");
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001355
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001356 int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
1357 INSTALL_LOCATION_ATTR, &error);
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001358 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001359 SourcePos(manifestFile, tree.getLineNumber()).error(
1360 "ERROR getting 'android:installLocation' attribute: %s",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001361 error.c_str());
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001362 goto bail;
1363 }
1364
1365 if (installLocation >= 0) {
1366 printf("install-location:'");
1367 switch (installLocation) {
1368 case 0:
1369 printf("auto");
1370 break;
1371 case 1:
1372 printf("internalOnly");
1373 break;
1374 case 2:
1375 printf("preferExternal");
1376 break;
1377 default:
1378 fprintf(stderr, "Invalid installLocation %d\n", installLocation);
1379 goto bail;
1380 }
1381 printf("'\n");
1382 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001383 } else if (depth == 2) {
1384 withinApplication = false;
1385 if (tag == "application") {
1386 withinApplication = true;
1387
1388 String8 label;
1389 const size_t NL = locales.size();
1390 for (size_t i=0; i<NL; i++) {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001391 const char* localeStr = locales[i].c_str();
Adam Lesinskia77685f2016-10-03 16:26:28 -07001392 assets.setConfiguration(config, localeStr != NULL ? localeStr : "");
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001393 String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1394 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001395 if (llabel != "") {
1396 if (localeStr == NULL || strlen(localeStr) == 0) {
1397 label = llabel;
Maurice Chu2675f762013-10-22 17:33:11 -07001398 printf("application-label:'%s'\n",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001399 ResTable::normalizeForOutput(llabel.c_str()).c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001400 } else {
1401 if (label == "") {
1402 label = llabel;
1403 }
1404 printf("application-label-%s:'%s'\n", localeStr,
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001405 ResTable::normalizeForOutput(llabel.c_str()).c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001406 }
1407 }
1408 }
1409
1410 ResTable_config tmpConfig = config;
1411 const size_t ND = densities.size();
1412 for (size_t i=0; i<ND; i++) {
1413 tmpConfig.density = densities[i];
1414 assets.setConfiguration(tmpConfig);
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001415 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1416 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001417 if (icon != "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001418 printf("application-icon-%d:'%s'\n", densities[i],
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001419 ResTable::normalizeForOutput(icon.c_str()).c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001420 }
1421 }
1422 assets.setConfiguration(config);
1423
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001424 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001425 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001426 SourcePos(manifestFile, tree.getLineNumber()).error(
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001427 "ERROR getting 'android:icon' attribute: %s", error.c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001428 goto bail;
1429 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001430 int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0,
1431 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001432 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001433 SourcePos(manifestFile, tree.getLineNumber()).error(
1434 "ERROR getting 'android:testOnly' attribute: %s",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001435 error.c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001436 goto bail;
1437 }
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001438
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001439 String8 banner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1440 &error);
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001441 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001442 SourcePos(manifestFile, tree.getLineNumber()).error(
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001443 "ERROR getting 'android:banner' attribute: %s", error.c_str());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001444 goto bail;
1445 }
Maurice Chu2675f762013-10-22 17:33:11 -07001446 printf("application: label='%s' ",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001447 ResTable::normalizeForOutput(label.c_str()).c_str());
1448 printf("icon='%s'", ResTable::normalizeForOutput(icon.c_str()).c_str());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001449 if (banner != "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001450 printf(" banner='%s'",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001451 ResTable::normalizeForOutput(banner.c_str()).c_str());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001452 }
1453 printf("\n");
Adam Lesinski282e1812014-01-23 18:17:42 -08001454 if (testOnly != 0) {
1455 printf("testOnly='%d'\n", testOnly);
1456 }
1457
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001458 int32_t isGame = AaptXml::getResolvedIntegerAttribute(res, tree,
1459 ISGAME_ATTR, 0, &error);
1460 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001461 SourcePos(manifestFile, tree.getLineNumber()).error(
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001462 "ERROR getting 'android:isGame' attribute: %s", error.c_str());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001463 goto bail;
1464 }
1465 if (isGame != 0) {
1466 printf("application-isGame\n");
1467 }
1468
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001469 int32_t debuggable = AaptXml::getResolvedIntegerAttribute(res, tree,
1470 DEBUGGABLE_ATTR, 0, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001471 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001472 SourcePos(manifestFile, tree.getLineNumber()).error(
1473 "ERROR getting 'android:debuggable' attribute: %s",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001474 error.c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001475 goto bail;
1476 }
1477 if (debuggable != 0) {
1478 printf("application-debuggable\n");
1479 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07001480
1481 // We must search by name because the multiArch flag hasn't been API
1482 // frozen yet.
1483 int32_t multiArchIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
1484 "multiArch");
1485 if (multiArchIndex >= 0) {
1486 Res_value value;
1487 if (tree.getAttributeValue(multiArchIndex, &value) != NO_ERROR) {
1488 if (value.dataType >= Res_value::TYPE_FIRST_INT &&
1489 value.dataType <= Res_value::TYPE_LAST_INT) {
1490 hasMultiArch = value.data;
1491 }
1492 }
1493 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001494 } else if (tag == "uses-sdk") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001495 int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR,
1496 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001497 if (error != "") {
1498 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001499 String8 name = AaptXml::getResolvedAttribute(res, tree,
1500 MIN_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001501 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001502 SourcePos(manifestFile, tree.getLineNumber()).error(
1503 "ERROR getting 'android:minSdkVersion' attribute: %s",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001504 error.c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001505 goto bail;
1506 }
Jackal Guo201a60a2021-08-31 12:37:30 +08001507 if (name == "Donut") targetSdk = SDK_DONUT;
Maurice Chu2675f762013-10-22 17:33:11 -07001508 printf("sdkVersion:'%s'\n",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001509 ResTable::normalizeForOutput(name.c_str()).c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001510 } else if (code != -1) {
1511 targetSdk = code;
1512 printf("sdkVersion:'%d'\n", code);
1513 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001514 code = AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -08001515 if (code != -1) {
1516 printf("maxSdkVersion:'%d'\n", code);
1517 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001518 code = AaptXml::getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001519 if (error != "") {
1520 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001521 String8 name = AaptXml::getResolvedAttribute(res, tree,
1522 TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001523 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001524 SourcePos(manifestFile, tree.getLineNumber()).error(
1525 "ERROR getting 'android:targetSdkVersion' attribute: %s",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001526 error.c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001527 goto bail;
1528 }
Jackal Guo201a60a2021-08-31 12:37:30 +08001529 if (name == "Donut" && targetSdk < SDK_DONUT) {
1530 targetSdk = SDK_DONUT;
1531 } else if (name != "" && targetSdk == 0) {
1532 // Bump to current development version
1533 targetSdk = SDK_CUR_DEVELOPMENT;
1534 }
Maurice Chu2675f762013-10-22 17:33:11 -07001535 printf("targetSdkVersion:'%s'\n",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001536 ResTable::normalizeForOutput(name.c_str()).c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001537 } else if (code != -1) {
1538 if (targetSdk < code) {
1539 targetSdk = code;
1540 }
1541 printf("targetSdkVersion:'%d'\n", code);
1542 }
1543 } else if (tag == "uses-configuration") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001544 int32_t reqTouchScreen = AaptXml::getIntegerAttribute(tree,
1545 REQ_TOUCH_SCREEN_ATTR, 0);
1546 int32_t reqKeyboardType = AaptXml::getIntegerAttribute(tree,
1547 REQ_KEYBOARD_TYPE_ATTR, 0);
1548 int32_t reqHardKeyboard = AaptXml::getIntegerAttribute(tree,
1549 REQ_HARD_KEYBOARD_ATTR, 0);
1550 int32_t reqNavigation = AaptXml::getIntegerAttribute(tree,
1551 REQ_NAVIGATION_ATTR, 0);
1552 int32_t reqFiveWayNav = AaptXml::getIntegerAttribute(tree,
1553 REQ_FIVE_WAY_NAV_ATTR, 0);
Adam Lesinski282e1812014-01-23 18:17:42 -08001554 printf("uses-configuration:");
1555 if (reqTouchScreen != 0) {
1556 printf(" reqTouchScreen='%d'", reqTouchScreen);
1557 }
1558 if (reqKeyboardType != 0) {
1559 printf(" reqKeyboardType='%d'", reqKeyboardType);
1560 }
1561 if (reqHardKeyboard != 0) {
1562 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1563 }
1564 if (reqNavigation != 0) {
1565 printf(" reqNavigation='%d'", reqNavigation);
1566 }
1567 if (reqFiveWayNav != 0) {
1568 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1569 }
1570 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001571 } else if (tag == "supports-input") {
1572 withinSupportsInput = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001573 } else if (tag == "supports-screens") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001574 smallScreen = AaptXml::getIntegerAttribute(tree,
1575 SMALL_SCREEN_ATTR, 1);
1576 normalScreen = AaptXml::getIntegerAttribute(tree,
1577 NORMAL_SCREEN_ATTR, 1);
1578 largeScreen = AaptXml::getIntegerAttribute(tree,
1579 LARGE_SCREEN_ATTR, 1);
1580 xlargeScreen = AaptXml::getIntegerAttribute(tree,
1581 XLARGE_SCREEN_ATTR, 1);
1582 anyDensity = AaptXml::getIntegerAttribute(tree,
1583 ANY_DENSITY_ATTR, 1);
1584 requiresSmallestWidthDp = AaptXml::getIntegerAttribute(tree,
1585 REQUIRES_SMALLEST_WIDTH_DP_ATTR, 0);
1586 compatibleWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1587 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, 0);
1588 largestWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1589 LARGEST_WIDTH_LIMIT_DP_ATTR, 0);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001590 } else if (tag == "feature-group") {
1591 withinFeatureGroup = true;
1592 FeatureGroup group;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001593 group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001594 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001595 SourcePos(manifestFile, tree.getLineNumber()).error(
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001596 "ERROR getting 'android:label' attribute: %s", error.c_str());
Adam Lesinski2c72b682014-06-24 09:56:01 -07001597 goto bail;
1598 }
1599 featureGroups.add(group);
1600
Adam Lesinski282e1812014-01-23 18:17:42 -08001601 } else if (tag == "uses-feature") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001602 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001603 if (name != "" && error == "") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001604 const char* androidSchema =
1605 "http://schemas.android.com/apk/res/android";
Adam Lesinski282e1812014-01-23 18:17:42 -08001606
Adam Lesinski694d0a72016-04-06 16:12:04 -07001607 int32_t req = AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1,
1608 &error);
1609 if (error != "") {
1610 SourcePos(manifestFile, tree.getLineNumber()).error(
1611 "failed to read attribute 'android:required': %s",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001612 error.c_str());
Adam Lesinski694d0a72016-04-06 16:12:04 -07001613 goto bail;
1614 }
1615
1616 int32_t version = AaptXml::getIntegerAttribute(tree, androidSchema,
1617 "version", 0, &error);
1618 if (error != "") {
1619 SourcePos(manifestFile, tree.getLineNumber()).error(
1620 "failed to read attribute 'android:version': %s",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001621 error.c_str());
Adam Lesinski694d0a72016-04-06 16:12:04 -07001622 goto bail;
1623 }
1624
1625 commonFeatures.features.add(name, Feature(req != 0, version));
Adam Lesinski2c72b682014-06-24 09:56:01 -07001626 if (req) {
1627 addParentFeatures(&commonFeatures, name);
Adam Lesinski282e1812014-01-23 18:17:42 -08001628 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001629 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001630 int vers = AaptXml::getIntegerAttribute(tree,
Adam Lesinski282e1812014-01-23 18:17:42 -08001631 GL_ES_VERSION_ATTR, &error);
1632 if (error == "") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001633 if (vers > commonFeatures.openGLESVersion) {
1634 commonFeatures.openGLESVersion = vers;
1635 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001636 }
1637 }
1638 } else if (tag == "uses-permission") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001639 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001640 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001641 SourcePos(manifestFile, tree.getLineNumber()).error(
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001642 "ERROR getting 'android:name' attribute: %s", error.c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001643 goto bail;
1644 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001645
1646 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001647 SourcePos(manifestFile, tree.getLineNumber()).error(
1648 "ERROR: missing 'android:name' for uses-permission");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001649 goto bail;
1650 }
1651
1652 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, false);
1653
Adam Lesinski2386df22016-12-28 15:08:58 -05001654 const int32_t maxSdkVersion =
1655 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, -1);
Dianne Hackborncd154e92017-02-28 17:37:35 -08001656 const String8 requiredFeature = AaptXml::getAttribute(tree,
1657 REQUIRED_FEATURE_ATTR, &error);
1658 const String8 requiredNotFeature = AaptXml::getAttribute(tree,
1659 REQUIRED_NOT_FEATURE_ATTR, &error);
Adam Lesinski2386df22016-12-28 15:08:58 -05001660
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001661 if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1662 hasWriteExternalStoragePermission = true;
Adam Lesinski2386df22016-12-28 15:08:58 -05001663 writeExternalStoragePermissionMaxSdkVersion = maxSdkVersion;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001664 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1665 hasReadExternalStoragePermission = true;
1666 } else if (name == "android.permission.READ_PHONE_STATE") {
1667 hasReadPhoneStatePermission = true;
1668 } else if (name == "android.permission.READ_CONTACTS") {
1669 hasReadContactsPermission = true;
1670 } else if (name == "android.permission.WRITE_CONTACTS") {
1671 hasWriteContactsPermission = true;
1672 } else if (name == "android.permission.READ_CALL_LOG") {
1673 hasReadCallLogPermission = true;
1674 } else if (name == "android.permission.WRITE_CALL_LOG") {
1675 hasWriteCallLogPermission = true;
1676 }
1677
1678 printUsesPermission(name,
1679 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
Dianne Hackborncd154e92017-02-28 17:37:35 -08001680 maxSdkVersion, requiredFeature, requiredNotFeature);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001681
1682 } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
1683 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1684 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001685 SourcePos(manifestFile, tree.getLineNumber()).error(
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001686 "ERROR getting 'android:name' attribute: %s", error.c_str());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001687 goto bail;
1688 }
1689
1690 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001691 SourcePos(manifestFile, tree.getLineNumber()).error(
1692 "ERROR: missing 'android:name' for uses-permission-sdk-23");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001693 goto bail;
1694 }
1695
1696 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, true);
1697
1698 printUsesPermissionSdk23(
1699 name, AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
1700
Adam Lesinski282e1812014-01-23 18:17:42 -08001701 } else if (tag == "uses-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001702 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001703 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001704 printf("uses-package:'%s'\n",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001705 ResTable::normalizeForOutput(name.c_str()).c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001706 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001707 SourcePos(manifestFile, tree.getLineNumber()).error(
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001708 "ERROR getting 'android:name' attribute: %s", error.c_str());
Adam Lesinski10de3af12016-07-13 10:14:03 -07001709 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001710 }
1711 } else if (tag == "original-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001712 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001713 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001714 printf("original-package:'%s'\n",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001715 ResTable::normalizeForOutput(name.c_str()).c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001716 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001717 SourcePos(manifestFile, tree.getLineNumber()).error(
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001718 "ERROR getting 'android:name' attribute: %s", error.c_str());
Adam Lesinski10de3af12016-07-13 10:14:03 -07001719 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001720 }
1721 } else if (tag == "supports-gl-texture") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001722 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001723 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001724 printf("supports-gl-texture:'%s'\n",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001725 ResTable::normalizeForOutput(name.c_str()).c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001726 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001727 SourcePos(manifestFile, tree.getLineNumber()).error(
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001728 "ERROR getting 'android:name' attribute: %s", error.c_str());
Adam Lesinski10de3af12016-07-13 10:14:03 -07001729 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001730 }
1731 } else if (tag == "compatible-screens") {
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001732 printCompatibleScreens(tree, &error);
1733 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001734 SourcePos(manifestFile, tree.getLineNumber()).error(
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001735 "ERROR getting compatible screens: %s", error.c_str());
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001736 goto bail;
1737 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001738 depth--;
1739 } else if (tag == "package-verifier") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001740 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001741 if (name != "" && error == "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001742 String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR,
1743 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001744 if (publicKey != "" && error == "") {
1745 printf("package-verifier: name='%s' publicKey='%s'\n",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001746 ResTable::normalizeForOutput(name.c_str()).c_str(),
1747 ResTable::normalizeForOutput(publicKey.c_str()).c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001748 }
1749 }
1750 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001751 } else if (depth == 3) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001752 withinActivity = false;
1753 withinReceiver = false;
1754 withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001755 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001756 hasIntentFilter = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001757 hasMetaHostPaymentCategory = false;
1758 hasMetaOffHostPaymentCategory = false;
1759 hasBindDeviceAdminPermission = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001760 hasBindAccessibilityServicePermission = false;
1761 hasBindPrintServicePermission = false;
1762 hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001763 hasRequiredSafAttributes = false;
1764 hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001765 hasBindDreamServicePermission = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001766 if (withinApplication) {
1767 if(tag == "activity") {
1768 withinActivity = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001769 activityName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001770 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001771 SourcePos(manifestFile, tree.getLineNumber()).error(
1772 "ERROR getting 'android:name' attribute: %s",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001773 error.c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001774 goto bail;
1775 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001776
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001777 activityLabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1778 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001779 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001780 SourcePos(manifestFile, tree.getLineNumber()).error(
1781 "ERROR getting 'android:label' attribute: %s",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001782 error.c_str());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001783 goto bail;
1784 }
1785
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001786 activityIcon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1787 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001788 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001789 SourcePos(manifestFile, tree.getLineNumber()).error(
1790 "ERROR getting 'android:icon' attribute: %s",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001791 error.c_str());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001792 goto bail;
1793 }
1794
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001795 activityBanner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1796 &error);
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001797 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001798 SourcePos(manifestFile, tree.getLineNumber()).error(
1799 "ERROR getting 'android:banner' attribute: %s",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001800 error.c_str());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001801 goto bail;
1802 }
1803
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001804 int32_t orien = AaptXml::getResolvedIntegerAttribute(res, tree,
Michael Wrightec4fdec2013-09-06 16:50:52 -07001805 SCREEN_ORIENTATION_ATTR, &error);
1806 if (error == "") {
1807 if (orien == 0 || orien == 6 || orien == 8) {
1808 // Requests landscape, sensorLandscape, or reverseLandscape.
Adam Lesinski43158772015-11-11 15:13:55 -08001809 addImpliedFeature(
1810 &impliedFeatures, "android.hardware.screen.landscape",
1811 String8("one or more activities have specified a "
1812 "landscape orientation"),
1813 false);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001814 } else if (orien == 1 || orien == 7 || orien == 9) {
1815 // Requests portrait, sensorPortrait, or reversePortrait.
Adam Lesinski43158772015-11-11 15:13:55 -08001816 addImpliedFeature(
1817 &impliedFeatures, "android.hardware.screen.portrait",
1818 String8("one or more activities have specified a "
1819 "portrait orientation"),
1820 false);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001821 }
1822 }
1823 } else if (tag == "uses-library") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001824 String8 libraryName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001825 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001826 SourcePos(manifestFile, tree.getLineNumber()).error(
Michael Wrightec4fdec2013-09-06 16:50:52 -07001827 "ERROR getting 'android:name' attribute for uses-library"
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001828 " %s", error.c_str());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001829 goto bail;
1830 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001831 int req = AaptXml::getIntegerAttribute(tree,
1832 REQUIRED_ATTR, 1);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001833 printf("uses-library%s:'%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001834 req ? "" : "-not-required", ResTable::normalizeForOutput(
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001835 libraryName.c_str()).c_str());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001836 } else if (tag == "receiver") {
1837 withinReceiver = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001838 receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001839
1840 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001841 SourcePos(manifestFile, tree.getLineNumber()).error(
Michael Wrightec4fdec2013-09-06 16:50:52 -07001842 "ERROR getting 'android:name' attribute for receiver:"
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001843 " %s", error.c_str());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001844 goto bail;
1845 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001846
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001847 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1848 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001849 if (error == "") {
1850 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1851 hasBindDeviceAdminPermission = true;
1852 }
1853 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001854 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001855 "ERROR getting 'android:permission' attribute for"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001856 " receiver '%s': %s",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001857 receiverName.c_str(), error.c_str());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001858 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001859 } else if (tag == "service") {
1860 withinService = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001861 serviceName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001862
1863 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001864 SourcePos(manifestFile, tree.getLineNumber()).error(
1865 "ERROR getting 'android:name' attribute for "
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001866 "service:%s", error.c_str());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001867 goto bail;
1868 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001869
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001870 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1871 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001872 if (error == "") {
Yi Kongb172c312022-07-20 15:15:33 +08001873 if (permission ==
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001874 "android.permission.BIND_ACCESSIBILITY_SERVICE") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001875 hasBindAccessibilityServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001876 } else if (permission ==
1877 "android.permission.BIND_PRINT_SERVICE") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001878 hasBindPrintServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001879 } else if (permission ==
1880 "android.permission.BIND_NFC_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07001881 hasBindNfcServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001882 } else if (permission ==
1883 "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001884 hasBindNotificationListenerServicePermission = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001885 } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
1886 hasBindDreamServicePermission = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001887 }
1888 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001889 SourcePos(manifestFile, tree.getLineNumber()).error(
1890 "ERROR getting 'android:permission' attribute for "
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001891 "service '%s': %s", serviceName.c_str(), error.c_str());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001892 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001893 } else if (tag == "provider") {
1894 withinProvider = true;
1895
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001896 bool exported = AaptXml::getResolvedIntegerAttribute(res, tree,
1897 EXPORTED_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001898 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001899 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001900 "ERROR getting 'android:exported' attribute for provider:"
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001901 " %s", error.c_str());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001902 goto bail;
1903 }
1904
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001905 bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute(
1906 res, tree, GRANT_URI_PERMISSIONS_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001907 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001908 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001909 "ERROR getting 'android:grantUriPermissions' attribute for "
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001910 "provider: %s", error.c_str());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001911 goto bail;
1912 }
1913
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001914 String8 permission = AaptXml::getResolvedAttribute(res, tree,
1915 PERMISSION_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001916 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001917 SourcePos(manifestFile, tree.getLineNumber()).error(
1918 "ERROR getting 'android:permission' attribute for "
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001919 "provider: %s", error.c_str());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001920 goto bail;
1921 }
1922
1923 hasRequiredSafAttributes |= exported && grantUriPermissions &&
1924 permission == "android.permission.MANAGE_DOCUMENTS";
1925
Michael Wrightec4fdec2013-09-06 16:50:52 -07001926 } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001927 String8 metaDataName = AaptXml::getResolvedAttribute(res, tree,
1928 NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001929 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001930 SourcePos(manifestFile, tree.getLineNumber()).error(
1931 "ERROR getting 'android:name' attribute for "
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001932 "meta-data: %s", error.c_str());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001933 goto bail;
1934 }
Maurice Chu2675f762013-10-22 17:33:11 -07001935 printf("meta-data: name='%s' ",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001936 ResTable::normalizeForOutput(metaDataName.c_str()).c_str());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001937 printResolvedResourceAttribute(res, tree, VALUE_ATTR, String8("value"),
Maurice Chu76327312013-10-16 18:28:46 -07001938 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001939 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001940 // Try looking for a RESOURCE_ATTR
1941 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001942 printResolvedResourceAttribute(res, tree, RESOURCE_ATTR,
Maurice Chu76327312013-10-16 18:28:46 -07001943 String8("resource"), &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001944 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001945 SourcePos(manifestFile, tree.getLineNumber()).error(
1946 "ERROR getting 'android:value' or "
Maurice Chu76327312013-10-16 18:28:46 -07001947 "'android:resource' attribute for "
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001948 "meta-data: %s", error.c_str());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001949 goto bail;
1950 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001951 }
Maurice Chu76327312013-10-16 18:28:46 -07001952 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001953 } else if (withinSupportsInput && tag == "input-type") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001954 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001955 if (name != "" && error == "") {
1956 supportedInput.add(name);
1957 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001958 SourcePos(manifestFile, tree.getLineNumber()).error(
1959 "ERROR getting 'android:name' attribute: %s",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001960 error.c_str());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001961 goto bail;
1962 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001963 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001964 } else if (withinFeatureGroup && tag == "uses-feature") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001965 const String8 androidSchema("http://schemas.android.com/apk/res/android");
Adam Lesinski2c72b682014-06-24 09:56:01 -07001966 FeatureGroup& top = featureGroups.editTop();
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001967
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001968 String8 name = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001969 if (name != "" && error == "") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001970 Feature feature(true);
1971
1972 int32_t featureVers = AaptXml::getIntegerAttribute(
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001973 tree, androidSchema.c_str(), "version", 0, &error);
Adam Lesinski694d0a72016-04-06 16:12:04 -07001974 if (error == "") {
1975 feature.version = featureVers;
1976 } else {
1977 SourcePos(manifestFile, tree.getLineNumber()).error(
1978 "failed to read attribute 'android:version': %s",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001979 error.c_str());
Adam Lesinski694d0a72016-04-06 16:12:04 -07001980 goto bail;
1981 }
1982
1983 top.features.add(name, feature);
Adam Lesinskid3edfde2014-08-08 17:32:44 -07001984 addParentFeatures(&top, name);
Adam Lesinski694d0a72016-04-06 16:12:04 -07001985
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001986 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001987 int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR,
1988 &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001989 if (error == "") {
1990 if (vers > top.openGLESVersion) {
1991 top.openGLESVersion = vers;
1992 }
1993 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001994 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001995 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001996 } else if (depth == 4) {
1997 if (tag == "intent-filter") {
1998 hasIntentFilter = true;
1999 withinIntentFilter = true;
2000 actMainActivity = false;
2001 actWidgetReceivers = false;
2002 actImeService = false;
2003 actWallpaperService = false;
2004 actAccessibilityService = false;
2005 actPrintService = false;
2006 actDeviceAdminEnabled = false;
2007 actHostApduService = false;
2008 actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002009 actDocumentsProvider = false;
2010 actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04002011 actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002012 actCamera = false;
2013 actCameraSecure = false;
2014 catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07002015 } else if (withinService && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07002016 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07002017 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07002018 SourcePos(manifestFile, tree.getLineNumber()).error(
2019 "ERROR getting 'android:name' attribute for "
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00002020 "meta-data tag in service '%s': %s", serviceName.c_str(),
2021 error.c_str());
Adam Lesinski94fc9122013-09-30 17:16:09 -07002022 goto bail;
2023 }
2024
2025 if (name == "android.nfc.cardemulation.host_apdu_service" ||
2026 name == "android.nfc.cardemulation.off_host_apdu_service") {
2027 bool offHost = true;
2028 if (name == "android.nfc.cardemulation.host_apdu_service") {
2029 offHost = false;
2030 }
2031
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07002032 String8 xmlPath = AaptXml::getResolvedAttribute(res, tree,
2033 RESOURCE_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07002034 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07002035 SourcePos(manifestFile, tree.getLineNumber()).error(
2036 "ERROR getting 'android:resource' attribute for "
2037 "meta-data tag in service '%s': %s",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00002038 serviceName.c_str(), error.c_str());
Adam Lesinski94fc9122013-09-30 17:16:09 -07002039 goto bail;
2040 }
2041
2042 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
2043 offHost, &error);
2044 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07002045 SourcePos(manifestFile, tree.getLineNumber()).error(
2046 "ERROR getting AID category for service '%s'",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00002047 serviceName.c_str());
Adam Lesinski94fc9122013-09-30 17:16:09 -07002048 goto bail;
2049 }
2050
2051 const size_t catLen = categories.size();
2052 for (size_t i = 0; i < catLen; i++) {
2053 bool paymentCategory = (categories[i] == "payment");
2054 if (offHost) {
2055 hasMetaOffHostPaymentCategory |= paymentCategory;
2056 } else {
2057 hasMetaHostPaymentCategory |= paymentCategory;
2058 }
2059 }
2060 }
2061 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002062 } else if ((depth == 5) && withinIntentFilter) {
2063 String8 action;
2064 if (tag == "action") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07002065 action = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07002066 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07002067 SourcePos(manifestFile, tree.getLineNumber()).error(
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00002068 "ERROR getting 'android:name' attribute: %s", error.c_str());
Adam Lesinskia5018c92013-09-30 16:23:15 -07002069 goto bail;
Michael Wrightec4fdec2013-09-06 16:50:52 -07002070 }
2071
Adam Lesinskia5018c92013-09-30 16:23:15 -07002072 if (withinActivity) {
2073 if (action == "android.intent.action.MAIN") {
2074 isMainActivity = true;
2075 actMainActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002076 } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
2077 action == "android.media.action.VIDEO_CAMERA") {
2078 actCamera = true;
2079 } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
2080 actCameraSecure = true;
Michael Wrightec4fdec2013-09-06 16:50:52 -07002081 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002082 } else if (withinReceiver) {
2083 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
2084 actWidgetReceivers = true;
2085 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
2086 actDeviceAdminEnabled = true;
2087 }
2088 } else if (withinService) {
2089 if (action == "android.view.InputMethod") {
2090 actImeService = true;
2091 } else if (action == "android.service.wallpaper.WallpaperService") {
2092 actWallpaperService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002093 } else if (action ==
2094 "android.accessibilityservice.AccessibilityService") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07002095 actAccessibilityService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002096 } else if (action =="android.printservice.PrintService") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07002097 actPrintService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002098 } else if (action ==
2099 "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07002100 actHostApduService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002101 } else if (action ==
2102 "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07002103 actOffHostApduService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002104 } else if (action ==
2105 "android.service.notification.NotificationListenerService") {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002106 actNotificationListenerService = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04002107 } else if (action == "android.service.dreams.DreamService") {
2108 actDreamService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002109 }
2110 } else if (withinProvider) {
2111 if (action == "android.content.action.DOCUMENTS_PROVIDER") {
2112 actDocumentsProvider = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07002113 }
2114 }
2115 if (action == "android.intent.action.SEARCH") {
2116 isSearchable = true;
2117 }
2118 }
2119
2120 if (tag == "category") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07002121 String8 category = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07002122 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07002123 SourcePos(manifestFile, tree.getLineNumber()).error(
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00002124 "ERROR getting 'name' attribute: %s", error.c_str());
Adam Lesinskia5018c92013-09-30 16:23:15 -07002125 goto bail;
2126 }
2127 if (withinActivity) {
2128 if (category == "android.intent.category.LAUNCHER") {
2129 isLauncherActivity = true;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08002130 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
2131 isLeanbackLauncherActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002132 } else if (category == "android.intent.category.HOME") {
2133 catLauncher = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08002134 }
2135 }
2136 }
2137 }
2138 }
2139
2140 // Pre-1.6 implicitly granted permission compatibility logic
Jackal Guo201a60a2021-08-31 12:37:30 +08002141 if (targetSdk < SDK_DONUT) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002142 if (!hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002143 printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
2144 printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
2145 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002146 hasWriteExternalStoragePermission = true;
2147 }
2148 if (!hasReadPhoneStatePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002149 printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
2150 printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
2151 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002152 }
2153 }
2154
2155 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
2156 // force them to always take READ_EXTERNAL_STORAGE as well. We always
2157 // do this (regardless of target API version) because we can't have
2158 // an app with write permission but not read permission.
2159 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
Adam Lesinski2386df22016-12-28 15:08:58 -05002160 printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
2161 false /* optional */, writeExternalStoragePermissionMaxSdkVersion);
Adam Lesinski58f1f362013-11-12 12:59:08 -08002162 printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
Adam Lesinski2386df22016-12-28 15:08:58 -05002163 String8("requested WRITE_EXTERNAL_STORAGE"),
2164 writeExternalStoragePermissionMaxSdkVersion);
Adam Lesinski282e1812014-01-23 18:17:42 -08002165 }
2166
2167 // Pre-JellyBean call log permission compatibility.
Jackal Guo201a60a2021-08-31 12:37:30 +08002168 if (targetSdk < SDK_JELLY_BEAN) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002169 if (!hasReadCallLogPermission && hasReadContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002170 printUsesPermission(String8("android.permission.READ_CALL_LOG"));
2171 printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
2172 String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002173 }
2174 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002175 printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
2176 printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
2177 String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002178 }
2179 }
2180
Adam Lesinskica955a42016-08-01 16:44:29 -07002181 // If the app hasn't declared the touchscreen as a feature requirement (either
2182 // directly or implied, required or not), then the faketouch feature is implied.
2183 if (!hasFeature("android.hardware.touchscreen", commonFeatures, impliedFeatures)) {
2184 addImpliedFeature(&impliedFeatures, "android.hardware.faketouch",
Adam Lesinski43158772015-11-11 15:13:55 -08002185 String8("default feature for all apps"), false);
Adam Lesinskica955a42016-08-01 16:44:29 -07002186 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07002187
2188 const size_t numFeatureGroups = featureGroups.size();
2189 if (numFeatureGroups == 0) {
2190 // If no <feature-group> tags were defined, apply auto-implied features.
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002191 printDefaultFeatureGroup(commonFeatures, impliedFeatures);
Adam Lesinski2c72b682014-06-24 09:56:01 -07002192
2193 } else {
2194 // <feature-group> tags are defined, so we ignore implied features and
2195 for (size_t i = 0; i < numFeatureGroups; i++) {
2196 FeatureGroup& grp = featureGroups.editItemAt(i);
2197
Adam Lesinskid7a94da2014-07-25 14:38:54 -07002198 if (commonFeatures.openGLESVersion > grp.openGLESVersion) {
2199 grp.openGLESVersion = commonFeatures.openGLESVersion;
2200 }
2201
Adam Lesinski2c72b682014-06-24 09:56:01 -07002202 // Merge the features defined in the top level (not inside a <feature-group>)
2203 // with this feature group.
2204 const size_t numCommonFeatures = commonFeatures.features.size();
2205 for (size_t j = 0; j < numCommonFeatures; j++) {
2206 if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07002207 grp.features.add(commonFeatures.features.keyAt(j),
2208 commonFeatures.features[j]);
Adam Lesinski2c72b682014-06-24 09:56:01 -07002209 }
2210 }
2211
Adam Lesinski73a05112014-12-08 12:53:17 -08002212 if (!grp.features.isEmpty()) {
Adam Lesinski2c72b682014-06-24 09:56:01 -07002213 printFeatureGroup(grp);
Adam Lesinski282e1812014-01-23 18:17:42 -08002214 }
2215 }
2216 }
2217
Adam Lesinski282e1812014-01-23 18:17:42 -08002218
Adam Lesinski282e1812014-01-23 18:17:42 -08002219 if (hasWidgetReceivers) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002220 printComponentPresence("app-widget");
Adam Lesinski282e1812014-01-23 18:17:42 -08002221 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002222 if (hasDeviceAdminReceiver) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002223 printComponentPresence("device-admin");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002224 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002225 if (hasImeService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002226 printComponentPresence("ime");
Adam Lesinski282e1812014-01-23 18:17:42 -08002227 }
2228 if (hasWallpaperService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002229 printComponentPresence("wallpaper");
Adam Lesinski282e1812014-01-23 18:17:42 -08002230 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002231 if (hasAccessibilityService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002232 printComponentPresence("accessibility");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002233 }
2234 if (hasPrintService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002235 printComponentPresence("print-service");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002236 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07002237 if (hasPaymentService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002238 printComponentPresence("payment");
2239 }
2240 if (isSearchable) {
2241 printComponentPresence("search");
2242 }
2243 if (hasDocumentsProvider) {
2244 printComponentPresence("document-provider");
2245 }
2246 if (hasLauncher) {
2247 printComponentPresence("launcher");
2248 }
2249 if (hasNotificationListenerService) {
2250 printComponentPresence("notification-listener");
2251 }
John Spurlockeb8d1be2014-06-25 17:46:15 -04002252 if (hasDreamService) {
2253 printComponentPresence("dream");
2254 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002255 if (hasCameraActivity) {
2256 printComponentPresence("camera");
2257 }
2258 if (hasCameraSecureActivity) {
2259 printComponentPresence("camera-secure");
2260 }
2261
2262 if (hasMainActivity) {
2263 printf("main\n");
Adam Lesinski94fc9122013-09-30 17:16:09 -07002264 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002265 if (hasOtherActivities) {
2266 printf("other-activities\n");
2267 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002268 if (hasOtherReceivers) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002269 printf("other-receivers\n");
2270 }
2271 if (hasOtherServices) {
2272 printf("other-services\n");
2273 }
2274
2275 // For modern apps, if screen size buckets haven't been specified
2276 // but the new width ranges have, then infer the buckets from them.
2277 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
2278 && requiresSmallestWidthDp > 0) {
2279 int compatWidth = compatibleWidthLimitDp;
2280 if (compatWidth <= 0) {
2281 compatWidth = requiresSmallestWidthDp;
2282 }
2283 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
2284 smallScreen = -1;
2285 } else {
2286 smallScreen = 0;
2287 }
2288 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
2289 normalScreen = -1;
2290 } else {
2291 normalScreen = 0;
2292 }
2293 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
2294 largeScreen = -1;
2295 } else {
2296 largeScreen = 0;
2297 }
2298 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
2299 xlargeScreen = -1;
2300 } else {
2301 xlargeScreen = 0;
2302 }
2303 }
2304
2305 // Determine default values for any unspecified screen sizes,
2306 // based on the target SDK of the package. As of 4 (donut)
2307 // the screen size support was introduced, so all default to
2308 // enabled.
2309 if (smallScreen > 0) {
Jackal Guo201a60a2021-08-31 12:37:30 +08002310 smallScreen = targetSdk >= SDK_DONUT ? -1 : 0;
Adam Lesinski282e1812014-01-23 18:17:42 -08002311 }
2312 if (normalScreen > 0) {
2313 normalScreen = -1;
2314 }
2315 if (largeScreen > 0) {
Jackal Guo201a60a2021-08-31 12:37:30 +08002316 largeScreen = targetSdk >= SDK_DONUT ? -1 : 0;
Adam Lesinski282e1812014-01-23 18:17:42 -08002317 }
2318 if (xlargeScreen > 0) {
2319 // Introduced in Gingerbread.
Jackal Guo201a60a2021-08-31 12:37:30 +08002320 xlargeScreen = targetSdk >= SDK_GINGERBREAD ? -1 : 0;
Adam Lesinski282e1812014-01-23 18:17:42 -08002321 }
2322 if (anyDensity > 0) {
Jackal Guo201a60a2021-08-31 12:37:30 +08002323 anyDensity = (targetSdk >= SDK_DONUT || requiresSmallestWidthDp > 0 ||
2324 compatibleWidthLimitDp > 0)
2325 ? -1
2326 : 0;
Adam Lesinski282e1812014-01-23 18:17:42 -08002327 }
2328 printf("supports-screens:");
2329 if (smallScreen != 0) {
2330 printf(" 'small'");
2331 }
2332 if (normalScreen != 0) {
2333 printf(" 'normal'");
2334 }
2335 if (largeScreen != 0) {
2336 printf(" 'large'");
2337 }
2338 if (xlargeScreen != 0) {
2339 printf(" 'xlarge'");
2340 }
2341 printf("\n");
2342 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
2343 if (requiresSmallestWidthDp > 0) {
2344 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
2345 }
2346 if (compatibleWidthLimitDp > 0) {
2347 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
2348 }
2349 if (largestWidthLimitDp > 0) {
2350 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
2351 }
2352
2353 printf("locales:");
2354 const size_t NL = locales.size();
2355 for (size_t i=0; i<NL; i++) {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00002356 const char* localeStr = locales[i].c_str();
Adam Lesinski282e1812014-01-23 18:17:42 -08002357 if (localeStr == NULL || strlen(localeStr) == 0) {
2358 localeStr = "--_--";
2359 }
2360 printf(" '%s'", localeStr);
2361 }
2362 printf("\n");
2363
2364 printf("densities:");
2365 const size_t ND = densities.size();
2366 for (size_t i=0; i<ND; i++) {
2367 printf(" '%d'", densities[i]);
2368 }
2369 printf("\n");
2370
2371 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
2372 if (dir != NULL) {
2373 if (dir->getFileCount() > 0) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002374 SortedVector<String8> architectures;
Adam Lesinski282e1812014-01-23 18:17:42 -08002375 for (size_t i=0; i<dir->getFileCount(); i++) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002376 architectures.add(ResTable::normalizeForOutput(
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00002377 dir->getFileName(i).c_str()));
Adam Lesinski282e1812014-01-23 18:17:42 -08002378 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07002379
2380 bool outputAltNativeCode = false;
2381 // A multiArch package is one that contains 64-bit and
2382 // 32-bit versions of native code and expects 3rd-party
2383 // apps to load these native code libraries. Since most
2384 // 64-bit systems also support 32-bit apps, the apps
2385 // loading this multiArch package's code may be either
2386 // 32-bit or 64-bit.
2387 if (hasMultiArch) {
2388 // If this is a multiArch package, report the 64-bit
2389 // version only. Then as a separate entry, report the
2390 // rest.
2391 //
2392 // If we report the 32-bit architecture, this APK will
2393 // be installed on a 32-bit device, causing a large waste
2394 // of bandwidth and disk space. This assumes that
2395 // the developer of the multiArch package has also
2396 // made a version that is 32-bit only.
2397 String8 intel64("x86_64");
2398 String8 arm64("arm64-v8a");
2399 ssize_t index = architectures.indexOf(intel64);
2400 if (index < 0) {
2401 index = architectures.indexOf(arm64);
2402 }
2403
2404 if (index >= 0) {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00002405 printf("native-code: '%s'\n", architectures[index].c_str());
Adam Lesinskie47fd122014-08-15 22:25:36 -07002406 architectures.removeAt(index);
2407 outputAltNativeCode = true;
2408 }
2409 }
2410
2411 const size_t archCount = architectures.size();
2412 if (archCount > 0) {
2413 if (outputAltNativeCode) {
2414 printf("alt-");
2415 }
2416 printf("native-code:");
2417 for (size_t i = 0; i < archCount; i++) {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00002418 printf(" '%s'", architectures[i].c_str());
Adam Lesinskie47fd122014-08-15 22:25:36 -07002419 }
2420 printf("\n");
2421 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002422 }
2423 delete dir;
2424 }
2425 } else if (strcmp("badger", option) == 0) {
2426 printf("%s", CONSOLE_DATA);
2427 } else if (strcmp("configurations", option) == 0) {
2428 Vector<ResTable_config> configs;
2429 res.getConfigurations(&configs);
2430 const size_t N = configs.size();
2431 for (size_t i=0; i<N; i++) {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00002432 printf("%s\n", configs[i].toString().c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08002433 }
2434 } else {
2435 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2436 goto bail;
2437 }
2438 }
2439
2440 result = NO_ERROR;
2441
2442bail:
Adam Lesinski10de3af12016-07-13 10:14:03 -07002443 if (SourcePos::hasErrors()) {
2444 SourcePos::printErrors(stderr);
2445 }
2446
Adam Lesinski282e1812014-01-23 18:17:42 -08002447 if (asset) {
2448 delete asset;
2449 }
2450 return (result != NO_ERROR);
2451}
2452
2453
2454/*
2455 * Handle the "add" command, which wants to add files to a new or
2456 * pre-existing archive.
2457 */
2458int doAdd(Bundle* bundle)
2459{
2460 ZipFile* zip = NULL;
2461 status_t result = UNKNOWN_ERROR;
2462 const char* zipFileName;
2463
2464 if (bundle->getUpdate()) {
2465 /* avoid confusion */
2466 fprintf(stderr, "ERROR: can't use '-u' with add\n");
2467 goto bail;
2468 }
2469
2470 if (bundle->getFileSpecCount() < 1) {
2471 fprintf(stderr, "ERROR: must specify zip file name\n");
2472 goto bail;
2473 }
2474 zipFileName = bundle->getFileSpecEntry(0);
2475
2476 if (bundle->getFileSpecCount() < 2) {
2477 fprintf(stderr, "NOTE: nothing to do\n");
2478 goto bail;
2479 }
2480
2481 zip = openReadWrite(zipFileName, true);
2482 if (zip == NULL) {
2483 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2484 goto bail;
2485 }
2486
2487 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2488 const char* fileName = bundle->getFileSpecEntry(i);
2489
Tomasz Wasilczyk804e8192023-08-23 02:22:53 +00002490 if (strcasecmp(getPathExtension(String8(fileName)).c_str(), ".gz") == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002491 printf(" '%s'... (from gzip)\n", fileName);
Tomasz Wasilczyk804e8192023-08-23 02:22:53 +00002492 result = zip->addGzip(fileName, getBasePath(String8(fileName)).c_str(), NULL);
Adam Lesinski282e1812014-01-23 18:17:42 -08002493 } else {
2494 if (bundle->getJunkPath()) {
Tomasz Wasilczyk804e8192023-08-23 02:22:53 +00002495 String8 storageName = getPathLeaf(String8(fileName));
Maurice Chu2675f762013-10-22 17:33:11 -07002496 printf(" '%s' as '%s'...\n", fileName,
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00002497 ResTable::normalizeForOutput(storageName.c_str()).c_str());
2498 result = zip->add(fileName, storageName.c_str(),
Adam Lesinski282e1812014-01-23 18:17:42 -08002499 bundle->getCompressionMethod(), NULL);
2500 } else {
2501 printf(" '%s'...\n", fileName);
2502 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2503 }
2504 }
2505 if (result != NO_ERROR) {
2506 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2507 if (result == NAME_NOT_FOUND) {
2508 fprintf(stderr, ": file not found\n");
2509 } else if (result == ALREADY_EXISTS) {
2510 fprintf(stderr, ": already exists in archive\n");
2511 } else {
2512 fprintf(stderr, "\n");
2513 }
2514 goto bail;
2515 }
2516 }
2517
2518 result = NO_ERROR;
2519
2520bail:
2521 delete zip;
2522 return (result != NO_ERROR);
2523}
2524
2525
2526/*
2527 * Delete files from an existing archive.
2528 */
2529int doRemove(Bundle* bundle)
2530{
2531 ZipFile* zip = NULL;
2532 status_t result = UNKNOWN_ERROR;
2533 const char* zipFileName;
2534
2535 if (bundle->getFileSpecCount() < 1) {
2536 fprintf(stderr, "ERROR: must specify zip file name\n");
2537 goto bail;
2538 }
2539 zipFileName = bundle->getFileSpecEntry(0);
2540
2541 if (bundle->getFileSpecCount() < 2) {
2542 fprintf(stderr, "NOTE: nothing to do\n");
2543 goto bail;
2544 }
2545
2546 zip = openReadWrite(zipFileName, false);
2547 if (zip == NULL) {
2548 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2549 zipFileName);
2550 goto bail;
2551 }
2552
2553 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2554 const char* fileName = bundle->getFileSpecEntry(i);
2555 ZipEntry* entry;
2556
2557 entry = zip->getEntryByName(fileName);
2558 if (entry == NULL) {
2559 printf(" '%s' NOT FOUND\n", fileName);
2560 continue;
2561 }
2562
2563 result = zip->remove(entry);
2564
2565 if (result != NO_ERROR) {
2566 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2567 bundle->getFileSpecEntry(i), zipFileName);
2568 goto bail;
2569 }
2570 }
2571
2572 /* update the archive */
2573 zip->flush();
2574
2575bail:
2576 delete zip;
2577 return (result != NO_ERROR);
2578}
2579
Adam Lesinski3921e872014-05-13 10:56:25 -07002580static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002581 const size_t numDirs = dir->getDirs().size();
2582 for (size_t i = 0; i < numDirs; i++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002583 bool ignore = ignoreConfig;
2584 const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00002585 const char* dirStr = subDir->getLeaf().c_str();
Adam Lesinski3921e872014-05-13 10:56:25 -07002586 if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2587 ignore = true;
2588 }
2589 status_t err = addResourcesToBuilder(subDir, builder, ignore);
Adam Lesinskifab50872014-04-16 14:40:42 -07002590 if (err != NO_ERROR) {
2591 return err;
2592 }
2593 }
2594
2595 const size_t numFiles = dir->getFiles().size();
2596 for (size_t i = 0; i < numFiles; i++) {
2597 sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2598 const size_t numConfigs = gp->getFiles().size();
2599 for (size_t j = 0; j < numConfigs; j++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002600 status_t err = NO_ERROR;
Adam Lesinskic7614e52017-03-16 16:54:23 -07002601 if (ignoreConfig) {
Guang Zhu8c2df712017-03-21 03:53:43 +00002602 err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
Adam Lesinskic7614e52017-03-16 16:54:23 -07002603 } else {
Guang Zhu8c2df712017-03-21 03:53:43 +00002604 err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
Adam Lesinskic7614e52017-03-16 16:54:23 -07002605 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002606 if (err != NO_ERROR) {
2607 fprintf(stderr, "Failed to add %s (%s) to builder.\n",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00002608 gp->getPath().c_str(), gp->getFiles()[j]->getPrintableSource().c_str());
Adam Lesinskifab50872014-04-16 14:40:42 -07002609 return err;
2610 }
2611 }
2612 }
2613 return NO_ERROR;
2614}
2615
2616static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2617 if (split->isBase()) {
2618 return original;
2619 }
2620
Tomasz Wasilczyk804e8192023-08-23 02:22:53 +00002621 String8 ext(getPathExtension(original));
Adam Lesinskifab50872014-04-16 14:40:42 -07002622 if (ext == String8(".apk")) {
2623 return String8::format("%s_%s%s",
Tomasz Wasilczyk804e8192023-08-23 02:22:53 +00002624 getBasePath(original).c_str(),
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00002625 split->getDirectorySafeName().c_str(),
2626 ext.c_str());
Adam Lesinskifab50872014-04-16 14:40:42 -07002627 }
2628
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00002629 return String8::format("%s_%s", original.c_str(),
2630 split->getDirectorySafeName().c_str());
Adam Lesinskifab50872014-04-16 14:40:42 -07002631}
Adam Lesinski282e1812014-01-23 18:17:42 -08002632
2633/*
2634 * Package up an asset directory and associated application files.
2635 */
2636int doPackage(Bundle* bundle)
2637{
2638 const char* outputAPKFile;
2639 int retVal = 1;
2640 status_t err;
2641 sp<AaptAssets> assets;
2642 int N;
2643 FILE* fp;
2644 String8 dependencyFile;
Adam Lesinskifab50872014-04-16 14:40:42 -07002645 sp<ApkBuilder> builder;
Adam Lesinski282e1812014-01-23 18:17:42 -08002646
Anton Krumina2ef5c02014-03-12 14:46:44 -07002647 // -c en_XA or/and ar_XB means do pseudolocalization
Adam Lesinskifab50872014-04-16 14:40:42 -07002648 sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2649 err = configFilter->parse(bundle->getConfigurations());
Adam Lesinski282e1812014-01-23 18:17:42 -08002650 if (err != NO_ERROR) {
2651 goto bail;
2652 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002653 if (configFilter->containsPseudo()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002654 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2655 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002656 if (configFilter->containsPseudoBidi()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002657 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
Adam Lesinski282e1812014-01-23 18:17:42 -08002658 }
2659
2660 N = bundle->getFileSpecCount();
2661 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
Adam Lesinski09384302014-01-22 16:07:42 -08002662 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002663 fprintf(stderr, "ERROR: no input files\n");
2664 goto bail;
2665 }
2666
2667 outputAPKFile = bundle->getOutputAPKFile();
2668
2669 // Make sure the filenames provided exist and are of the appropriate type.
2670 if (outputAPKFile) {
2671 FileType type;
2672 type = getFileType(outputAPKFile);
2673 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2674 fprintf(stderr,
2675 "ERROR: output file '%s' exists but is not regular file\n",
2676 outputAPKFile);
2677 goto bail;
2678 }
2679 }
2680
2681 // Load the assets.
2682 assets = new AaptAssets();
2683
2684 // Set up the resource gathering in assets if we're going to generate
2685 // dependency files. Every time we encounter a resource while slurping
2686 // the tree, we'll add it to these stores so we have full resource paths
2687 // to write to a dependency file.
2688 if (bundle->getGenDependencies()) {
2689 sp<FilePathStore> resPathStore = new FilePathStore;
2690 assets->setFullResPaths(resPathStore);
2691 sp<FilePathStore> assetPathStore = new FilePathStore;
2692 assets->setFullAssetPaths(assetPathStore);
2693 }
2694
2695 err = assets->slurpFromArgs(bundle);
2696 if (err < 0) {
2697 goto bail;
2698 }
2699
2700 if (bundle->getVerbose()) {
2701 assets->print(String8());
2702 }
2703
Adam Lesinskifab50872014-04-16 14:40:42 -07002704 // Create the ApkBuilder, which will collect the compiled files
2705 // to write to the final APK (or sets of APKs if we are building
2706 // a Split APK.
2707 builder = new ApkBuilder(configFilter);
2708
2709 // If we are generating a Split APK, find out which configurations to split on.
2710 if (bundle->getSplitConfigurations().size() > 0) {
2711 const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2712 const size_t numSplits = splitStrs.size();
2713 for (size_t i = 0; i < numSplits; i++) {
2714 std::set<ConfigDescription> configs;
2715 if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00002716 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].c_str());
Adam Lesinskifab50872014-04-16 14:40:42 -07002717 goto bail;
2718 }
2719
2720 err = builder->createSplitForConfigs(configs);
2721 if (err != NO_ERROR) {
2722 goto bail;
2723 }
2724 }
2725 }
2726
Adam Lesinski282e1812014-01-23 18:17:42 -08002727 // If they asked for any fileAs that need to be compiled, do so.
2728 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002729 err = buildResources(bundle, assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002730 if (err != 0) {
2731 goto bail;
2732 }
2733 }
2734
2735 // At this point we've read everything and processed everything. From here
2736 // on out it's just writing output files.
2737 if (SourcePos::hasErrors()) {
2738 goto bail;
2739 }
2740
2741 // Update symbols with information about which ones are needed as Java symbols.
2742 assets->applyJavaSymbols();
2743 if (SourcePos::hasErrors()) {
2744 goto bail;
2745 }
2746
2747 // If we've been asked to generate a dependency file, do that here
2748 if (bundle->getGenDependencies()) {
2749 // If this is the packaging step, generate the dependency file next to
2750 // the output apk (e.g. bin/resources.ap_.d)
2751 if (outputAPKFile) {
2752 dependencyFile = String8(outputAPKFile);
2753 // Add the .d extension to the dependency file.
2754 dependencyFile.append(".d");
2755 } else {
2756 // Else if this is the R.java dependency generation step,
2757 // generate the dependency file in the R.java package subdirectory
2758 // e.g. gen/com/foo/app/R.java.d
2759 dependencyFile = String8(bundle->getRClassDir());
Tomasz Wasilczyk804e8192023-08-23 02:22:53 +00002760 appendPath(dependencyFile, "R.java.d");
Adam Lesinski282e1812014-01-23 18:17:42 -08002761 }
2762 // Make sure we have a clean dependency file to start with
2763 fp = fopen(dependencyFile, "w");
2764 fclose(fp);
2765 }
2766
2767 // Write out R.java constants
2768 if (!assets->havePrivateSymbols()) {
2769 if (bundle->getCustomPackage() == NULL) {
2770 // Write the R.java file into the appropriate class directory
2771 // e.g. gen/com/foo/app/R.java
Adam Lesinski1e4663852014-08-15 14:47:28 -07002772 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002773 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002774 } else {
2775 const String8 customPkg(bundle->getCustomPackage());
Adam Lesinski1e4663852014-08-15 14:47:28 -07002776 err = writeResourceSymbols(bundle, assets, customPkg, true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002777 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002778 }
2779 if (err < 0) {
2780 goto bail;
2781 }
2782 // If we have library files, we're going to write our R.java file into
2783 // the appropriate class directory for those libraries as well.
2784 // e.g. gen/com/foo/app/lib/R.java
2785 if (bundle->getExtraPackages() != NULL) {
2786 // Split on colon
2787 String8 libs(bundle->getExtraPackages());
2788 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2789 while (packageString != NULL) {
2790 // Write the R.java file out with the correct package name
Marcin Kosiba0f3a5a62014-09-11 13:48:48 +01002791 err = writeResourceSymbols(bundle, assets, String8(packageString), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002792 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002793 if (err < 0) {
2794 goto bail;
2795 }
2796 packageString = strtok(NULL, ":");
2797 }
2798 libs.unlockBuffer();
2799 }
2800 } else {
Adam Lesinski1e4663852014-08-15 14:47:28 -07002801 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002802 if (err < 0) {
2803 goto bail;
2804 }
Adam Lesinski1e4663852014-08-15 14:47:28 -07002805 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002806 if (err < 0) {
2807 goto bail;
2808 }
2809 }
2810
2811 // Write out the ProGuard file
2812 err = writeProguardFile(bundle, assets);
2813 if (err < 0) {
2814 goto bail;
2815 }
2816
Rohit Agrawal86229cb2016-04-21 16:29:58 -07002817 // Write out the Main Dex ProGuard file
2818 err = writeMainDexProguardFile(bundle, assets);
2819 if (err < 0) {
2820 goto bail;
2821 }
2822
Adam Lesinski282e1812014-01-23 18:17:42 -08002823 // Write the apk
2824 if (outputAPKFile) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002825 // Gather all resources and add them to the APK Builder. The builder will then
2826 // figure out which Split they belong in.
2827 err = addResourcesToBuilder(assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002828 if (err != NO_ERROR) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002829 goto bail;
2830 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002831
2832 const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2833 const size_t numSplits = splits.size();
2834 for (size_t i = 0; i < numSplits; i++) {
2835 const sp<ApkSplit>& split = splits[i];
2836 String8 outputPath = buildApkName(String8(outputAPKFile), split);
2837 err = writeAPK(bundle, outputPath, split);
2838 if (err != NO_ERROR) {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00002839 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.c_str());
Adam Lesinskifab50872014-04-16 14:40:42 -07002840 goto bail;
2841 }
2842 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002843 }
2844
2845 // If we've been asked to generate a dependency file, we need to finish up here.
2846 // the writeResourceSymbols and writeAPK functions have already written the target
2847 // half of the dependency file, now we need to write the prerequisites. (files that
2848 // the R.java file or .ap_ file depend on)
2849 if (bundle->getGenDependencies()) {
2850 // Now that writeResourceSymbols or writeAPK has taken care of writing
2851 // the targets to our dependency file, we'll write the prereqs
2852 fp = fopen(dependencyFile, "a+");
2853 fprintf(fp, " : ");
2854 bool includeRaw = (outputAPKFile != NULL);
2855 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2856 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2857 // and therefore was not added to our pathstores during slurping
2858 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2859 fclose(fp);
2860 }
2861
2862 retVal = 0;
2863bail:
2864 if (SourcePos::hasErrors()) {
2865 SourcePos::printErrors(stderr);
2866 }
2867 return retVal;
2868}
2869
2870/*
2871 * Do PNG Crunching
2872 * PRECONDITIONS
2873 * -S flag points to a source directory containing drawable* folders
2874 * -C flag points to destination directory. The folder structure in the
2875 * source directory will be mirrored to the destination (cache) directory
2876 *
2877 * POSTCONDITIONS
2878 * Destination directory will be updated to match the PNG files in
Maurice Chu2675f762013-10-22 17:33:11 -07002879 * the source directory.
Adam Lesinski282e1812014-01-23 18:17:42 -08002880 */
2881int doCrunch(Bundle* bundle)
2882{
2883 fprintf(stdout, "Crunching PNG Files in ");
2884 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2885 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2886
2887 updatePreProcessedCache(bundle);
2888
2889 return NO_ERROR;
2890}
2891
2892/*
2893 * Do PNG Crunching on a single flag
2894 * -i points to a single png file
2895 * -o points to a single png output file
2896 */
2897int doSingleCrunch(Bundle* bundle)
2898{
2899 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2900 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2901
2902 String8 input(bundle->getSingleCrunchInputFile());
2903 String8 output(bundle->getSingleCrunchOutputFile());
2904
2905 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2906 // we can't return the status_t as it gets truncate to the lower 8 bits.
2907 return 42;
2908 }
2909
2910 return NO_ERROR;
2911}
2912
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002913int runInDaemonMode(Bundle* bundle) {
2914 std::cout << "Ready" << std::endl;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002915 for (std::string cmd; std::getline(std::cin, cmd);) {
2916 if (cmd == "quit") {
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002917 return NO_ERROR;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002918 } else if (cmd == "s") {
2919 // Two argument crunch
2920 std::string inputFile, outputFile;
2921 std::getline(std::cin, inputFile);
2922 std::getline(std::cin, outputFile);
2923 bundle->setSingleCrunchInputFile(inputFile.c_str());
2924 bundle->setSingleCrunchOutputFile(outputFile.c_str());
2925 std::cout << "Crunching " << inputFile << std::endl;
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002926 if (doSingleCrunch(bundle) != NO_ERROR) {
2927 std::cout << "Error" << std::endl;
2928 }
2929 std::cout << "Done" << std::endl;
2930 } else {
2931 // in case of invalid command, just bail out.
2932 std::cerr << "Unknown command" << std::endl;
2933 return -1;
2934 }
2935 }
2936 return -1;
2937}
2938
Adam Lesinski282e1812014-01-23 18:17:42 -08002939char CONSOLE_DATA[2925] = {
2940 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2941 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2942 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2943 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2944 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2945 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2946 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2947 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2948 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2949 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2950 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2951 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2952 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2953 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2954 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2955 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2956 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2957 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2958 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2959 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2960 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2961 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2962 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2963 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2964 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2965 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2966 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2967 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2968 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2969 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2970 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2971 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2972 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2973 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2974 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2975 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2976 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2977 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2978 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2979 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2980 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2981 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2982 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2983 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2984 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2985 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2986 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2987 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2988 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2989 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2990 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2991 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2992 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2993 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2994 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2995 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2996 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2997 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2998 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2999 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
3000 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
3001 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
3002 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
3003 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
3004 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
3005 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
3006 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
3007 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
3008 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
3009 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
3010 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
3011 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
3012 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3013 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
3014 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
3015 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
3016 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3017 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
3018 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
3019 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
3020 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
3021 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
3022 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
3023 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3024 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
3025 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
3026 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
3027 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
3028 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
3029 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3030 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3031 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
3032 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
3033 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3034 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3035 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
3036 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3037 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
3038 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
3039 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
3040 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3041 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3042 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
3043 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
3044 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
3045 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3046 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
3047 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3048 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3049 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
3050 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
3051 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3052 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3053 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
3054 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3055 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
3056 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
3057 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
3058 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3059 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3060 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
3061 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
3062 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
3063 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
3064 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
3065 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
3066 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
3067 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
3068 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
3069 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3070 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3071 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
3072 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
3073 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
3074 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
3075 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
3076 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3077 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3078 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
3079 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
3080 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
3081 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3082 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
3083 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3084 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
3085 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
3086 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
3087 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3088 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3089 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
3090 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
3091 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
3092 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
3093 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
3094 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3095 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3096 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
3097 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
3098 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3099 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3100 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3101 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3102 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
3103 };