blob: 5e85802551970a1e1a0f810ac4d7470da28e1fd3 [file] [log] [blame]
Adam Lesinski282e1812014-01-23 18:17:42 -08001//
2// Copyright 2006 The Android Open Source Project
3//
4// Android Asset Packaging Tool main entry point.
5//
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07006#include "AaptXml.h"
Adam Lesinskifab50872014-04-16 14:40:42 -07007#include "ApkBuilder.h"
Adam Lesinski282e1812014-01-23 18:17:42 -08008#include "Bundle.h"
Adam Lesinski2c72b682014-06-24 09:56:01 -07009#include "Images.h"
10#include "Main.h"
Adam Lesinski282e1812014-01-23 18:17:42 -080011#include "ResourceFilter.h"
12#include "ResourceTable.h"
Adam Lesinski282e1812014-01-23 18:17:42 -080013#include "XMLNode.h"
14
Adam Lesinski282e1812014-01-23 18:17:42 -080015#include <utils/Errors.h>
Adam Lesinski2c72b682014-06-24 09:56:01 -070016#include <utils/KeyedVector.h>
17#include <utils/List.h>
18#include <utils/Log.h>
19#include <utils/SortedVector.h>
20#include <utils/threads.h>
21#include <utils/Vector.h>
Adam Lesinski282e1812014-01-23 18:17:42 -080022
Adam Lesinski282e1812014-01-23 18:17:42 -080023#include <errno.h>
Adam Lesinski2c72b682014-06-24 09:56:01 -070024#include <fcntl.h>
Adam Lesinski282e1812014-01-23 18:17:42 -080025
Jerome Dochez6f1280c2014-09-26 10:21:21 -070026#include <iostream>
27#include <string>
28#include <sstream>
29
Adam Lesinski282e1812014-01-23 18:17:42 -080030using namespace android;
31
Adam Lesinski282e1812014-01-23 18:17:42 -080032/*
33 * Open the file read only. The call fails if the file doesn't exist.
34 *
35 * Returns NULL on failure.
36 */
37ZipFile* openReadOnly(const char* fileName)
38{
39 ZipFile* zip;
40 status_t result;
41
42 zip = new ZipFile;
43 result = zip->open(fileName, ZipFile::kOpenReadOnly);
44 if (result != NO_ERROR) {
45 if (result == NAME_NOT_FOUND) {
46 fprintf(stderr, "ERROR: '%s' not found\n", fileName);
47 } else if (result == PERMISSION_DENIED) {
48 fprintf(stderr, "ERROR: '%s' access denied\n", fileName);
49 } else {
50 fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n",
51 fileName);
52 }
53 delete zip;
54 return NULL;
55 }
56
57 return zip;
58}
59
60/*
61 * Open the file read-write. The file will be created if it doesn't
62 * already exist and "okayToCreate" is set.
63 *
64 * Returns NULL on failure.
65 */
66ZipFile* openReadWrite(const char* fileName, bool okayToCreate)
67{
68 ZipFile* zip = NULL;
69 status_t result;
70 int flags;
71
72 flags = ZipFile::kOpenReadWrite;
73 if (okayToCreate) {
74 flags |= ZipFile::kOpenCreate;
75 }
76
77 zip = new ZipFile;
78 result = zip->open(fileName, flags);
79 if (result != NO_ERROR) {
80 delete zip;
81 zip = NULL;
82 goto bail;
83 }
84
85bail:
86 return zip;
87}
88
89
90/*
91 * Return a short string describing the compression method.
92 */
93const char* compressionName(int method)
94{
95 if (method == ZipEntry::kCompressStored) {
96 return "Stored";
97 } else if (method == ZipEntry::kCompressDeflated) {
98 return "Deflated";
99 } else {
100 return "Unknown";
101 }
102}
103
104/*
105 * Return the percent reduction in size (0% == no compression).
106 */
107int calcPercent(long uncompressedLen, long compressedLen)
108{
109 if (!uncompressedLen) {
110 return 0;
111 } else {
112 return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5);
113 }
114}
115
116/*
117 * Handle the "list" command, which can be a simple file dump or
118 * a verbose listing.
119 *
120 * The verbose listing closely matches the output of the Info-ZIP "unzip"
121 * command.
122 */
123int doList(Bundle* bundle)
124{
125 int result = 1;
126 ZipFile* zip = NULL;
127 const ZipEntry* entry;
128 long totalUncLen, totalCompLen;
129 const char* zipFileName;
130
131 if (bundle->getFileSpecCount() != 1) {
132 fprintf(stderr, "ERROR: specify zip file name (only)\n");
133 goto bail;
134 }
135 zipFileName = bundle->getFileSpecEntry(0);
136
137 zip = openReadOnly(zipFileName);
138 if (zip == NULL) {
139 goto bail;
140 }
141
142 int count, i;
143
144 if (bundle->getVerbose()) {
145 printf("Archive: %s\n", zipFileName);
146 printf(
147 " Length Method Size Ratio Offset Date Time CRC-32 Name\n");
148 printf(
149 "-------- ------ ------- ----- ------- ---- ---- ------ ----\n");
150 }
151
152 totalUncLen = totalCompLen = 0;
153
154 count = zip->getNumEntries();
155 for (i = 0; i < count; i++) {
156 entry = zip->getEntryByIndex(i);
157 if (bundle->getVerbose()) {
158 char dateBuf[32];
159 time_t when;
160
161 when = entry->getModWhen();
162 strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M",
163 localtime(&when));
164
165 printf("%8ld %-7.7s %7ld %3d%% %8zd %s %08lx %s\n",
166 (long) entry->getUncompressedLen(),
167 compressionName(entry->getCompressionMethod()),
168 (long) entry->getCompressedLen(),
169 calcPercent(entry->getUncompressedLen(),
170 entry->getCompressedLen()),
171 (size_t) entry->getLFHOffset(),
172 dateBuf,
173 entry->getCRC32(),
174 entry->getFileName());
175 } else {
176 printf("%s\n", entry->getFileName());
177 }
178
179 totalUncLen += entry->getUncompressedLen();
180 totalCompLen += entry->getCompressedLen();
181 }
182
183 if (bundle->getVerbose()) {
184 printf(
185 "-------- ------- --- -------\n");
186 printf("%8ld %7ld %2d%% %d files\n",
187 totalUncLen,
188 totalCompLen,
189 calcPercent(totalUncLen, totalCompLen),
190 zip->getNumEntries());
191 }
192
193 if (bundle->getAndroidList()) {
194 AssetManager assets;
195 if (!assets.addAssetPath(String8(zipFileName), NULL)) {
196 fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n");
197 goto bail;
198 }
199
Elliott Hughesba3fe562015-08-12 14:49:53 -0700200#ifdef __ANDROID__
Andreas Gampeb8dc7bc2014-10-01 19:07:51 -0700201 static const bool kHaveAndroidOs = true;
202#else
203 static const bool kHaveAndroidOs = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800204#endif
Andreas Gampeb8dc7bc2014-10-01 19:07:51 -0700205 const ResTable& res = assets.getResources(false);
206 if (!kHaveAndroidOs) {
207 printf("\nResource table:\n");
208 res.print(false);
209 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800210
211 Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
212 Asset::ACCESS_BUFFER);
213 if (manifestAsset == NULL) {
214 printf("\nNo AndroidManifest.xml found.\n");
215 } else {
216 printf("\nAndroid manifest:\n");
217 ResXMLTree tree;
218 tree.setTo(manifestAsset->getBuffer(true),
219 manifestAsset->getLength());
220 printXMLBlock(&tree);
221 }
222 delete manifestAsset;
223 }
224
225 result = 0;
226
227bail:
228 delete zip;
229 return result;
230}
231
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700232static void printResolvedResourceAttribute(const ResTable& resTable, const ResXMLTree& tree,
Chih-Hung Hsieh9b8528f2016-08-10 14:15:30 -0700233 uint32_t attrRes, const String8& attrLabel, String8* outError)
Maurice Chu76327312013-10-16 18:28:46 -0700234{
235 Res_value value;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700236 AaptXml::getResolvedResourceAttribute(resTable, tree, attrRes, &value, outError);
Maurice Chu76327312013-10-16 18:28:46 -0700237 if (*outError != "") {
238 *outError = "error print resolved resource attribute";
239 return;
240 }
241 if (value.dataType == Res_value::TYPE_STRING) {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700242 String8 result = AaptXml::getResolvedAttribute(resTable, tree, attrRes, outError);
Maurice Chu2675f762013-10-22 17:33:11 -0700243 printf("%s='%s'", attrLabel.string(),
244 ResTable::normalizeForOutput(result.string()).string());
Maurice Chu76327312013-10-16 18:28:46 -0700245 } else if (Res_value::TYPE_FIRST_INT <= value.dataType &&
246 value.dataType <= Res_value::TYPE_LAST_INT) {
247 printf("%s='%d'", attrLabel.string(), value.data);
248 } else {
249 printf("%s='0x%x'", attrLabel.string(), (int)value.data);
250 }
251}
252
Adam Lesinski282e1812014-01-23 18:17:42 -0800253// These are attribute resource constants for the platform, as found
254// in android.R.attr
255enum {
256 LABEL_ATTR = 0x01010001,
257 ICON_ATTR = 0x01010002,
258 NAME_ATTR = 0x01010003,
Adam Lesinskia5018c92013-09-30 16:23:15 -0700259 PERMISSION_ATTR = 0x01010006,
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700260 EXPORTED_ATTR = 0x01010010,
261 GRANT_URI_PERMISSIONS_ATTR = 0x0101001b,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700262 RESOURCE_ATTR = 0x01010025,
Adam Lesinski282e1812014-01-23 18:17:42 -0800263 DEBUGGABLE_ATTR = 0x0101000f,
264 VALUE_ATTR = 0x01010024,
265 VERSION_CODE_ATTR = 0x0101021b,
266 VERSION_NAME_ATTR = 0x0101021c,
267 SCREEN_ORIENTATION_ATTR = 0x0101001e,
268 MIN_SDK_VERSION_ATTR = 0x0101020c,
269 MAX_SDK_VERSION_ATTR = 0x01010271,
270 REQ_TOUCH_SCREEN_ATTR = 0x01010227,
271 REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
272 REQ_HARD_KEYBOARD_ATTR = 0x01010229,
273 REQ_NAVIGATION_ATTR = 0x0101022a,
274 REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
275 TARGET_SDK_VERSION_ATTR = 0x01010270,
276 TEST_ONLY_ATTR = 0x01010272,
277 ANY_DENSITY_ATTR = 0x0101026c,
278 GL_ES_VERSION_ATTR = 0x01010281,
279 SMALL_SCREEN_ATTR = 0x01010284,
280 NORMAL_SCREEN_ATTR = 0x01010285,
281 LARGE_SCREEN_ATTR = 0x01010286,
282 XLARGE_SCREEN_ATTR = 0x010102bf,
283 REQUIRED_ATTR = 0x0101028e,
Adam Lesinskicaf797c2014-08-22 12:56:26 -0700284 INSTALL_LOCATION_ATTR = 0x010102b7,
Adam Lesinski282e1812014-01-23 18:17:42 -0800285 SCREEN_SIZE_ATTR = 0x010102ca,
286 SCREEN_DENSITY_ATTR = 0x010102cb,
287 REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
288 COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
289 LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
290 PUBLIC_KEY_ATTR = 0x010103a6,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700291 CATEGORY_ATTR = 0x010103e8,
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800292 BANNER_ATTR = 0x10103f2,
Tim Kilbournd9b1cad2014-10-24 12:43:41 -0700293 ISGAME_ATTR = 0x10103f4,
Dianne Hackborncd154e92017-02-28 17:37:35 -0800294 REQUIRED_FEATURE_ATTR = 0x1010557,
295 REQUIRED_NOT_FEATURE_ATTR = 0x1010558,
Adam Lesinski282e1812014-01-23 18:17:42 -0800296};
297
Maurice Chu2675f762013-10-22 17:33:11 -0700298String8 getComponentName(String8 &pkgName, String8 &componentName) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800299 ssize_t idx = componentName.find(".");
300 String8 retStr(pkgName);
301 if (idx == 0) {
302 retStr += componentName;
303 } else if (idx < 0) {
304 retStr += ".";
305 retStr += componentName;
306 } else {
Maurice Chu2675f762013-10-22 17:33:11 -0700307 return componentName;
Adam Lesinski282e1812014-01-23 18:17:42 -0800308 }
Maurice Chu2675f762013-10-22 17:33:11 -0700309 return retStr;
Adam Lesinski282e1812014-01-23 18:17:42 -0800310}
311
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700312static void printCompatibleScreens(ResXMLTree& tree, String8* outError) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800313 size_t len;
314 ResXMLTree::event_code_t code;
315 int depth = 0;
316 bool first = true;
317 printf("compatible-screens:");
318 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
319 if (code == ResXMLTree::END_TAG) {
320 depth--;
321 if (depth < 0) {
322 break;
323 }
324 continue;
325 }
326 if (code != ResXMLTree::START_TAG) {
327 continue;
328 }
329 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700330 const char16_t* ctag16 = tree.getElementName(&len);
331 if (ctag16 == NULL) {
332 *outError = "failed to get XML element name (bad string pool)";
333 return;
334 }
335 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800336 if (tag == "screen") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700337 int32_t screenSize = AaptXml::getIntegerAttribute(tree,
338 SCREEN_SIZE_ATTR);
339 int32_t screenDensity = AaptXml::getIntegerAttribute(tree,
340 SCREEN_DENSITY_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -0800341 if (screenSize > 0 && screenDensity > 0) {
342 if (!first) {
343 printf(",");
344 }
345 first = false;
346 printf("'%d/%d'", screenSize, screenDensity);
347 }
348 }
349 }
350 printf("\n");
351}
352
Dianne Hackborncd154e92017-02-28 17:37:35 -0800353static void printUsesPermission(const String8& name, bool optional=false, int maxSdkVersion=-1,
354 const String8& requiredFeature = String8::empty(),
355 const String8& requiredNotFeature = String8::empty()) {
Adam Lesinski58f1f362013-11-12 12:59:08 -0800356 printf("uses-permission: name='%s'", ResTable::normalizeForOutput(name.string()).string());
357 if (maxSdkVersion != -1) {
358 printf(" maxSdkVersion='%d'", maxSdkVersion);
359 }
Dianne Hackborncd154e92017-02-28 17:37:35 -0800360 if (requiredFeature.length() > 0) {
361 printf(" requiredFeature='%s'", requiredFeature.string());
362 }
363 if (requiredNotFeature.length() > 0) {
364 printf(" requiredNotFeature='%s'", requiredNotFeature.string());
365 }
Adam Lesinski58f1f362013-11-12 12:59:08 -0800366 printf("\n");
367
368 if (optional) {
369 printf("optional-permission: name='%s'",
370 ResTable::normalizeForOutput(name.string()).string());
371 if (maxSdkVersion != -1) {
372 printf(" maxSdkVersion='%d'", maxSdkVersion);
373 }
374 printf("\n");
375 }
376}
377
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800378static void printUsesPermissionSdk23(const String8& name, int maxSdkVersion=-1) {
379 printf("uses-permission-sdk-23: ");
380
381 printf("name='%s'", ResTable::normalizeForOutput(name.string()).string());
382 if (maxSdkVersion != -1) {
383 printf(" maxSdkVersion='%d'", maxSdkVersion);
384 }
385 printf("\n");
386}
387
Adam Lesinski2386df22016-12-28 15:08:58 -0500388static void printUsesImpliedPermission(const String8& name, const String8& reason,
389 const int32_t maxSdkVersion = -1) {
390 printf("uses-implied-permission: name='%s'",
391 ResTable::normalizeForOutput(name.string()).string());
392 if (maxSdkVersion != -1) {
393 printf(" maxSdkVersion='%d'", maxSdkVersion);
394 }
395 printf(" reason='%s'\n", ResTable::normalizeForOutput(reason.string()).string());
Adam Lesinski58f1f362013-11-12 12:59:08 -0800396}
397
Chih-Hung Hsieh9b8528f2016-08-10 14:15:30 -0700398Vector<String8> getNfcAidCategories(AssetManager& assets, const String8& xmlPath, bool offHost,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700399 String8 *outError = NULL)
400{
401 Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER);
402 if (aidAsset == NULL) {
403 if (outError != NULL) *outError = "xml resource does not exist";
404 return Vector<String8>();
405 }
406
407 const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service");
408
409 bool withinApduService = false;
410 Vector<String8> categories;
411
412 String8 error;
413 ResXMLTree tree;
414 tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength());
415
416 size_t len;
417 int depth = 0;
418 ResXMLTree::event_code_t code;
419 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
420 if (code == ResXMLTree::END_TAG) {
421 depth--;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700422 const char16_t* ctag16 = tree.getElementName(&len);
423 if (ctag16 == NULL) {
424 *outError = "failed to get XML element name (bad string pool)";
425 return Vector<String8>();
426 }
427 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700428
429 if (depth == 0 && tag == serviceTagName) {
430 withinApduService = false;
431 }
432
433 } else if (code == ResXMLTree::START_TAG) {
434 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700435 const char16_t* ctag16 = tree.getElementName(&len);
436 if (ctag16 == NULL) {
437 *outError = "failed to get XML element name (bad string pool)";
438 return Vector<String8>();
439 }
440 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700441
442 if (depth == 1) {
443 if (tag == serviceTagName) {
444 withinApduService = true;
445 }
446 } else if (depth == 2 && withinApduService) {
447 if (tag == "aid-group") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700448 String8 category = AaptXml::getAttribute(tree, CATEGORY_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700449 if (error != "") {
450 if (outError != NULL) *outError = error;
451 return Vector<String8>();
452 }
453
454 categories.add(category);
455 }
456 }
457 }
458 }
459 aidAsset->close();
460 return categories;
461}
462
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700463static void printComponentPresence(const char* componentName) {
464 printf("provides-component:'%s'\n", componentName);
465}
466
Adam Lesinski2c72b682014-06-24 09:56:01 -0700467/**
468 * Represents a feature that has been automatically added due to
469 * a pre-requisite or some other reason.
470 */
471struct ImpliedFeature {
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800472 ImpliedFeature() : impliedBySdk23(false) {}
473 ImpliedFeature(const String8& n, bool sdk23) : name(n), impliedBySdk23(sdk23) {}
474
Adam Lesinski2c72b682014-06-24 09:56:01 -0700475 /**
476 * Name of the implied feature.
477 */
478 String8 name;
479
480 /**
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800481 * Was this implied by a permission from SDK 23 (<uses-permission-sdk-23 />)?
482 */
483 bool impliedBySdk23;
484
485 /**
Adam Lesinski2c72b682014-06-24 09:56:01 -0700486 * List of human-readable reasons for why this feature was implied.
487 */
488 SortedVector<String8> reasons;
489};
490
Adam Lesinski694d0a72016-04-06 16:12:04 -0700491struct Feature {
492 Feature() : required(false), version(-1) {}
Chih-Hung Hsiehd53e3be2016-05-03 10:02:51 -0700493 explicit Feature(bool required, int32_t version = -1) : required(required), version(version) {}
Adam Lesinski694d0a72016-04-06 16:12:04 -0700494
495 /**
496 * Whether the feature is required.
497 */
498 bool required;
499
500 /**
501 * What version of the feature is requested.
502 */
503 int32_t version;
504};
505
Adam Lesinski2c72b682014-06-24 09:56:01 -0700506/**
507 * Represents a <feature-group> tag in the AndroidManifest.xml
508 */
509struct FeatureGroup {
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700510 FeatureGroup() : openGLESVersion(-1) {}
511
Adam Lesinski2c72b682014-06-24 09:56:01 -0700512 /**
513 * Human readable label
514 */
515 String8 label;
516
517 /**
518 * Explicit features defined in the group
519 */
Adam Lesinski694d0a72016-04-06 16:12:04 -0700520 KeyedVector<String8, Feature> features;
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700521
522 /**
523 * OpenGL ES version required
524 */
525 int openGLESVersion;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700526};
527
Adam Lesinskica955a42016-08-01 16:44:29 -0700528static bool hasFeature(const char* name, const FeatureGroup& grp,
529 const KeyedVector<String8, ImpliedFeature>& implied) {
530 String8 name8(name);
531 ssize_t idx = grp.features.indexOfKey(name8);
532 if (idx < 0) {
533 idx = implied.indexOfKey(name8);
534 }
535 return idx >= 0;
536}
537
Adam Lesinski2c72b682014-06-24 09:56:01 -0700538static void addImpliedFeature(KeyedVector<String8, ImpliedFeature>* impliedFeatures,
Adam Lesinski43158772015-11-11 15:13:55 -0800539 const char* name, const String8& reason, bool sdk23) {
Adam Lesinski2c72b682014-06-24 09:56:01 -0700540 String8 name8(name);
541 ssize_t idx = impliedFeatures->indexOfKey(name8);
542 if (idx < 0) {
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800543 idx = impliedFeatures->add(name8, ImpliedFeature(name8, sdk23));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700544 }
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800545
546 ImpliedFeature* feature = &impliedFeatures->editValueAt(idx);
547
548 // A non-sdk 23 implied feature takes precedence.
549 if (feature->impliedBySdk23 && !sdk23) {
550 feature->impliedBySdk23 = false;
551 }
Adam Lesinski43158772015-11-11 15:13:55 -0800552 feature->reasons.add(reason);
Adam Lesinski2c72b682014-06-24 09:56:01 -0700553}
554
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800555static void printFeatureGroupImpl(const FeatureGroup& grp,
556 const KeyedVector<String8, ImpliedFeature>* impliedFeatures) {
Adam Lesinski2c72b682014-06-24 09:56:01 -0700557 printf("feature-group: label='%s'\n", grp.label.string());
558
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700559 if (grp.openGLESVersion > 0) {
560 printf(" uses-gl-es: '0x%x'\n", grp.openGLESVersion);
561 }
562
Adam Lesinski2c72b682014-06-24 09:56:01 -0700563 const size_t numFeatures = grp.features.size();
564 for (size_t i = 0; i < numFeatures; i++) {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700565 const Feature& feature = grp.features[i];
566 const bool required = feature.required;
567 const int32_t version = feature.version;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700568
569 const String8& featureName = grp.features.keyAt(i);
Adam Lesinski694d0a72016-04-06 16:12:04 -0700570 printf(" uses-feature%s: name='%s'", (required ? "" : "-not-required"),
Adam Lesinski2c72b682014-06-24 09:56:01 -0700571 ResTable::normalizeForOutput(featureName.string()).string());
Adam Lesinski694d0a72016-04-06 16:12:04 -0700572
573 if (version > 0) {
574 printf(" version='%d'", version);
575 }
576 printf("\n");
Adam Lesinski2c72b682014-06-24 09:56:01 -0700577 }
578
579 const size_t numImpliedFeatures =
580 (impliedFeatures != NULL) ? impliedFeatures->size() : 0;
581 for (size_t i = 0; i < numImpliedFeatures; i++) {
582 const ImpliedFeature& impliedFeature = impliedFeatures->valueAt(i);
583 if (grp.features.indexOfKey(impliedFeature.name) >= 0) {
584 // The feature is explicitly set, no need to use implied
585 // definition.
586 continue;
587 }
588
589 String8 printableFeatureName(ResTable::normalizeForOutput(
590 impliedFeature.name.string()));
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800591 const char* sdk23Suffix = impliedFeature.impliedBySdk23 ? "-sdk-23" : "";
592
593 printf(" uses-feature%s: name='%s'\n", sdk23Suffix, printableFeatureName.string());
594 printf(" uses-implied-feature%s: name='%s' reason='", sdk23Suffix,
595 printableFeatureName.string());
Adam Lesinski2c72b682014-06-24 09:56:01 -0700596 const size_t numReasons = impliedFeature.reasons.size();
597 for (size_t j = 0; j < numReasons; j++) {
598 printf("%s", impliedFeature.reasons[j].string());
599 if (j + 2 < numReasons) {
600 printf(", ");
601 } else if (j + 1 < numReasons) {
602 printf(", and ");
603 }
604 }
605 printf("'\n");
606 }
607}
608
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800609static void printFeatureGroup(const FeatureGroup& grp) {
610 printFeatureGroupImpl(grp, NULL);
611}
612
613static void printDefaultFeatureGroup(const FeatureGroup& grp,
614 const KeyedVector<String8, ImpliedFeature>& impliedFeatures) {
615 printFeatureGroupImpl(grp, &impliedFeatures);
616}
617
Adam Lesinski2c72b682014-06-24 09:56:01 -0700618static void addParentFeatures(FeatureGroup* grp, const String8& name) {
619 if (name == "android.hardware.camera.autofocus" ||
620 name == "android.hardware.camera.flash") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700621 grp->features.add(String8("android.hardware.camera"), Feature(true));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700622 } else if (name == "android.hardware.location.gps" ||
623 name == "android.hardware.location.network") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700624 grp->features.add(String8("android.hardware.location"), Feature(true));
Adam Lesinskica955a42016-08-01 16:44:29 -0700625 } else if (name == "android.hardware.faketouch.multitouch") {
626 grp->features.add(String8("android.hardware.faketouch"), Feature(true));
627 } else if (name == "android.hardware.faketouch.multitouch.distinct" ||
628 name == "android.hardware.faketouch.multitouch.jazzhands") {
629 grp->features.add(String8("android.hardware.faketouch.multitouch"), Feature(true));
630 grp->features.add(String8("android.hardware.faketouch"), Feature(true));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700631 } else if (name == "android.hardware.touchscreen.multitouch") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700632 grp->features.add(String8("android.hardware.touchscreen"), Feature(true));
Adam Lesinskica955a42016-08-01 16:44:29 -0700633 } else if (name == "android.hardware.touchscreen.multitouch.distinct" ||
634 name == "android.hardware.touchscreen.multitouch.jazzhands") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700635 grp->features.add(String8("android.hardware.touchscreen.multitouch"), Feature(true));
636 grp->features.add(String8("android.hardware.touchscreen"), Feature(true));
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700637 } else if (name == "android.hardware.opengles.aep") {
638 const int openGLESVersion31 = 0x00030001;
639 if (openGLESVersion31 > grp->openGLESVersion) {
640 grp->openGLESVersion = openGLESVersion31;
641 }
Adam Lesinski2c72b682014-06-24 09:56:01 -0700642 }
643}
644
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800645static void addImpliedFeaturesForPermission(const int targetSdk, const String8& name,
646 KeyedVector<String8, ImpliedFeature>* impliedFeatures,
647 bool impliedBySdk23Permission) {
648 if (name == "android.permission.CAMERA") {
649 addImpliedFeature(impliedFeatures, "android.hardware.camera",
Adam Lesinski43158772015-11-11 15:13:55 -0800650 String8::format("requested %s permission", name.string()),
651 impliedBySdk23Permission);
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800652 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
Adam Lesinski43158772015-11-11 15:13:55 -0800653 if (targetSdk < SDK_LOLLIPOP) {
654 addImpliedFeature(impliedFeatures, "android.hardware.location.gps",
655 String8::format("requested %s permission", name.string()),
656 impliedBySdk23Permission);
657 addImpliedFeature(impliedFeatures, "android.hardware.location.gps",
658 String8::format("targetSdkVersion < %d", SDK_LOLLIPOP),
659 impliedBySdk23Permission);
660 }
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800661 addImpliedFeature(impliedFeatures, "android.hardware.location",
Adam Lesinski43158772015-11-11 15:13:55 -0800662 String8::format("requested %s permission", name.string()),
663 impliedBySdk23Permission);
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800664 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
Adam Lesinski43158772015-11-11 15:13:55 -0800665 if (targetSdk < SDK_LOLLIPOP) {
666 addImpliedFeature(impliedFeatures, "android.hardware.location.network",
667 String8::format("requested %s permission", name.string()),
668 impliedBySdk23Permission);
669 addImpliedFeature(impliedFeatures, "android.hardware.location.network",
670 String8::format("targetSdkVersion < %d", SDK_LOLLIPOP),
671 impliedBySdk23Permission);
672 }
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800673 addImpliedFeature(impliedFeatures, "android.hardware.location",
Adam Lesinski43158772015-11-11 15:13:55 -0800674 String8::format("requested %s permission", name.string()),
675 impliedBySdk23Permission);
676 } else if (name == "android.permission.ACCESS_MOCK_LOCATION" ||
677 name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800678 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
679 addImpliedFeature(impliedFeatures, "android.hardware.location",
Adam Lesinski43158772015-11-11 15:13:55 -0800680 String8::format("requested %s permission", name.string()),
681 impliedBySdk23Permission);
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800682 } else if (name == "android.permission.BLUETOOTH" ||
683 name == "android.permission.BLUETOOTH_ADMIN") {
Adam Lesinski43158772015-11-11 15:13:55 -0800684 if (targetSdk > SDK_DONUT) {
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800685 addImpliedFeature(impliedFeatures, "android.hardware.bluetooth",
Adam Lesinski43158772015-11-11 15:13:55 -0800686 String8::format("requested %s permission", name.string()),
687 impliedBySdk23Permission);
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800688 addImpliedFeature(impliedFeatures, "android.hardware.bluetooth",
Adam Lesinski43158772015-11-11 15:13:55 -0800689 String8::format("targetSdkVersion > %d", SDK_DONUT),
690 impliedBySdk23Permission);
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800691 }
692 } else if (name == "android.permission.RECORD_AUDIO") {
693 addImpliedFeature(impliedFeatures, "android.hardware.microphone",
Adam Lesinski43158772015-11-11 15:13:55 -0800694 String8::format("requested %s permission", name.string()),
695 impliedBySdk23Permission);
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800696 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
697 name == "android.permission.CHANGE_WIFI_STATE" ||
698 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
699 addImpliedFeature(impliedFeatures, "android.hardware.wifi",
Adam Lesinski43158772015-11-11 15:13:55 -0800700 String8::format("requested %s permission", name.string()),
701 impliedBySdk23Permission);
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800702 } else if (name == "android.permission.CALL_PHONE" ||
703 name == "android.permission.CALL_PRIVILEGED" ||
704 name == "android.permission.MODIFY_PHONE_STATE" ||
705 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
706 name == "android.permission.READ_SMS" ||
707 name == "android.permission.RECEIVE_SMS" ||
708 name == "android.permission.RECEIVE_MMS" ||
709 name == "android.permission.RECEIVE_WAP_PUSH" ||
710 name == "android.permission.SEND_SMS" ||
711 name == "android.permission.WRITE_APN_SETTINGS" ||
712 name == "android.permission.WRITE_SMS") {
713 addImpliedFeature(impliedFeatures, "android.hardware.telephony",
Adam Lesinski43158772015-11-11 15:13:55 -0800714 String8("requested a telephony permission"),
715 impliedBySdk23Permission);
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800716 }
717}
718
Adam Lesinski282e1812014-01-23 18:17:42 -0800719/*
720 * Handle the "dump" command, to extract select data from an archive.
721 */
722extern char CONSOLE_DATA[2925]; // see EOF
723int doDump(Bundle* bundle)
724{
725 status_t result = UNKNOWN_ERROR;
Adam Lesinski282e1812014-01-23 18:17:42 -0800726
727 if (bundle->getFileSpecCount() < 1) {
728 fprintf(stderr, "ERROR: no dump option specified\n");
729 return 1;
730 }
731
732 if (bundle->getFileSpecCount() < 2) {
733 fprintf(stderr, "ERROR: no dump file specified\n");
734 return 1;
735 }
736
737 const char* option = bundle->getFileSpecEntry(0);
738 const char* filename = bundle->getFileSpecEntry(1);
739
740 AssetManager assets;
Narayan Kamathf85e41f2014-01-24 14:14:30 +0000741 int32_t assetsCookie;
Adam Lesinski282e1812014-01-23 18:17:42 -0800742 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
743 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
744 return 1;
745 }
746
Adam Lesinski57fe4832017-05-10 15:42:22 -0700747 // Now add any dependencies passed in.
748 for (size_t i = 0; i < bundle->getPackageIncludes().size(); i++) {
749 const String8& assetPath = bundle->getPackageIncludes()[i];
750 if (!assets.addAssetPath(assetPath, NULL)) {
751 fprintf(stderr, "ERROR: included asset path %s could not be loaded\n", assetPath.string());
752 return 1;
753 }
754 }
755
Adam Lesinski282e1812014-01-23 18:17:42 -0800756 // Make a dummy config for retrieving resources... we need to supply
757 // non-default values for some configs so that we can retrieve resources
758 // in the app that don't have a default. The most important of these is
759 // the API version because key resources like icons will have an implicit
760 // version if they are using newer config types like density.
761 ResTable_config config;
Narayan Kamath91447d82014-01-21 15:32:36 +0000762 memset(&config, 0, sizeof(ResTable_config));
Adam Lesinski282e1812014-01-23 18:17:42 -0800763 config.language[0] = 'e';
764 config.language[1] = 'n';
765 config.country[0] = 'U';
766 config.country[1] = 'S';
767 config.orientation = ResTable_config::ORIENTATION_PORT;
768 config.density = ResTable_config::DENSITY_MEDIUM;
769 config.sdkVersion = 10000; // Very high.
770 config.screenWidthDp = 320;
771 config.screenHeightDp = 480;
772 config.smallestScreenWidthDp = 320;
Adam Lesinskic2dea8d2014-08-04 16:40:41 -0700773 config.screenLayout |= ResTable_config::SCREENSIZE_NORMAL;
Adam Lesinski282e1812014-01-23 18:17:42 -0800774 assets.setConfiguration(config);
775
776 const ResTable& res = assets.getResources(false);
Dan Albert68001652014-09-09 09:51:01 -0700777 if (res.getError() != NO_ERROR) {
Adam Lesinski25e9d552014-05-19 15:01:43 -0700778 fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
Adam Lesinski63e646e2014-07-30 11:40:39 -0700779 return 1;
Adam Lesinski282e1812014-01-23 18:17:42 -0800780 }
781
Adam Lesinski694d0a72016-04-06 16:12:04 -0700782 // Source for AndroidManifest.xml
Adam Lesinski10de3af12016-07-13 10:14:03 -0700783 const String8 manifestFile("AndroidManifest.xml");
Adam Lesinski694d0a72016-04-06 16:12:04 -0700784
Adam Lesinski2cb761e2014-08-15 13:59:02 -0700785 // The dynamicRefTable can be null if there are no resources for this asset cookie.
786 // This fine.
Adam Lesinski63e646e2014-07-30 11:40:39 -0700787 const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700788
789 Asset* asset = NULL;
790
Adam Lesinski282e1812014-01-23 18:17:42 -0800791 if (strcmp("resources", option) == 0) {
Elliott Hughesba3fe562015-08-12 14:49:53 -0700792#ifndef __ANDROID__
Adam Lesinski282e1812014-01-23 18:17:42 -0800793 res.print(bundle->getValues());
794#endif
795
796 } else if (strcmp("strings", option) == 0) {
797 const ResStringPool* pool = res.getTableStringBlock(0);
798 printStringPool(pool);
799
800 } else if (strcmp("xmltree", option) == 0) {
801 if (bundle->getFileSpecCount() < 3) {
802 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
803 goto bail;
804 }
805
806 for (int i=2; i<bundle->getFileSpecCount(); i++) {
807 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700808 ResXMLTree tree(dynamicRefTable);
809 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800810 if (asset == NULL) {
Adam Lesinskifcb5f7b2016-11-02 13:17:10 -0700811 fprintf(stderr, "ERROR: dump failed because resource %s not found\n", resname);
Adam Lesinski282e1812014-01-23 18:17:42 -0800812 goto bail;
813 }
814
815 if (tree.setTo(asset->getBuffer(true),
816 asset->getLength()) != NO_ERROR) {
817 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
818 goto bail;
819 }
820 tree.restart();
821 printXMLBlock(&tree);
822 tree.uninit();
823 delete asset;
824 asset = NULL;
825 }
826
827 } else if (strcmp("xmlstrings", option) == 0) {
828 if (bundle->getFileSpecCount() < 3) {
829 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
830 goto bail;
831 }
832
833 for (int i=2; i<bundle->getFileSpecCount(); i++) {
834 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700835 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800836 if (asset == NULL) {
837 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
838 goto bail;
839 }
840
Adam Lesinski63e646e2014-07-30 11:40:39 -0700841 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800842 if (tree.setTo(asset->getBuffer(true),
843 asset->getLength()) != NO_ERROR) {
844 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
845 goto bail;
846 }
847 printStringPool(&tree.getStrings());
848 delete asset;
849 asset = NULL;
850 }
851
852 } else {
Adam Lesinski63e646e2014-07-30 11:40:39 -0700853 asset = assets.openNonAsset(assetsCookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800854 if (asset == NULL) {
855 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
856 goto bail;
857 }
858
Adam Lesinski63e646e2014-07-30 11:40:39 -0700859 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800860 if (tree.setTo(asset->getBuffer(true),
861 asset->getLength()) != NO_ERROR) {
862 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
863 goto bail;
864 }
865 tree.restart();
866
867 if (strcmp("permissions", option) == 0) {
868 size_t len;
869 ResXMLTree::event_code_t code;
870 int depth = 0;
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800871 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
872 code != ResXMLTree::BAD_DOCUMENT) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800873 if (code == ResXMLTree::END_TAG) {
874 depth--;
875 continue;
876 }
877 if (code != ResXMLTree::START_TAG) {
878 continue;
879 }
880 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700881 const char16_t* ctag16 = tree.getElementName(&len);
882 if (ctag16 == NULL) {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700883 SourcePos(manifestFile, tree.getLineNumber()).error(
884 "ERROR: failed to get XML element name (bad string pool)");
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700885 goto bail;
886 }
887 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800888 //printf("Depth %d tag %s\n", depth, tag.string());
889 if (depth == 1) {
890 if (tag != "manifest") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700891 SourcePos(manifestFile, tree.getLineNumber()).error(
892 "ERROR: manifest does not start with <manifest> tag");
Adam Lesinski282e1812014-01-23 18:17:42 -0800893 goto bail;
894 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700895 String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -0700896 printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800897 } else if (depth == 2) {
898 if (tag == "permission") {
899 String8 error;
900 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
901 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700902 SourcePos(manifestFile, tree.getLineNumber()).error(
903 "ERROR getting 'android:name': %s", error.string());
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800904 goto bail;
905 }
906
907 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700908 SourcePos(manifestFile, tree.getLineNumber()).error(
909 "ERROR: missing 'android:name' for permission");
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800910 goto bail;
911 }
912 printf("permission: %s\n",
913 ResTable::normalizeForOutput(name.string()).string());
914 } else if (tag == "uses-permission") {
915 String8 error;
916 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
917 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700918 SourcePos(manifestFile, tree.getLineNumber()).error(
919 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800920 goto bail;
921 }
922
923 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700924 SourcePos(manifestFile, tree.getLineNumber()).error(
925 "ERROR: missing 'android:name' for uses-permission");
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800926 goto bail;
927 }
928 printUsesPermission(name,
929 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
930 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
931 } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
932 String8 error;
933 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
934 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700935 SourcePos(manifestFile, tree.getLineNumber()).error(
936 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800937 goto bail;
938 }
939
940 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700941 SourcePos(manifestFile, tree.getLineNumber()).error(
942 "ERROR: missing 'android:name' for uses-permission-sdk-23");
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800943 goto bail;
944 }
945 printUsesPermissionSdk23(
946 name,
947 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
Adam Lesinski282e1812014-01-23 18:17:42 -0800948 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800949 }
950 }
951 } else if (strcmp("badging", option) == 0) {
952 Vector<String8> locales;
953 res.getLocales(&locales);
954
955 Vector<ResTable_config> configs;
956 res.getConfigurations(&configs);
957 SortedVector<int> densities;
958 const size_t NC = configs.size();
959 for (size_t i=0; i<NC; i++) {
960 int dens = configs[i].density;
961 if (dens == 0) {
962 dens = 160;
963 }
964 densities.add(dens);
965 }
966
967 size_t len;
968 ResXMLTree::event_code_t code;
969 int depth = 0;
970 String8 error;
971 bool withinActivity = false;
972 bool isMainActivity = false;
973 bool isLauncherActivity = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800974 bool isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800975 bool isSearchable = false;
976 bool withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700977 bool withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700978 bool withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800979 bool withinReceiver = false;
980 bool withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700981 bool withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800982 bool withinIntentFilter = false;
983 bool hasMainActivity = false;
984 bool hasOtherActivities = false;
985 bool hasOtherReceivers = false;
986 bool hasOtherServices = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700987 bool hasIntentFilter = false;
988
Adam Lesinski282e1812014-01-23 18:17:42 -0800989 bool hasWallpaperService = false;
990 bool hasImeService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700991 bool hasAccessibilityService = false;
992 bool hasPrintService = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800993 bool hasWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700994 bool hasDeviceAdminReceiver = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700995 bool hasPaymentService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700996 bool hasDocumentsProvider = false;
997 bool hasCameraActivity = false;
998 bool hasCameraSecureActivity = false;
999 bool hasLauncher = false;
1000 bool hasNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001001 bool hasDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001002
Adam Lesinski282e1812014-01-23 18:17:42 -08001003 bool actMainActivity = false;
1004 bool actWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001005 bool actDeviceAdminEnabled = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001006 bool actImeService = false;
1007 bool actWallpaperService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001008 bool actAccessibilityService = false;
1009 bool actPrintService = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001010 bool actHostApduService = false;
1011 bool actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001012 bool actDocumentsProvider = false;
1013 bool actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001014 bool actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001015 bool actCamera = false;
1016 bool actCameraSecure = false;
1017 bool catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001018 bool hasMetaHostPaymentCategory = false;
1019 bool hasMetaOffHostPaymentCategory = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001020
1021 // These permissions are required by services implementing services
1022 // the system binds to (IME, Accessibility, PrintServices, etc.)
1023 bool hasBindDeviceAdminPermission = false;
1024 bool hasBindInputMethodPermission = false;
1025 bool hasBindAccessibilityServicePermission = false;
1026 bool hasBindPrintServicePermission = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001027 bool hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001028 bool hasRequiredSafAttributes = false;
1029 bool hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001030 bool hasBindDreamServicePermission = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001031
1032 // These two implement the implicit permissions that are granted
1033 // to pre-1.6 applications.
1034 bool hasWriteExternalStoragePermission = false;
Adam Lesinski2386df22016-12-28 15:08:58 -05001035 int32_t writeExternalStoragePermissionMaxSdkVersion = -1;
Adam Lesinski282e1812014-01-23 18:17:42 -08001036 bool hasReadPhoneStatePermission = false;
1037
1038 // If an app requests write storage, they will also get read storage.
1039 bool hasReadExternalStoragePermission = false;
1040
1041 // Implement transition to read and write call log.
1042 bool hasReadContactsPermission = false;
1043 bool hasWriteContactsPermission = false;
1044 bool hasReadCallLogPermission = false;
1045 bool hasWriteCallLogPermission = false;
1046
Adam Lesinskie47fd122014-08-15 22:25:36 -07001047 // If an app declares itself as multiArch, we report the
1048 // native libraries differently.
1049 bool hasMultiArch = false;
1050
Adam Lesinski282e1812014-01-23 18:17:42 -08001051 // This next group of variables is used to implement a group of
1052 // backward-compatibility heuristics necessitated by the addition of
1053 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
1054 // heuristic is "if an app requests a permission but doesn't explicitly
1055 // request the corresponding <uses-feature>, presume it's there anyway".
Adam Lesinski2c72b682014-06-24 09:56:01 -07001056
Adam Lesinski282e1812014-01-23 18:17:42 -08001057 // 2.2 also added some other features that apps can request, but that
1058 // have no corresponding permission, so we cannot implement any
1059 // back-compatibility heuristic for them. The below are thus unnecessary
1060 // (but are retained here for documentary purposes.)
1061 //bool specCompassFeature = false;
1062 //bool specAccelerometerFeature = false;
1063 //bool specProximityFeature = false;
1064 //bool specAmbientLightFeature = false;
1065 //bool specLiveWallpaperFeature = false;
1066
1067 int targetSdk = 0;
1068 int smallScreen = 1;
1069 int normalScreen = 1;
1070 int largeScreen = 1;
1071 int xlargeScreen = 1;
1072 int anyDensity = 1;
1073 int requiresSmallestWidthDp = 0;
1074 int compatibleWidthLimitDp = 0;
1075 int largestWidthLimitDp = 0;
1076 String8 pkg;
1077 String8 activityName;
1078 String8 activityLabel;
1079 String8 activityIcon;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001080 String8 activityBanner;
Adam Lesinski282e1812014-01-23 18:17:42 -08001081 String8 receiverName;
1082 String8 serviceName;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001083 Vector<String8> supportedInput;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001084
1085 FeatureGroup commonFeatures;
1086 Vector<FeatureGroup> featureGroups;
1087 KeyedVector<String8, ImpliedFeature> impliedFeatures;
1088
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001089 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
1090 code != ResXMLTree::BAD_DOCUMENT) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001091 if (code == ResXMLTree::END_TAG) {
1092 depth--;
1093 if (depth < 2) {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001094 if (withinSupportsInput && !supportedInput.isEmpty()) {
1095 printf("supports-input: '");
1096 const size_t N = supportedInput.size();
1097 for (size_t i=0; i<N; i++) {
Maurice Chu2675f762013-10-22 17:33:11 -07001098 printf("%s", ResTable::normalizeForOutput(
1099 supportedInput[i].string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001100 if (i != N - 1) {
1101 printf("' '");
1102 } else {
1103 printf("'\n");
1104 }
1105 }
1106 supportedInput.clear();
1107 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001108 withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001109 withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001110 withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001111 } else if (depth < 3) {
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001112 if (withinActivity && isMainActivity) {
Maurice Chu2675f762013-10-22 17:33:11 -07001113 String8 aName(getComponentName(pkg, activityName));
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001114 if (isLauncherActivity) {
1115 printf("launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001116 if (aName.length() > 0) {
1117 printf(" name='%s' ",
1118 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001119 }
1120 printf(" label='%s' icon='%s'\n",
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001121 ResTable::normalizeForOutput(activityLabel.string())
1122 .string(),
1123 ResTable::normalizeForOutput(activityIcon.string())
1124 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001125 }
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001126 if (isLeanbackLauncherActivity) {
1127 printf("leanback-launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001128 if (aName.length() > 0) {
1129 printf(" name='%s' ",
1130 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001131 }
1132 printf(" label='%s' icon='%s' banner='%s'\n",
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001133 ResTable::normalizeForOutput(activityLabel.string())
1134 .string(),
1135 ResTable::normalizeForOutput(activityIcon.string())
1136 .string(),
1137 ResTable::normalizeForOutput(activityBanner.string())
1138 .string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001139 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001140 }
1141 if (!hasIntentFilter) {
1142 hasOtherActivities |= withinActivity;
1143 hasOtherReceivers |= withinReceiver;
1144 hasOtherServices |= withinService;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001145 } else {
1146 if (withinService) {
1147 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
1148 hasBindNfcServicePermission);
1149 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
1150 hasBindNfcServicePermission);
1151 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001152 }
1153 withinActivity = false;
1154 withinService = false;
1155 withinReceiver = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001156 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001157 hasIntentFilter = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001158 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001159 } else if (depth < 4) {
1160 if (withinIntentFilter) {
1161 if (withinActivity) {
1162 hasMainActivity |= actMainActivity;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001163 hasLauncher |= catLauncher;
1164 hasCameraActivity |= actCamera;
1165 hasCameraSecureActivity |= actCameraSecure;
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001166 hasOtherActivities |=
1167 !actMainActivity && !actCamera && !actCameraSecure;
Adam Lesinski282e1812014-01-23 18:17:42 -08001168 } else if (withinReceiver) {
1169 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001170 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
1171 hasBindDeviceAdminPermission);
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001172 hasOtherReceivers |=
1173 (!actWidgetReceivers && !actDeviceAdminEnabled);
Adam Lesinski282e1812014-01-23 18:17:42 -08001174 } else if (withinService) {
1175 hasImeService |= actImeService;
1176 hasWallpaperService |= actWallpaperService;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001177 hasAccessibilityService |= (actAccessibilityService &&
1178 hasBindAccessibilityServicePermission);
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001179 hasPrintService |=
1180 (actPrintService && hasBindPrintServicePermission);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001181 hasNotificationListenerService |= actNotificationListenerService &&
1182 hasBindNotificationListenerServicePermission;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001183 hasDreamService |= actDreamService && hasBindDreamServicePermission;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001184 hasOtherServices |= (!actImeService && !actWallpaperService &&
Adam Lesinski94fc9122013-09-30 17:16:09 -07001185 !actAccessibilityService && !actPrintService &&
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001186 !actHostApduService && !actOffHostApduService &&
1187 !actNotificationListenerService);
1188 } else if (withinProvider) {
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001189 hasDocumentsProvider |=
1190 actDocumentsProvider && hasRequiredSafAttributes;
Adam Lesinski282e1812014-01-23 18:17:42 -08001191 }
1192 }
1193 withinIntentFilter = false;
1194 }
1195 continue;
1196 }
1197 if (code != ResXMLTree::START_TAG) {
1198 continue;
1199 }
1200 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001201
1202 const char16_t* ctag16 = tree.getElementName(&len);
1203 if (ctag16 == NULL) {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001204 SourcePos(manifestFile, tree.getLineNumber()).error(
1205 "ERROR: failed to get XML element name (bad string pool)");
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001206 goto bail;
1207 }
1208 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -08001209 //printf("Depth %d, %s\n", depth, tag.string());
1210 if (depth == 1) {
1211 if (tag != "manifest") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001212 SourcePos(manifestFile, tree.getLineNumber()).error(
1213 "ERROR: manifest does not start with <manifest> tag");
Adam Lesinski282e1812014-01-23 18:17:42 -08001214 goto bail;
1215 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001216 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -07001217 printf("package: name='%s' ",
1218 ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001219 int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR,
1220 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001221 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001222 SourcePos(manifestFile, tree.getLineNumber()).error(
1223 "ERROR getting 'android:versionCode' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001224 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001225 goto bail;
1226 }
1227 if (versionCode > 0) {
1228 printf("versionCode='%d' ", versionCode);
1229 } else {
1230 printf("versionCode='' ");
1231 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001232 String8 versionName = AaptXml::getResolvedAttribute(res, tree,
1233 VERSION_NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001234 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001235 SourcePos(manifestFile, tree.getLineNumber()).error(
1236 "ERROR getting 'android:versionName' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001237 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001238 goto bail;
1239 }
Adam Lesinski25d35a92014-08-11 09:41:56 -07001240 printf("versionName='%s'",
Maurice Chu2675f762013-10-22 17:33:11 -07001241 ResTable::normalizeForOutput(versionName.string()).string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001242
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001243 String8 splitName = AaptXml::getAttribute(tree, NULL, "split");
Adam Lesinski25d35a92014-08-11 09:41:56 -07001244 if (!splitName.isEmpty()) {
1245 printf(" split='%s'", ResTable::normalizeForOutput(
1246 splitName.string()).string());
1247 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001248
Adam Lesinski5283fab2014-08-29 11:23:55 -07001249 String8 platformVersionName = AaptXml::getAttribute(tree, NULL,
1250 "platformBuildVersionName");
1251 printf(" platformBuildVersionName='%s'", platformVersionName.string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001252 printf("\n");
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001253
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001254 int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
1255 INSTALL_LOCATION_ATTR, &error);
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001256 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001257 SourcePos(manifestFile, tree.getLineNumber()).error(
1258 "ERROR getting 'android:installLocation' attribute: %s",
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001259 error.string());
1260 goto bail;
1261 }
1262
1263 if (installLocation >= 0) {
1264 printf("install-location:'");
1265 switch (installLocation) {
1266 case 0:
1267 printf("auto");
1268 break;
1269 case 1:
1270 printf("internalOnly");
1271 break;
1272 case 2:
1273 printf("preferExternal");
1274 break;
1275 default:
1276 fprintf(stderr, "Invalid installLocation %d\n", installLocation);
1277 goto bail;
1278 }
1279 printf("'\n");
1280 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001281 } else if (depth == 2) {
1282 withinApplication = false;
1283 if (tag == "application") {
1284 withinApplication = true;
1285
1286 String8 label;
1287 const size_t NL = locales.size();
1288 for (size_t i=0; i<NL; i++) {
1289 const char* localeStr = locales[i].string();
Adam Lesinskia77685f2016-10-03 16:26:28 -07001290 assets.setConfiguration(config, localeStr != NULL ? localeStr : "");
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001291 String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1292 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001293 if (llabel != "") {
1294 if (localeStr == NULL || strlen(localeStr) == 0) {
1295 label = llabel;
Maurice Chu2675f762013-10-22 17:33:11 -07001296 printf("application-label:'%s'\n",
1297 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001298 } else {
1299 if (label == "") {
1300 label = llabel;
1301 }
1302 printf("application-label-%s:'%s'\n", localeStr,
Maurice Chu2675f762013-10-22 17:33:11 -07001303 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001304 }
1305 }
1306 }
1307
1308 ResTable_config tmpConfig = config;
1309 const size_t ND = densities.size();
1310 for (size_t i=0; i<ND; i++) {
1311 tmpConfig.density = densities[i];
1312 assets.setConfiguration(tmpConfig);
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001313 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1314 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001315 if (icon != "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001316 printf("application-icon-%d:'%s'\n", densities[i],
1317 ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001318 }
1319 }
1320 assets.setConfiguration(config);
1321
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001322 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001323 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001324 SourcePos(manifestFile, tree.getLineNumber()).error(
1325 "ERROR getting 'android:icon' attribute: %s", error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001326 goto bail;
1327 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001328 int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0,
1329 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001330 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001331 SourcePos(manifestFile, tree.getLineNumber()).error(
1332 "ERROR getting 'android:testOnly' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001333 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001334 goto bail;
1335 }
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001336
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001337 String8 banner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1338 &error);
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001339 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001340 SourcePos(manifestFile, tree.getLineNumber()).error(
1341 "ERROR getting 'android:banner' attribute: %s", error.string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001342 goto bail;
1343 }
Maurice Chu2675f762013-10-22 17:33:11 -07001344 printf("application: label='%s' ",
1345 ResTable::normalizeForOutput(label.string()).string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001346 printf("icon='%s'", ResTable::normalizeForOutput(icon.string()).string());
1347 if (banner != "") {
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001348 printf(" banner='%s'",
1349 ResTable::normalizeForOutput(banner.string()).string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001350 }
1351 printf("\n");
Adam Lesinski282e1812014-01-23 18:17:42 -08001352 if (testOnly != 0) {
1353 printf("testOnly='%d'\n", testOnly);
1354 }
1355
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001356 int32_t isGame = AaptXml::getResolvedIntegerAttribute(res, tree,
1357 ISGAME_ATTR, 0, &error);
1358 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001359 SourcePos(manifestFile, tree.getLineNumber()).error(
1360 "ERROR getting 'android:isGame' attribute: %s", error.string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001361 goto bail;
1362 }
1363 if (isGame != 0) {
1364 printf("application-isGame\n");
1365 }
1366
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001367 int32_t debuggable = AaptXml::getResolvedIntegerAttribute(res, tree,
1368 DEBUGGABLE_ATTR, 0, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001369 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001370 SourcePos(manifestFile, tree.getLineNumber()).error(
1371 "ERROR getting 'android:debuggable' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001372 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001373 goto bail;
1374 }
1375 if (debuggable != 0) {
1376 printf("application-debuggable\n");
1377 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07001378
1379 // We must search by name because the multiArch flag hasn't been API
1380 // frozen yet.
1381 int32_t multiArchIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
1382 "multiArch");
1383 if (multiArchIndex >= 0) {
1384 Res_value value;
1385 if (tree.getAttributeValue(multiArchIndex, &value) != NO_ERROR) {
1386 if (value.dataType >= Res_value::TYPE_FIRST_INT &&
1387 value.dataType <= Res_value::TYPE_LAST_INT) {
1388 hasMultiArch = value.data;
1389 }
1390 }
1391 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001392 } else if (tag == "uses-sdk") {
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001393 int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR,
1394 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001395 if (error != "") {
1396 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001397 String8 name = AaptXml::getResolvedAttribute(res, tree,
1398 MIN_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001399 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001400 SourcePos(manifestFile, tree.getLineNumber()).error(
1401 "ERROR getting 'android:minSdkVersion' attribute: %s",
Adam Lesinski282e1812014-01-23 18:17:42 -08001402 error.string());
1403 goto bail;
1404 }
1405 if (name == "Donut") targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001406 printf("sdkVersion:'%s'\n",
1407 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001408 } else if (code != -1) {
1409 targetSdk = code;
1410 printf("sdkVersion:'%d'\n", code);
1411 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001412 code = AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -08001413 if (code != -1) {
1414 printf("maxSdkVersion:'%d'\n", code);
1415 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001416 code = AaptXml::getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001417 if (error != "") {
1418 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001419 String8 name = AaptXml::getResolvedAttribute(res, tree,
1420 TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001421 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001422 SourcePos(manifestFile, tree.getLineNumber()).error(
1423 "ERROR getting 'android:targetSdkVersion' attribute: %s",
Adam Lesinski282e1812014-01-23 18:17:42 -08001424 error.string());
1425 goto bail;
1426 }
1427 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001428 printf("targetSdkVersion:'%s'\n",
1429 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001430 } else if (code != -1) {
1431 if (targetSdk < code) {
1432 targetSdk = code;
1433 }
1434 printf("targetSdkVersion:'%d'\n", code);
1435 }
1436 } else if (tag == "uses-configuration") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001437 int32_t reqTouchScreen = AaptXml::getIntegerAttribute(tree,
1438 REQ_TOUCH_SCREEN_ATTR, 0);
1439 int32_t reqKeyboardType = AaptXml::getIntegerAttribute(tree,
1440 REQ_KEYBOARD_TYPE_ATTR, 0);
1441 int32_t reqHardKeyboard = AaptXml::getIntegerAttribute(tree,
1442 REQ_HARD_KEYBOARD_ATTR, 0);
1443 int32_t reqNavigation = AaptXml::getIntegerAttribute(tree,
1444 REQ_NAVIGATION_ATTR, 0);
1445 int32_t reqFiveWayNav = AaptXml::getIntegerAttribute(tree,
1446 REQ_FIVE_WAY_NAV_ATTR, 0);
Adam Lesinski282e1812014-01-23 18:17:42 -08001447 printf("uses-configuration:");
1448 if (reqTouchScreen != 0) {
1449 printf(" reqTouchScreen='%d'", reqTouchScreen);
1450 }
1451 if (reqKeyboardType != 0) {
1452 printf(" reqKeyboardType='%d'", reqKeyboardType);
1453 }
1454 if (reqHardKeyboard != 0) {
1455 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1456 }
1457 if (reqNavigation != 0) {
1458 printf(" reqNavigation='%d'", reqNavigation);
1459 }
1460 if (reqFiveWayNav != 0) {
1461 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1462 }
1463 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001464 } else if (tag == "supports-input") {
1465 withinSupportsInput = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001466 } else if (tag == "supports-screens") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001467 smallScreen = AaptXml::getIntegerAttribute(tree,
1468 SMALL_SCREEN_ATTR, 1);
1469 normalScreen = AaptXml::getIntegerAttribute(tree,
1470 NORMAL_SCREEN_ATTR, 1);
1471 largeScreen = AaptXml::getIntegerAttribute(tree,
1472 LARGE_SCREEN_ATTR, 1);
1473 xlargeScreen = AaptXml::getIntegerAttribute(tree,
1474 XLARGE_SCREEN_ATTR, 1);
1475 anyDensity = AaptXml::getIntegerAttribute(tree,
1476 ANY_DENSITY_ATTR, 1);
1477 requiresSmallestWidthDp = AaptXml::getIntegerAttribute(tree,
1478 REQUIRES_SMALLEST_WIDTH_DP_ATTR, 0);
1479 compatibleWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1480 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, 0);
1481 largestWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1482 LARGEST_WIDTH_LIMIT_DP_ATTR, 0);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001483 } else if (tag == "feature-group") {
1484 withinFeatureGroup = true;
1485 FeatureGroup group;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001486 group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001487 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001488 SourcePos(manifestFile, tree.getLineNumber()).error(
1489 "ERROR getting 'android:label' attribute: %s", error.string());
Adam Lesinski2c72b682014-06-24 09:56:01 -07001490 goto bail;
1491 }
1492 featureGroups.add(group);
1493
Adam Lesinski282e1812014-01-23 18:17:42 -08001494 } else if (tag == "uses-feature") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001495 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001496 if (name != "" && error == "") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001497 const char* androidSchema =
1498 "http://schemas.android.com/apk/res/android";
Adam Lesinski282e1812014-01-23 18:17:42 -08001499
Adam Lesinski694d0a72016-04-06 16:12:04 -07001500 int32_t req = AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1,
1501 &error);
1502 if (error != "") {
1503 SourcePos(manifestFile, tree.getLineNumber()).error(
1504 "failed to read attribute 'android:required': %s",
1505 error.string());
1506 goto bail;
1507 }
1508
1509 int32_t version = AaptXml::getIntegerAttribute(tree, androidSchema,
1510 "version", 0, &error);
1511 if (error != "") {
1512 SourcePos(manifestFile, tree.getLineNumber()).error(
1513 "failed to read attribute 'android:version': %s",
1514 error.string());
1515 goto bail;
1516 }
1517
1518 commonFeatures.features.add(name, Feature(req != 0, version));
Adam Lesinski2c72b682014-06-24 09:56:01 -07001519 if (req) {
1520 addParentFeatures(&commonFeatures, name);
Adam Lesinski282e1812014-01-23 18:17:42 -08001521 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001522 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001523 int vers = AaptXml::getIntegerAttribute(tree,
Adam Lesinski282e1812014-01-23 18:17:42 -08001524 GL_ES_VERSION_ATTR, &error);
1525 if (error == "") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001526 if (vers > commonFeatures.openGLESVersion) {
1527 commonFeatures.openGLESVersion = vers;
1528 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001529 }
1530 }
1531 } else if (tag == "uses-permission") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001532 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001533 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001534 SourcePos(manifestFile, tree.getLineNumber()).error(
1535 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001536 goto bail;
1537 }
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001538
1539 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001540 SourcePos(manifestFile, tree.getLineNumber()).error(
1541 "ERROR: missing 'android:name' for uses-permission");
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001542 goto bail;
1543 }
1544
1545 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, false);
1546
Adam Lesinski2386df22016-12-28 15:08:58 -05001547 const int32_t maxSdkVersion =
1548 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, -1);
Dianne Hackborncd154e92017-02-28 17:37:35 -08001549 const String8 requiredFeature = AaptXml::getAttribute(tree,
1550 REQUIRED_FEATURE_ATTR, &error);
1551 const String8 requiredNotFeature = AaptXml::getAttribute(tree,
1552 REQUIRED_NOT_FEATURE_ATTR, &error);
Adam Lesinski2386df22016-12-28 15:08:58 -05001553
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001554 if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1555 hasWriteExternalStoragePermission = true;
Adam Lesinski2386df22016-12-28 15:08:58 -05001556 writeExternalStoragePermissionMaxSdkVersion = maxSdkVersion;
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001557 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1558 hasReadExternalStoragePermission = true;
1559 } else if (name == "android.permission.READ_PHONE_STATE") {
1560 hasReadPhoneStatePermission = true;
1561 } else if (name == "android.permission.READ_CONTACTS") {
1562 hasReadContactsPermission = true;
1563 } else if (name == "android.permission.WRITE_CONTACTS") {
1564 hasWriteContactsPermission = true;
1565 } else if (name == "android.permission.READ_CALL_LOG") {
1566 hasReadCallLogPermission = true;
1567 } else if (name == "android.permission.WRITE_CALL_LOG") {
1568 hasWriteCallLogPermission = true;
1569 }
1570
1571 printUsesPermission(name,
1572 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
Dianne Hackborncd154e92017-02-28 17:37:35 -08001573 maxSdkVersion, requiredFeature, requiredNotFeature);
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001574
1575 } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
1576 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1577 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001578 SourcePos(manifestFile, tree.getLineNumber()).error(
1579 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001580 goto bail;
1581 }
1582
1583 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001584 SourcePos(manifestFile, tree.getLineNumber()).error(
1585 "ERROR: missing 'android:name' for uses-permission-sdk-23");
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001586 goto bail;
1587 }
1588
1589 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, true);
1590
1591 printUsesPermissionSdk23(
1592 name, AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
1593
Adam Lesinski282e1812014-01-23 18:17:42 -08001594 } else if (tag == "uses-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001595 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001596 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001597 printf("uses-package:'%s'\n",
1598 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001599 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001600 SourcePos(manifestFile, tree.getLineNumber()).error(
1601 "ERROR getting 'android:name' attribute: %s", error.string());
1602 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001603 }
1604 } else if (tag == "original-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001605 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001606 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001607 printf("original-package:'%s'\n",
1608 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001609 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001610 SourcePos(manifestFile, tree.getLineNumber()).error(
1611 "ERROR getting 'android:name' attribute: %s", error.string());
1612 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001613 }
1614 } else if (tag == "supports-gl-texture") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001615 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001616 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001617 printf("supports-gl-texture:'%s'\n",
1618 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001619 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001620 SourcePos(manifestFile, tree.getLineNumber()).error(
1621 "ERROR getting 'android:name' attribute: %s", error.string());
1622 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001623 }
1624 } else if (tag == "compatible-screens") {
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001625 printCompatibleScreens(tree, &error);
1626 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001627 SourcePos(manifestFile, tree.getLineNumber()).error(
1628 "ERROR getting compatible screens: %s", error.string());
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001629 goto bail;
1630 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001631 depth--;
1632 } else if (tag == "package-verifier") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001633 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001634 if (name != "" && error == "") {
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001635 String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR,
1636 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001637 if (publicKey != "" && error == "") {
1638 printf("package-verifier: name='%s' publicKey='%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001639 ResTable::normalizeForOutput(name.string()).string(),
1640 ResTable::normalizeForOutput(publicKey.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001641 }
1642 }
1643 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001644 } else if (depth == 3) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001645 withinActivity = false;
1646 withinReceiver = false;
1647 withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001648 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001649 hasIntentFilter = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001650 hasMetaHostPaymentCategory = false;
1651 hasMetaOffHostPaymentCategory = false;
1652 hasBindDeviceAdminPermission = false;
1653 hasBindInputMethodPermission = false;
1654 hasBindAccessibilityServicePermission = false;
1655 hasBindPrintServicePermission = false;
1656 hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001657 hasRequiredSafAttributes = false;
1658 hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001659 hasBindDreamServicePermission = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001660 if (withinApplication) {
1661 if(tag == "activity") {
1662 withinActivity = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001663 activityName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001664 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001665 SourcePos(manifestFile, tree.getLineNumber()).error(
1666 "ERROR getting 'android:name' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001667 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001668 goto bail;
1669 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001670
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001671 activityLabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1672 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001673 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001674 SourcePos(manifestFile, tree.getLineNumber()).error(
1675 "ERROR getting 'android:label' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001676 error.string());
1677 goto bail;
1678 }
1679
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001680 activityIcon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1681 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001682 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001683 SourcePos(manifestFile, tree.getLineNumber()).error(
1684 "ERROR getting 'android:icon' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001685 error.string());
1686 goto bail;
1687 }
1688
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001689 activityBanner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1690 &error);
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001691 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001692 SourcePos(manifestFile, tree.getLineNumber()).error(
1693 "ERROR getting 'android:banner' attribute: %s",
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001694 error.string());
1695 goto bail;
1696 }
1697
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001698 int32_t orien = AaptXml::getResolvedIntegerAttribute(res, tree,
Michael Wrightec4fdec2013-09-06 16:50:52 -07001699 SCREEN_ORIENTATION_ATTR, &error);
1700 if (error == "") {
1701 if (orien == 0 || orien == 6 || orien == 8) {
1702 // Requests landscape, sensorLandscape, or reverseLandscape.
Adam Lesinski43158772015-11-11 15:13:55 -08001703 addImpliedFeature(
1704 &impliedFeatures, "android.hardware.screen.landscape",
1705 String8("one or more activities have specified a "
1706 "landscape orientation"),
1707 false);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001708 } else if (orien == 1 || orien == 7 || orien == 9) {
1709 // Requests portrait, sensorPortrait, or reversePortrait.
Adam Lesinski43158772015-11-11 15:13:55 -08001710 addImpliedFeature(
1711 &impliedFeatures, "android.hardware.screen.portrait",
1712 String8("one or more activities have specified a "
1713 "portrait orientation"),
1714 false);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001715 }
1716 }
1717 } else if (tag == "uses-library") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001718 String8 libraryName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001719 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001720 SourcePos(manifestFile, tree.getLineNumber()).error(
Michael Wrightec4fdec2013-09-06 16:50:52 -07001721 "ERROR getting 'android:name' attribute for uses-library"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001722 " %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001723 goto bail;
1724 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001725 int req = AaptXml::getIntegerAttribute(tree,
1726 REQUIRED_ATTR, 1);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001727 printf("uses-library%s:'%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001728 req ? "" : "-not-required", ResTable::normalizeForOutput(
1729 libraryName.string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001730 } else if (tag == "receiver") {
1731 withinReceiver = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001732 receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001733
1734 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001735 SourcePos(manifestFile, tree.getLineNumber()).error(
Michael Wrightec4fdec2013-09-06 16:50:52 -07001736 "ERROR getting 'android:name' attribute for receiver:"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001737 " %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001738 goto bail;
1739 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001740
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001741 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1742 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001743 if (error == "") {
1744 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1745 hasBindDeviceAdminPermission = true;
1746 }
1747 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001748 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001749 "ERROR getting 'android:permission' attribute for"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001750 " receiver '%s': %s",
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001751 receiverName.string(), error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001752 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001753 } else if (tag == "service") {
1754 withinService = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001755 serviceName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001756
1757 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001758 SourcePos(manifestFile, tree.getLineNumber()).error(
1759 "ERROR getting 'android:name' attribute for "
1760 "service:%s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001761 goto bail;
1762 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001763
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001764 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1765 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001766 if (error == "") {
1767 if (permission == "android.permission.BIND_INPUT_METHOD") {
1768 hasBindInputMethodPermission = true;
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001769 } else if (permission ==
1770 "android.permission.BIND_ACCESSIBILITY_SERVICE") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001771 hasBindAccessibilityServicePermission = true;
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001772 } else if (permission ==
1773 "android.permission.BIND_PRINT_SERVICE") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001774 hasBindPrintServicePermission = true;
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001775 } else if (permission ==
1776 "android.permission.BIND_NFC_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07001777 hasBindNfcServicePermission = true;
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001778 } else if (permission ==
1779 "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001780 hasBindNotificationListenerServicePermission = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001781 } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
1782 hasBindDreamServicePermission = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001783 }
1784 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001785 SourcePos(manifestFile, tree.getLineNumber()).error(
1786 "ERROR getting 'android:permission' attribute for "
1787 "service '%s': %s", serviceName.string(), error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001788 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001789 } else if (tag == "provider") {
1790 withinProvider = true;
1791
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001792 bool exported = AaptXml::getResolvedIntegerAttribute(res, tree,
1793 EXPORTED_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001794 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001795 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001796 "ERROR getting 'android:exported' attribute for provider:"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001797 " %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001798 goto bail;
1799 }
1800
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001801 bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute(
1802 res, tree, GRANT_URI_PERMISSIONS_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001803 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001804 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001805 "ERROR getting 'android:grantUriPermissions' attribute for "
Adam Lesinski10de3af12016-07-13 10:14:03 -07001806 "provider: %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001807 goto bail;
1808 }
1809
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001810 String8 permission = AaptXml::getResolvedAttribute(res, tree,
1811 PERMISSION_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001812 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001813 SourcePos(manifestFile, tree.getLineNumber()).error(
1814 "ERROR getting 'android:permission' attribute for "
1815 "provider: %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001816 goto bail;
1817 }
1818
1819 hasRequiredSafAttributes |= exported && grantUriPermissions &&
1820 permission == "android.permission.MANAGE_DOCUMENTS";
1821
Michael Wrightec4fdec2013-09-06 16:50:52 -07001822 } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001823 String8 metaDataName = AaptXml::getResolvedAttribute(res, tree,
1824 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(
1827 "ERROR getting 'android:name' attribute for "
1828 "meta-data: %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001829 goto bail;
1830 }
Maurice Chu2675f762013-10-22 17:33:11 -07001831 printf("meta-data: name='%s' ",
1832 ResTable::normalizeForOutput(metaDataName.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001833 printResolvedResourceAttribute(res, tree, VALUE_ATTR, String8("value"),
Maurice Chu76327312013-10-16 18:28:46 -07001834 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001835 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001836 // Try looking for a RESOURCE_ATTR
1837 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001838 printResolvedResourceAttribute(res, tree, RESOURCE_ATTR,
Maurice Chu76327312013-10-16 18:28:46 -07001839 String8("resource"), &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001840 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001841 SourcePos(manifestFile, tree.getLineNumber()).error(
1842 "ERROR getting 'android:value' or "
Maurice Chu76327312013-10-16 18:28:46 -07001843 "'android:resource' attribute for "
Adam Lesinski10de3af12016-07-13 10:14:03 -07001844 "meta-data: %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001845 goto bail;
1846 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001847 }
Maurice Chu76327312013-10-16 18:28:46 -07001848 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001849 } else if (withinSupportsInput && tag == "input-type") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001850 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001851 if (name != "" && error == "") {
1852 supportedInput.add(name);
1853 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001854 SourcePos(manifestFile, tree.getLineNumber()).error(
1855 "ERROR getting 'android:name' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001856 error.string());
1857 goto bail;
1858 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001859 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001860 } else if (withinFeatureGroup && tag == "uses-feature") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001861 const String8 androidSchema("http://schemas.android.com/apk/res/android");
Adam Lesinski2c72b682014-06-24 09:56:01 -07001862 FeatureGroup& top = featureGroups.editTop();
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001863
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001864 String8 name = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001865 if (name != "" && error == "") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001866 Feature feature(true);
1867
1868 int32_t featureVers = AaptXml::getIntegerAttribute(
1869 tree, androidSchema.string(), "version", 0, &error);
1870 if (error == "") {
1871 feature.version = featureVers;
1872 } else {
1873 SourcePos(manifestFile, tree.getLineNumber()).error(
1874 "failed to read attribute 'android:version': %s",
1875 error.string());
1876 goto bail;
1877 }
1878
1879 top.features.add(name, feature);
Adam Lesinskid3edfde2014-08-08 17:32:44 -07001880 addParentFeatures(&top, name);
Adam Lesinski694d0a72016-04-06 16:12:04 -07001881
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001882 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001883 int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR,
1884 &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001885 if (error == "") {
1886 if (vers > top.openGLESVersion) {
1887 top.openGLESVersion = vers;
1888 }
1889 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001890 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001891 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001892 } else if (depth == 4) {
1893 if (tag == "intent-filter") {
1894 hasIntentFilter = true;
1895 withinIntentFilter = true;
1896 actMainActivity = false;
1897 actWidgetReceivers = false;
1898 actImeService = false;
1899 actWallpaperService = false;
1900 actAccessibilityService = false;
1901 actPrintService = false;
1902 actDeviceAdminEnabled = false;
1903 actHostApduService = false;
1904 actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001905 actDocumentsProvider = false;
1906 actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001907 actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001908 actCamera = false;
1909 actCameraSecure = false;
1910 catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001911 } else if (withinService && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001912 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001913 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001914 SourcePos(manifestFile, tree.getLineNumber()).error(
1915 "ERROR getting 'android:name' attribute for "
1916 "meta-data tag in service '%s': %s", serviceName.string(),
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001917 error.string());
Adam Lesinski94fc9122013-09-30 17:16:09 -07001918 goto bail;
1919 }
1920
1921 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1922 name == "android.nfc.cardemulation.off_host_apdu_service") {
1923 bool offHost = true;
1924 if (name == "android.nfc.cardemulation.host_apdu_service") {
1925 offHost = false;
1926 }
1927
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001928 String8 xmlPath = AaptXml::getResolvedAttribute(res, tree,
1929 RESOURCE_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001930 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001931 SourcePos(manifestFile, tree.getLineNumber()).error(
1932 "ERROR getting 'android:resource' attribute for "
1933 "meta-data tag in service '%s': %s",
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001934 serviceName.string(), error.string());
Adam Lesinski94fc9122013-09-30 17:16:09 -07001935 goto bail;
1936 }
1937
1938 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1939 offHost, &error);
1940 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001941 SourcePos(manifestFile, tree.getLineNumber()).error(
1942 "ERROR getting AID category for service '%s'",
Adam Lesinski94fc9122013-09-30 17:16:09 -07001943 serviceName.string());
1944 goto bail;
1945 }
1946
1947 const size_t catLen = categories.size();
1948 for (size_t i = 0; i < catLen; i++) {
1949 bool paymentCategory = (categories[i] == "payment");
1950 if (offHost) {
1951 hasMetaOffHostPaymentCategory |= paymentCategory;
1952 } else {
1953 hasMetaHostPaymentCategory |= paymentCategory;
1954 }
1955 }
1956 }
1957 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001958 } else if ((depth == 5) && withinIntentFilter) {
1959 String8 action;
1960 if (tag == "action") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001961 action = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001962 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001963 SourcePos(manifestFile, tree.getLineNumber()).error(
1964 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001965 goto bail;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001966 }
1967
Adam Lesinskia5018c92013-09-30 16:23:15 -07001968 if (withinActivity) {
1969 if (action == "android.intent.action.MAIN") {
1970 isMainActivity = true;
1971 actMainActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001972 } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
1973 action == "android.media.action.VIDEO_CAMERA") {
1974 actCamera = true;
1975 } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
1976 actCameraSecure = true;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001977 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001978 } else if (withinReceiver) {
1979 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1980 actWidgetReceivers = true;
1981 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1982 actDeviceAdminEnabled = true;
1983 }
1984 } else if (withinService) {
1985 if (action == "android.view.InputMethod") {
1986 actImeService = true;
1987 } else if (action == "android.service.wallpaper.WallpaperService") {
1988 actWallpaperService = true;
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001989 } else if (action ==
1990 "android.accessibilityservice.AccessibilityService") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001991 actAccessibilityService = true;
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001992 } else if (action =="android.printservice.PrintService") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001993 actPrintService = true;
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001994 } else if (action ==
1995 "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07001996 actHostApduService = true;
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001997 } else if (action ==
1998 "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07001999 actOffHostApduService = true;
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08002000 } else if (action ==
2001 "android.service.notification.NotificationListenerService") {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002002 actNotificationListenerService = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04002003 } else if (action == "android.service.dreams.DreamService") {
2004 actDreamService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002005 }
2006 } else if (withinProvider) {
2007 if (action == "android.content.action.DOCUMENTS_PROVIDER") {
2008 actDocumentsProvider = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07002009 }
2010 }
2011 if (action == "android.intent.action.SEARCH") {
2012 isSearchable = true;
2013 }
2014 }
2015
2016 if (tag == "category") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07002017 String8 category = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07002018 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07002019 SourcePos(manifestFile, tree.getLineNumber()).error(
2020 "ERROR getting 'name' attribute: %s", error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07002021 goto bail;
2022 }
2023 if (withinActivity) {
2024 if (category == "android.intent.category.LAUNCHER") {
2025 isLauncherActivity = true;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08002026 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
2027 isLeanbackLauncherActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002028 } else if (category == "android.intent.category.HOME") {
2029 catLauncher = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08002030 }
2031 }
2032 }
2033 }
2034 }
2035
2036 // Pre-1.6 implicitly granted permission compatibility logic
2037 if (targetSdk < 4) {
2038 if (!hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002039 printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
2040 printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
2041 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002042 hasWriteExternalStoragePermission = true;
2043 }
2044 if (!hasReadPhoneStatePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002045 printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
2046 printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
2047 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002048 }
2049 }
2050
2051 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
2052 // force them to always take READ_EXTERNAL_STORAGE as well. We always
2053 // do this (regardless of target API version) because we can't have
2054 // an app with write permission but not read permission.
2055 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
Adam Lesinski2386df22016-12-28 15:08:58 -05002056 printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
2057 false /* optional */, writeExternalStoragePermissionMaxSdkVersion);
Adam Lesinski58f1f362013-11-12 12:59:08 -08002058 printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
Adam Lesinski2386df22016-12-28 15:08:58 -05002059 String8("requested WRITE_EXTERNAL_STORAGE"),
2060 writeExternalStoragePermissionMaxSdkVersion);
Adam Lesinski282e1812014-01-23 18:17:42 -08002061 }
2062
2063 // Pre-JellyBean call log permission compatibility.
2064 if (targetSdk < 16) {
2065 if (!hasReadCallLogPermission && hasReadContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002066 printUsesPermission(String8("android.permission.READ_CALL_LOG"));
2067 printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
2068 String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002069 }
2070 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002071 printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
2072 printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
2073 String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002074 }
2075 }
2076
Adam Lesinskica955a42016-08-01 16:44:29 -07002077 // If the app hasn't declared the touchscreen as a feature requirement (either
2078 // directly or implied, required or not), then the faketouch feature is implied.
2079 if (!hasFeature("android.hardware.touchscreen", commonFeatures, impliedFeatures)) {
2080 addImpliedFeature(&impliedFeatures, "android.hardware.faketouch",
Adam Lesinski43158772015-11-11 15:13:55 -08002081 String8("default feature for all apps"), false);
Adam Lesinskica955a42016-08-01 16:44:29 -07002082 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07002083
2084 const size_t numFeatureGroups = featureGroups.size();
2085 if (numFeatureGroups == 0) {
2086 // If no <feature-group> tags were defined, apply auto-implied features.
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08002087 printDefaultFeatureGroup(commonFeatures, impliedFeatures);
Adam Lesinski2c72b682014-06-24 09:56:01 -07002088
2089 } else {
2090 // <feature-group> tags are defined, so we ignore implied features and
2091 for (size_t i = 0; i < numFeatureGroups; i++) {
2092 FeatureGroup& grp = featureGroups.editItemAt(i);
2093
Adam Lesinskid7a94da2014-07-25 14:38:54 -07002094 if (commonFeatures.openGLESVersion > grp.openGLESVersion) {
2095 grp.openGLESVersion = commonFeatures.openGLESVersion;
2096 }
2097
Adam Lesinski2c72b682014-06-24 09:56:01 -07002098 // Merge the features defined in the top level (not inside a <feature-group>)
2099 // with this feature group.
2100 const size_t numCommonFeatures = commonFeatures.features.size();
2101 for (size_t j = 0; j < numCommonFeatures; j++) {
2102 if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07002103 grp.features.add(commonFeatures.features.keyAt(j),
2104 commonFeatures.features[j]);
Adam Lesinski2c72b682014-06-24 09:56:01 -07002105 }
2106 }
2107
Adam Lesinski73a05112014-12-08 12:53:17 -08002108 if (!grp.features.isEmpty()) {
Adam Lesinski2c72b682014-06-24 09:56:01 -07002109 printFeatureGroup(grp);
Adam Lesinski282e1812014-01-23 18:17:42 -08002110 }
2111 }
2112 }
2113
Adam Lesinski282e1812014-01-23 18:17:42 -08002114
Adam Lesinski282e1812014-01-23 18:17:42 -08002115 if (hasWidgetReceivers) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002116 printComponentPresence("app-widget");
Adam Lesinski282e1812014-01-23 18:17:42 -08002117 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002118 if (hasDeviceAdminReceiver) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002119 printComponentPresence("device-admin");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002120 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002121 if (hasImeService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002122 printComponentPresence("ime");
Adam Lesinski282e1812014-01-23 18:17:42 -08002123 }
2124 if (hasWallpaperService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002125 printComponentPresence("wallpaper");
Adam Lesinski282e1812014-01-23 18:17:42 -08002126 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002127 if (hasAccessibilityService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002128 printComponentPresence("accessibility");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002129 }
2130 if (hasPrintService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002131 printComponentPresence("print-service");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002132 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07002133 if (hasPaymentService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002134 printComponentPresence("payment");
2135 }
2136 if (isSearchable) {
2137 printComponentPresence("search");
2138 }
2139 if (hasDocumentsProvider) {
2140 printComponentPresence("document-provider");
2141 }
2142 if (hasLauncher) {
2143 printComponentPresence("launcher");
2144 }
2145 if (hasNotificationListenerService) {
2146 printComponentPresence("notification-listener");
2147 }
John Spurlockeb8d1be2014-06-25 17:46:15 -04002148 if (hasDreamService) {
2149 printComponentPresence("dream");
2150 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002151 if (hasCameraActivity) {
2152 printComponentPresence("camera");
2153 }
2154 if (hasCameraSecureActivity) {
2155 printComponentPresence("camera-secure");
2156 }
2157
2158 if (hasMainActivity) {
2159 printf("main\n");
Adam Lesinski94fc9122013-09-30 17:16:09 -07002160 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002161 if (hasOtherActivities) {
2162 printf("other-activities\n");
2163 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002164 if (hasOtherReceivers) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002165 printf("other-receivers\n");
2166 }
2167 if (hasOtherServices) {
2168 printf("other-services\n");
2169 }
2170
2171 // For modern apps, if screen size buckets haven't been specified
2172 // but the new width ranges have, then infer the buckets from them.
2173 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
2174 && requiresSmallestWidthDp > 0) {
2175 int compatWidth = compatibleWidthLimitDp;
2176 if (compatWidth <= 0) {
2177 compatWidth = requiresSmallestWidthDp;
2178 }
2179 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
2180 smallScreen = -1;
2181 } else {
2182 smallScreen = 0;
2183 }
2184 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
2185 normalScreen = -1;
2186 } else {
2187 normalScreen = 0;
2188 }
2189 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
2190 largeScreen = -1;
2191 } else {
2192 largeScreen = 0;
2193 }
2194 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
2195 xlargeScreen = -1;
2196 } else {
2197 xlargeScreen = 0;
2198 }
2199 }
2200
2201 // Determine default values for any unspecified screen sizes,
2202 // based on the target SDK of the package. As of 4 (donut)
2203 // the screen size support was introduced, so all default to
2204 // enabled.
2205 if (smallScreen > 0) {
2206 smallScreen = targetSdk >= 4 ? -1 : 0;
2207 }
2208 if (normalScreen > 0) {
2209 normalScreen = -1;
2210 }
2211 if (largeScreen > 0) {
2212 largeScreen = targetSdk >= 4 ? -1 : 0;
2213 }
2214 if (xlargeScreen > 0) {
2215 // Introduced in Gingerbread.
2216 xlargeScreen = targetSdk >= 9 ? -1 : 0;
2217 }
2218 if (anyDensity > 0) {
2219 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
2220 || compatibleWidthLimitDp > 0) ? -1 : 0;
2221 }
2222 printf("supports-screens:");
2223 if (smallScreen != 0) {
2224 printf(" 'small'");
2225 }
2226 if (normalScreen != 0) {
2227 printf(" 'normal'");
2228 }
2229 if (largeScreen != 0) {
2230 printf(" 'large'");
2231 }
2232 if (xlargeScreen != 0) {
2233 printf(" 'xlarge'");
2234 }
2235 printf("\n");
2236 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
2237 if (requiresSmallestWidthDp > 0) {
2238 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
2239 }
2240 if (compatibleWidthLimitDp > 0) {
2241 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
2242 }
2243 if (largestWidthLimitDp > 0) {
2244 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
2245 }
2246
2247 printf("locales:");
2248 const size_t NL = locales.size();
2249 for (size_t i=0; i<NL; i++) {
2250 const char* localeStr = locales[i].string();
2251 if (localeStr == NULL || strlen(localeStr) == 0) {
2252 localeStr = "--_--";
2253 }
2254 printf(" '%s'", localeStr);
2255 }
2256 printf("\n");
2257
2258 printf("densities:");
2259 const size_t ND = densities.size();
2260 for (size_t i=0; i<ND; i++) {
2261 printf(" '%d'", densities[i]);
2262 }
2263 printf("\n");
2264
2265 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
2266 if (dir != NULL) {
2267 if (dir->getFileCount() > 0) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002268 SortedVector<String8> architectures;
Adam Lesinski282e1812014-01-23 18:17:42 -08002269 for (size_t i=0; i<dir->getFileCount(); i++) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002270 architectures.add(ResTable::normalizeForOutput(
2271 dir->getFileName(i).string()));
Adam Lesinski282e1812014-01-23 18:17:42 -08002272 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07002273
2274 bool outputAltNativeCode = false;
2275 // A multiArch package is one that contains 64-bit and
2276 // 32-bit versions of native code and expects 3rd-party
2277 // apps to load these native code libraries. Since most
2278 // 64-bit systems also support 32-bit apps, the apps
2279 // loading this multiArch package's code may be either
2280 // 32-bit or 64-bit.
2281 if (hasMultiArch) {
2282 // If this is a multiArch package, report the 64-bit
2283 // version only. Then as a separate entry, report the
2284 // rest.
2285 //
2286 // If we report the 32-bit architecture, this APK will
2287 // be installed on a 32-bit device, causing a large waste
2288 // of bandwidth and disk space. This assumes that
2289 // the developer of the multiArch package has also
2290 // made a version that is 32-bit only.
2291 String8 intel64("x86_64");
2292 String8 arm64("arm64-v8a");
2293 ssize_t index = architectures.indexOf(intel64);
2294 if (index < 0) {
2295 index = architectures.indexOf(arm64);
2296 }
2297
2298 if (index >= 0) {
2299 printf("native-code: '%s'\n", architectures[index].string());
2300 architectures.removeAt(index);
2301 outputAltNativeCode = true;
2302 }
2303 }
2304
2305 const size_t archCount = architectures.size();
2306 if (archCount > 0) {
2307 if (outputAltNativeCode) {
2308 printf("alt-");
2309 }
2310 printf("native-code:");
2311 for (size_t i = 0; i < archCount; i++) {
2312 printf(" '%s'", architectures[i].string());
2313 }
2314 printf("\n");
2315 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002316 }
2317 delete dir;
2318 }
2319 } else if (strcmp("badger", option) == 0) {
2320 printf("%s", CONSOLE_DATA);
2321 } else if (strcmp("configurations", option) == 0) {
2322 Vector<ResTable_config> configs;
2323 res.getConfigurations(&configs);
2324 const size_t N = configs.size();
2325 for (size_t i=0; i<N; i++) {
2326 printf("%s\n", configs[i].toString().string());
2327 }
2328 } else {
2329 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2330 goto bail;
2331 }
2332 }
2333
2334 result = NO_ERROR;
2335
2336bail:
Adam Lesinski10de3af12016-07-13 10:14:03 -07002337 if (SourcePos::hasErrors()) {
2338 SourcePos::printErrors(stderr);
2339 }
2340
Adam Lesinski282e1812014-01-23 18:17:42 -08002341 if (asset) {
2342 delete asset;
2343 }
2344 return (result != NO_ERROR);
2345}
2346
2347
2348/*
2349 * Handle the "add" command, which wants to add files to a new or
2350 * pre-existing archive.
2351 */
2352int doAdd(Bundle* bundle)
2353{
2354 ZipFile* zip = NULL;
2355 status_t result = UNKNOWN_ERROR;
2356 const char* zipFileName;
2357
2358 if (bundle->getUpdate()) {
2359 /* avoid confusion */
2360 fprintf(stderr, "ERROR: can't use '-u' with add\n");
2361 goto bail;
2362 }
2363
2364 if (bundle->getFileSpecCount() < 1) {
2365 fprintf(stderr, "ERROR: must specify zip file name\n");
2366 goto bail;
2367 }
2368 zipFileName = bundle->getFileSpecEntry(0);
2369
2370 if (bundle->getFileSpecCount() < 2) {
2371 fprintf(stderr, "NOTE: nothing to do\n");
2372 goto bail;
2373 }
2374
2375 zip = openReadWrite(zipFileName, true);
2376 if (zip == NULL) {
2377 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2378 goto bail;
2379 }
2380
2381 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2382 const char* fileName = bundle->getFileSpecEntry(i);
2383
2384 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
2385 printf(" '%s'... (from gzip)\n", fileName);
2386 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
2387 } else {
2388 if (bundle->getJunkPath()) {
2389 String8 storageName = String8(fileName).getPathLeaf();
Maurice Chu2675f762013-10-22 17:33:11 -07002390 printf(" '%s' as '%s'...\n", fileName,
2391 ResTable::normalizeForOutput(storageName.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08002392 result = zip->add(fileName, storageName.string(),
2393 bundle->getCompressionMethod(), NULL);
2394 } else {
2395 printf(" '%s'...\n", fileName);
2396 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2397 }
2398 }
2399 if (result != NO_ERROR) {
2400 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2401 if (result == NAME_NOT_FOUND) {
2402 fprintf(stderr, ": file not found\n");
2403 } else if (result == ALREADY_EXISTS) {
2404 fprintf(stderr, ": already exists in archive\n");
2405 } else {
2406 fprintf(stderr, "\n");
2407 }
2408 goto bail;
2409 }
2410 }
2411
2412 result = NO_ERROR;
2413
2414bail:
2415 delete zip;
2416 return (result != NO_ERROR);
2417}
2418
2419
2420/*
2421 * Delete files from an existing archive.
2422 */
2423int doRemove(Bundle* bundle)
2424{
2425 ZipFile* zip = NULL;
2426 status_t result = UNKNOWN_ERROR;
2427 const char* zipFileName;
2428
2429 if (bundle->getFileSpecCount() < 1) {
2430 fprintf(stderr, "ERROR: must specify zip file name\n");
2431 goto bail;
2432 }
2433 zipFileName = bundle->getFileSpecEntry(0);
2434
2435 if (bundle->getFileSpecCount() < 2) {
2436 fprintf(stderr, "NOTE: nothing to do\n");
2437 goto bail;
2438 }
2439
2440 zip = openReadWrite(zipFileName, false);
2441 if (zip == NULL) {
2442 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2443 zipFileName);
2444 goto bail;
2445 }
2446
2447 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2448 const char* fileName = bundle->getFileSpecEntry(i);
2449 ZipEntry* entry;
2450
2451 entry = zip->getEntryByName(fileName);
2452 if (entry == NULL) {
2453 printf(" '%s' NOT FOUND\n", fileName);
2454 continue;
2455 }
2456
2457 result = zip->remove(entry);
2458
2459 if (result != NO_ERROR) {
2460 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2461 bundle->getFileSpecEntry(i), zipFileName);
2462 goto bail;
2463 }
2464 }
2465
2466 /* update the archive */
2467 zip->flush();
2468
2469bail:
2470 delete zip;
2471 return (result != NO_ERROR);
2472}
2473
Adam Lesinski3921e872014-05-13 10:56:25 -07002474static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002475 const size_t numDirs = dir->getDirs().size();
2476 for (size_t i = 0; i < numDirs; i++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002477 bool ignore = ignoreConfig;
2478 const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2479 const char* dirStr = subDir->getLeaf().string();
2480 if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2481 ignore = true;
2482 }
2483 status_t err = addResourcesToBuilder(subDir, builder, ignore);
Adam Lesinskifab50872014-04-16 14:40:42 -07002484 if (err != NO_ERROR) {
2485 return err;
2486 }
2487 }
2488
2489 const size_t numFiles = dir->getFiles().size();
2490 for (size_t i = 0; i < numFiles; i++) {
2491 sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2492 const size_t numConfigs = gp->getFiles().size();
2493 for (size_t j = 0; j < numConfigs; j++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002494 status_t err = NO_ERROR;
Adam Lesinskic7614e52017-03-16 16:54:23 -07002495 if (ignoreConfig) {
Guang Zhu8c2df712017-03-21 03:53:43 +00002496 err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
Adam Lesinskic7614e52017-03-16 16:54:23 -07002497 } else {
Guang Zhu8c2df712017-03-21 03:53:43 +00002498 err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
Adam Lesinskic7614e52017-03-16 16:54:23 -07002499 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002500 if (err != NO_ERROR) {
2501 fprintf(stderr, "Failed to add %s (%s) to builder.\n",
Guang Zhu8c2df712017-03-21 03:53:43 +00002502 gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
Adam Lesinskifab50872014-04-16 14:40:42 -07002503 return err;
2504 }
2505 }
2506 }
2507 return NO_ERROR;
2508}
2509
2510static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2511 if (split->isBase()) {
2512 return original;
2513 }
2514
2515 String8 ext(original.getPathExtension());
2516 if (ext == String8(".apk")) {
2517 return String8::format("%s_%s%s",
2518 original.getBasePath().string(),
2519 split->getDirectorySafeName().string(),
2520 ext.string());
2521 }
2522
2523 return String8::format("%s_%s", original.string(),
2524 split->getDirectorySafeName().string());
2525}
Adam Lesinski282e1812014-01-23 18:17:42 -08002526
2527/*
2528 * Package up an asset directory and associated application files.
2529 */
2530int doPackage(Bundle* bundle)
2531{
2532 const char* outputAPKFile;
2533 int retVal = 1;
2534 status_t err;
2535 sp<AaptAssets> assets;
2536 int N;
2537 FILE* fp;
2538 String8 dependencyFile;
Adam Lesinskifab50872014-04-16 14:40:42 -07002539 sp<ApkBuilder> builder;
Adam Lesinski282e1812014-01-23 18:17:42 -08002540
Anton Krumina2ef5c02014-03-12 14:46:44 -07002541 // -c en_XA or/and ar_XB means do pseudolocalization
Adam Lesinskifab50872014-04-16 14:40:42 -07002542 sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2543 err = configFilter->parse(bundle->getConfigurations());
Adam Lesinski282e1812014-01-23 18:17:42 -08002544 if (err != NO_ERROR) {
2545 goto bail;
2546 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002547 if (configFilter->containsPseudo()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002548 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2549 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002550 if (configFilter->containsPseudoBidi()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002551 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
Adam Lesinski282e1812014-01-23 18:17:42 -08002552 }
2553
2554 N = bundle->getFileSpecCount();
2555 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
Adam Lesinski09384302014-01-22 16:07:42 -08002556 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002557 fprintf(stderr, "ERROR: no input files\n");
2558 goto bail;
2559 }
2560
2561 outputAPKFile = bundle->getOutputAPKFile();
2562
2563 // Make sure the filenames provided exist and are of the appropriate type.
2564 if (outputAPKFile) {
2565 FileType type;
2566 type = getFileType(outputAPKFile);
2567 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2568 fprintf(stderr,
2569 "ERROR: output file '%s' exists but is not regular file\n",
2570 outputAPKFile);
2571 goto bail;
2572 }
2573 }
2574
2575 // Load the assets.
2576 assets = new AaptAssets();
2577
2578 // Set up the resource gathering in assets if we're going to generate
2579 // dependency files. Every time we encounter a resource while slurping
2580 // the tree, we'll add it to these stores so we have full resource paths
2581 // to write to a dependency file.
2582 if (bundle->getGenDependencies()) {
2583 sp<FilePathStore> resPathStore = new FilePathStore;
2584 assets->setFullResPaths(resPathStore);
2585 sp<FilePathStore> assetPathStore = new FilePathStore;
2586 assets->setFullAssetPaths(assetPathStore);
2587 }
2588
2589 err = assets->slurpFromArgs(bundle);
2590 if (err < 0) {
2591 goto bail;
2592 }
2593
2594 if (bundle->getVerbose()) {
2595 assets->print(String8());
2596 }
2597
Adam Lesinskifab50872014-04-16 14:40:42 -07002598 // Create the ApkBuilder, which will collect the compiled files
2599 // to write to the final APK (or sets of APKs if we are building
2600 // a Split APK.
2601 builder = new ApkBuilder(configFilter);
2602
2603 // If we are generating a Split APK, find out which configurations to split on.
2604 if (bundle->getSplitConfigurations().size() > 0) {
2605 const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2606 const size_t numSplits = splitStrs.size();
2607 for (size_t i = 0; i < numSplits; i++) {
2608 std::set<ConfigDescription> configs;
2609 if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2610 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2611 goto bail;
2612 }
2613
2614 err = builder->createSplitForConfigs(configs);
2615 if (err != NO_ERROR) {
2616 goto bail;
2617 }
2618 }
2619 }
2620
Adam Lesinski282e1812014-01-23 18:17:42 -08002621 // If they asked for any fileAs that need to be compiled, do so.
2622 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002623 err = buildResources(bundle, assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002624 if (err != 0) {
2625 goto bail;
2626 }
2627 }
2628
2629 // At this point we've read everything and processed everything. From here
2630 // on out it's just writing output files.
2631 if (SourcePos::hasErrors()) {
2632 goto bail;
2633 }
2634
2635 // Update symbols with information about which ones are needed as Java symbols.
2636 assets->applyJavaSymbols();
2637 if (SourcePos::hasErrors()) {
2638 goto bail;
2639 }
2640
2641 // If we've been asked to generate a dependency file, do that here
2642 if (bundle->getGenDependencies()) {
2643 // If this is the packaging step, generate the dependency file next to
2644 // the output apk (e.g. bin/resources.ap_.d)
2645 if (outputAPKFile) {
2646 dependencyFile = String8(outputAPKFile);
2647 // Add the .d extension to the dependency file.
2648 dependencyFile.append(".d");
2649 } else {
2650 // Else if this is the R.java dependency generation step,
2651 // generate the dependency file in the R.java package subdirectory
2652 // e.g. gen/com/foo/app/R.java.d
2653 dependencyFile = String8(bundle->getRClassDir());
2654 dependencyFile.appendPath("R.java.d");
2655 }
2656 // Make sure we have a clean dependency file to start with
2657 fp = fopen(dependencyFile, "w");
2658 fclose(fp);
2659 }
2660
2661 // Write out R.java constants
2662 if (!assets->havePrivateSymbols()) {
2663 if (bundle->getCustomPackage() == NULL) {
2664 // Write the R.java file into the appropriate class directory
2665 // e.g. gen/com/foo/app/R.java
Adam Lesinski1e4663852014-08-15 14:47:28 -07002666 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002667 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002668 } else {
2669 const String8 customPkg(bundle->getCustomPackage());
Adam Lesinski1e4663852014-08-15 14:47:28 -07002670 err = writeResourceSymbols(bundle, assets, customPkg, true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002671 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002672 }
2673 if (err < 0) {
2674 goto bail;
2675 }
2676 // If we have library files, we're going to write our R.java file into
2677 // the appropriate class directory for those libraries as well.
2678 // e.g. gen/com/foo/app/lib/R.java
2679 if (bundle->getExtraPackages() != NULL) {
2680 // Split on colon
2681 String8 libs(bundle->getExtraPackages());
2682 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2683 while (packageString != NULL) {
2684 // Write the R.java file out with the correct package name
Marcin Kosiba0f3a5a62014-09-11 13:48:48 +01002685 err = writeResourceSymbols(bundle, assets, String8(packageString), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002686 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002687 if (err < 0) {
2688 goto bail;
2689 }
2690 packageString = strtok(NULL, ":");
2691 }
2692 libs.unlockBuffer();
2693 }
2694 } else {
Adam Lesinski1e4663852014-08-15 14:47:28 -07002695 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002696 if (err < 0) {
2697 goto bail;
2698 }
Adam Lesinski1e4663852014-08-15 14:47:28 -07002699 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002700 if (err < 0) {
2701 goto bail;
2702 }
2703 }
2704
2705 // Write out the ProGuard file
2706 err = writeProguardFile(bundle, assets);
2707 if (err < 0) {
2708 goto bail;
2709 }
2710
Rohit Agrawal86229cb2016-04-21 16:29:58 -07002711 // Write out the Main Dex ProGuard file
2712 err = writeMainDexProguardFile(bundle, assets);
2713 if (err < 0) {
2714 goto bail;
2715 }
2716
Adam Lesinski282e1812014-01-23 18:17:42 -08002717 // Write the apk
2718 if (outputAPKFile) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002719 // Gather all resources and add them to the APK Builder. The builder will then
2720 // figure out which Split they belong in.
2721 err = addResourcesToBuilder(assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002722 if (err != NO_ERROR) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002723 goto bail;
2724 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002725
2726 const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2727 const size_t numSplits = splits.size();
2728 for (size_t i = 0; i < numSplits; i++) {
2729 const sp<ApkSplit>& split = splits[i];
2730 String8 outputPath = buildApkName(String8(outputAPKFile), split);
2731 err = writeAPK(bundle, outputPath, split);
2732 if (err != NO_ERROR) {
2733 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2734 goto bail;
2735 }
2736 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002737 }
2738
2739 // If we've been asked to generate a dependency file, we need to finish up here.
2740 // the writeResourceSymbols and writeAPK functions have already written the target
2741 // half of the dependency file, now we need to write the prerequisites. (files that
2742 // the R.java file or .ap_ file depend on)
2743 if (bundle->getGenDependencies()) {
2744 // Now that writeResourceSymbols or writeAPK has taken care of writing
2745 // the targets to our dependency file, we'll write the prereqs
2746 fp = fopen(dependencyFile, "a+");
2747 fprintf(fp, " : ");
2748 bool includeRaw = (outputAPKFile != NULL);
2749 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2750 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2751 // and therefore was not added to our pathstores during slurping
2752 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2753 fclose(fp);
2754 }
2755
2756 retVal = 0;
2757bail:
2758 if (SourcePos::hasErrors()) {
2759 SourcePos::printErrors(stderr);
2760 }
2761 return retVal;
2762}
2763
2764/*
2765 * Do PNG Crunching
2766 * PRECONDITIONS
2767 * -S flag points to a source directory containing drawable* folders
2768 * -C flag points to destination directory. The folder structure in the
2769 * source directory will be mirrored to the destination (cache) directory
2770 *
2771 * POSTCONDITIONS
2772 * Destination directory will be updated to match the PNG files in
Maurice Chu2675f762013-10-22 17:33:11 -07002773 * the source directory.
Adam Lesinski282e1812014-01-23 18:17:42 -08002774 */
2775int doCrunch(Bundle* bundle)
2776{
2777 fprintf(stdout, "Crunching PNG Files in ");
2778 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2779 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2780
2781 updatePreProcessedCache(bundle);
2782
2783 return NO_ERROR;
2784}
2785
2786/*
2787 * Do PNG Crunching on a single flag
2788 * -i points to a single png file
2789 * -o points to a single png output file
2790 */
2791int doSingleCrunch(Bundle* bundle)
2792{
2793 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2794 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2795
2796 String8 input(bundle->getSingleCrunchInputFile());
2797 String8 output(bundle->getSingleCrunchOutputFile());
2798
2799 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2800 // we can't return the status_t as it gets truncate to the lower 8 bits.
2801 return 42;
2802 }
2803
2804 return NO_ERROR;
2805}
2806
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002807int runInDaemonMode(Bundle* bundle) {
2808 std::cout << "Ready" << std::endl;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002809 for (std::string cmd; std::getline(std::cin, cmd);) {
2810 if (cmd == "quit") {
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002811 return NO_ERROR;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002812 } else if (cmd == "s") {
2813 // Two argument crunch
2814 std::string inputFile, outputFile;
2815 std::getline(std::cin, inputFile);
2816 std::getline(std::cin, outputFile);
2817 bundle->setSingleCrunchInputFile(inputFile.c_str());
2818 bundle->setSingleCrunchOutputFile(outputFile.c_str());
2819 std::cout << "Crunching " << inputFile << std::endl;
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002820 if (doSingleCrunch(bundle) != NO_ERROR) {
2821 std::cout << "Error" << std::endl;
2822 }
2823 std::cout << "Done" << std::endl;
2824 } else {
2825 // in case of invalid command, just bail out.
2826 std::cerr << "Unknown command" << std::endl;
2827 return -1;
2828 }
2829 }
2830 return -1;
2831}
2832
Adam Lesinski282e1812014-01-23 18:17:42 -08002833char CONSOLE_DATA[2925] = {
2834 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2835 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2836 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2837 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2838 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2839 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2840 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2841 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2842 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2843 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2844 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2845 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2846 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2847 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2848 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2849 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2850 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2851 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2852 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2853 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2854 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2855 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2856 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2857 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2858 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2859 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2860 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2861 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2862 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2863 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2864 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2865 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2866 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2867 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2868 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2869 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2870 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2871 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2872 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2873 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2874 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2875 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2876 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2877 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2878 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2879 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2880 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2881 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2882 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2883 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2884 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2885 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2886 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2887 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2888 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2889 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2890 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2891 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2892 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2893 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2894 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2895 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2896 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2897 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2898 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2899 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2900 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2901 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2902 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2903 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2904 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2905 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2906 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2907 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2908 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2909 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2910 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2911 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2912 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2913 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2914 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2915 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2916 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2917 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2918 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2919 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2920 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2921 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2922 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2923 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2924 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2925 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2926 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2927 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2928 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2929 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2930 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2931 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2932 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2933 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2934 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2935 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2936 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2937 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2938 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2939 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2940 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2941 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2942 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2943 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2944 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2945 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2946 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2947 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2948 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2949 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2950 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2951 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2952 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2953 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2954 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2955 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2956 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2957 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2958 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2959 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2960 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2961 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2962 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2963 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2964 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2965 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2966 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2967 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2968 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2969 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2970 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2971 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2972 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2973 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2974 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2975 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2976 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2977 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2978 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2979 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2980 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2981 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2982 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2983 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2984 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2985 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2986 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2987 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2988 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2989 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2990 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2991 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2992 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2993 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2994 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2995 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2996 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2997 };