blob: 63f1802fdf2e576f29b7f377e639e222f9bfe9db [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 Lesinskiad751222014-08-18 14:06:38 -070032#ifndef AAPT_VERSION
33 #define AAPT_VERSION ""
34#endif
35
Adam Lesinski282e1812014-01-23 18:17:42 -080036/*
37 * Show version info. All the cool kids do it.
38 */
39int doVersion(Bundle* bundle)
40{
41 if (bundle->getFileSpecCount() != 0) {
42 printf("(ignoring extra arguments)\n");
43 }
Adam Lesinskiad751222014-08-18 14:06:38 -070044 printf("Android Asset Packaging Tool, v0.2-" AAPT_VERSION "\n");
Adam Lesinski282e1812014-01-23 18:17:42 -080045
46 return 0;
47}
48
49
50/*
51 * Open the file read only. The call fails if the file doesn't exist.
52 *
53 * Returns NULL on failure.
54 */
55ZipFile* openReadOnly(const char* fileName)
56{
57 ZipFile* zip;
58 status_t result;
59
60 zip = new ZipFile;
61 result = zip->open(fileName, ZipFile::kOpenReadOnly);
62 if (result != NO_ERROR) {
63 if (result == NAME_NOT_FOUND) {
64 fprintf(stderr, "ERROR: '%s' not found\n", fileName);
65 } else if (result == PERMISSION_DENIED) {
66 fprintf(stderr, "ERROR: '%s' access denied\n", fileName);
67 } else {
68 fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n",
69 fileName);
70 }
71 delete zip;
72 return NULL;
73 }
74
75 return zip;
76}
77
78/*
79 * Open the file read-write. The file will be created if it doesn't
80 * already exist and "okayToCreate" is set.
81 *
82 * Returns NULL on failure.
83 */
84ZipFile* openReadWrite(const char* fileName, bool okayToCreate)
85{
86 ZipFile* zip = NULL;
87 status_t result;
88 int flags;
89
90 flags = ZipFile::kOpenReadWrite;
91 if (okayToCreate) {
92 flags |= ZipFile::kOpenCreate;
93 }
94
95 zip = new ZipFile;
96 result = zip->open(fileName, flags);
97 if (result != NO_ERROR) {
98 delete zip;
99 zip = NULL;
100 goto bail;
101 }
102
103bail:
104 return zip;
105}
106
107
108/*
109 * Return a short string describing the compression method.
110 */
111const char* compressionName(int method)
112{
113 if (method == ZipEntry::kCompressStored) {
114 return "Stored";
115 } else if (method == ZipEntry::kCompressDeflated) {
116 return "Deflated";
117 } else {
118 return "Unknown";
119 }
120}
121
122/*
123 * Return the percent reduction in size (0% == no compression).
124 */
125int calcPercent(long uncompressedLen, long compressedLen)
126{
127 if (!uncompressedLen) {
128 return 0;
129 } else {
130 return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5);
131 }
132}
133
134/*
135 * Handle the "list" command, which can be a simple file dump or
136 * a verbose listing.
137 *
138 * The verbose listing closely matches the output of the Info-ZIP "unzip"
139 * command.
140 */
141int doList(Bundle* bundle)
142{
143 int result = 1;
144 ZipFile* zip = NULL;
145 const ZipEntry* entry;
146 long totalUncLen, totalCompLen;
147 const char* zipFileName;
148
149 if (bundle->getFileSpecCount() != 1) {
150 fprintf(stderr, "ERROR: specify zip file name (only)\n");
151 goto bail;
152 }
153 zipFileName = bundle->getFileSpecEntry(0);
154
155 zip = openReadOnly(zipFileName);
156 if (zip == NULL) {
157 goto bail;
158 }
159
160 int count, i;
161
162 if (bundle->getVerbose()) {
163 printf("Archive: %s\n", zipFileName);
164 printf(
165 " Length Method Size Ratio Offset Date Time CRC-32 Name\n");
166 printf(
167 "-------- ------ ------- ----- ------- ---- ---- ------ ----\n");
168 }
169
170 totalUncLen = totalCompLen = 0;
171
172 count = zip->getNumEntries();
173 for (i = 0; i < count; i++) {
174 entry = zip->getEntryByIndex(i);
175 if (bundle->getVerbose()) {
176 char dateBuf[32];
177 time_t when;
178
179 when = entry->getModWhen();
180 strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M",
181 localtime(&when));
182
183 printf("%8ld %-7.7s %7ld %3d%% %8zd %s %08lx %s\n",
184 (long) entry->getUncompressedLen(),
185 compressionName(entry->getCompressionMethod()),
186 (long) entry->getCompressedLen(),
187 calcPercent(entry->getUncompressedLen(),
188 entry->getCompressedLen()),
189 (size_t) entry->getLFHOffset(),
190 dateBuf,
191 entry->getCRC32(),
192 entry->getFileName());
193 } else {
194 printf("%s\n", entry->getFileName());
195 }
196
197 totalUncLen += entry->getUncompressedLen();
198 totalCompLen += entry->getCompressedLen();
199 }
200
201 if (bundle->getVerbose()) {
202 printf(
203 "-------- ------- --- -------\n");
204 printf("%8ld %7ld %2d%% %d files\n",
205 totalUncLen,
206 totalCompLen,
207 calcPercent(totalUncLen, totalCompLen),
208 zip->getNumEntries());
209 }
210
211 if (bundle->getAndroidList()) {
212 AssetManager assets;
213 if (!assets.addAssetPath(String8(zipFileName), NULL)) {
214 fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n");
215 goto bail;
216 }
217
Elliott Hughesba3fe562015-08-12 14:49:53 -0700218#ifdef __ANDROID__
Andreas Gampeb8dc7bc2014-10-01 19:07:51 -0700219 static const bool kHaveAndroidOs = true;
220#else
221 static const bool kHaveAndroidOs = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800222#endif
Andreas Gampeb8dc7bc2014-10-01 19:07:51 -0700223 const ResTable& res = assets.getResources(false);
224 if (!kHaveAndroidOs) {
225 printf("\nResource table:\n");
226 res.print(false);
227 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800228
229 Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
230 Asset::ACCESS_BUFFER);
231 if (manifestAsset == NULL) {
232 printf("\nNo AndroidManifest.xml found.\n");
233 } else {
234 printf("\nAndroid manifest:\n");
235 ResXMLTree tree;
236 tree.setTo(manifestAsset->getBuffer(true),
237 manifestAsset->getLength());
238 printXMLBlock(&tree);
239 }
240 delete manifestAsset;
241 }
242
243 result = 0;
244
245bail:
246 delete zip;
247 return result;
248}
249
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700250static void printResolvedResourceAttribute(const ResTable& resTable, const ResXMLTree& tree,
Chih-Hung Hsieh9b8528f2016-08-10 14:15:30 -0700251 uint32_t attrRes, const String8& attrLabel, String8* outError)
Maurice Chu76327312013-10-16 18:28:46 -0700252{
253 Res_value value;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700254 AaptXml::getResolvedResourceAttribute(resTable, tree, attrRes, &value, outError);
Maurice Chu76327312013-10-16 18:28:46 -0700255 if (*outError != "") {
256 *outError = "error print resolved resource attribute";
257 return;
258 }
259 if (value.dataType == Res_value::TYPE_STRING) {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700260 String8 result = AaptXml::getResolvedAttribute(resTable, tree, attrRes, outError);
Maurice Chu2675f762013-10-22 17:33:11 -0700261 printf("%s='%s'", attrLabel.string(),
262 ResTable::normalizeForOutput(result.string()).string());
Maurice Chu76327312013-10-16 18:28:46 -0700263 } else if (Res_value::TYPE_FIRST_INT <= value.dataType &&
264 value.dataType <= Res_value::TYPE_LAST_INT) {
265 printf("%s='%d'", attrLabel.string(), value.data);
266 } else {
267 printf("%s='0x%x'", attrLabel.string(), (int)value.data);
268 }
269}
270
Adam Lesinski282e1812014-01-23 18:17:42 -0800271// These are attribute resource constants for the platform, as found
272// in android.R.attr
273enum {
274 LABEL_ATTR = 0x01010001,
275 ICON_ATTR = 0x01010002,
276 NAME_ATTR = 0x01010003,
Adam Lesinskia5018c92013-09-30 16:23:15 -0700277 PERMISSION_ATTR = 0x01010006,
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700278 EXPORTED_ATTR = 0x01010010,
279 GRANT_URI_PERMISSIONS_ATTR = 0x0101001b,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700280 RESOURCE_ATTR = 0x01010025,
Adam Lesinski282e1812014-01-23 18:17:42 -0800281 DEBUGGABLE_ATTR = 0x0101000f,
282 VALUE_ATTR = 0x01010024,
283 VERSION_CODE_ATTR = 0x0101021b,
284 VERSION_NAME_ATTR = 0x0101021c,
285 SCREEN_ORIENTATION_ATTR = 0x0101001e,
286 MIN_SDK_VERSION_ATTR = 0x0101020c,
287 MAX_SDK_VERSION_ATTR = 0x01010271,
288 REQ_TOUCH_SCREEN_ATTR = 0x01010227,
289 REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
290 REQ_HARD_KEYBOARD_ATTR = 0x01010229,
291 REQ_NAVIGATION_ATTR = 0x0101022a,
292 REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
293 TARGET_SDK_VERSION_ATTR = 0x01010270,
294 TEST_ONLY_ATTR = 0x01010272,
295 ANY_DENSITY_ATTR = 0x0101026c,
296 GL_ES_VERSION_ATTR = 0x01010281,
297 SMALL_SCREEN_ATTR = 0x01010284,
298 NORMAL_SCREEN_ATTR = 0x01010285,
299 LARGE_SCREEN_ATTR = 0x01010286,
300 XLARGE_SCREEN_ATTR = 0x010102bf,
301 REQUIRED_ATTR = 0x0101028e,
Adam Lesinskicaf797c2014-08-22 12:56:26 -0700302 INSTALL_LOCATION_ATTR = 0x010102b7,
Adam Lesinski282e1812014-01-23 18:17:42 -0800303 SCREEN_SIZE_ATTR = 0x010102ca,
304 SCREEN_DENSITY_ATTR = 0x010102cb,
305 REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
306 COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
307 LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
308 PUBLIC_KEY_ATTR = 0x010103a6,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700309 CATEGORY_ATTR = 0x010103e8,
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800310 BANNER_ATTR = 0x10103f2,
Tim Kilbournd9b1cad2014-10-24 12:43:41 -0700311 ISGAME_ATTR = 0x10103f4,
Dianne Hackborncd154e92017-02-28 17:37:35 -0800312 REQUIRED_FEATURE_ATTR = 0x1010557,
313 REQUIRED_NOT_FEATURE_ATTR = 0x1010558,
Adam Lesinski282e1812014-01-23 18:17:42 -0800314};
315
Maurice Chu2675f762013-10-22 17:33:11 -0700316String8 getComponentName(String8 &pkgName, String8 &componentName) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800317 ssize_t idx = componentName.find(".");
318 String8 retStr(pkgName);
319 if (idx == 0) {
320 retStr += componentName;
321 } else if (idx < 0) {
322 retStr += ".";
323 retStr += componentName;
324 } else {
Maurice Chu2675f762013-10-22 17:33:11 -0700325 return componentName;
Adam Lesinski282e1812014-01-23 18:17:42 -0800326 }
Maurice Chu2675f762013-10-22 17:33:11 -0700327 return retStr;
Adam Lesinski282e1812014-01-23 18:17:42 -0800328}
329
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700330static void printCompatibleScreens(ResXMLTree& tree, String8* outError) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800331 size_t len;
332 ResXMLTree::event_code_t code;
333 int depth = 0;
334 bool first = true;
335 printf("compatible-screens:");
336 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
337 if (code == ResXMLTree::END_TAG) {
338 depth--;
339 if (depth < 0) {
340 break;
341 }
342 continue;
343 }
344 if (code != ResXMLTree::START_TAG) {
345 continue;
346 }
347 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700348 const char16_t* ctag16 = tree.getElementName(&len);
349 if (ctag16 == NULL) {
350 *outError = "failed to get XML element name (bad string pool)";
351 return;
352 }
353 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800354 if (tag == "screen") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700355 int32_t screenSize = AaptXml::getIntegerAttribute(tree,
356 SCREEN_SIZE_ATTR);
357 int32_t screenDensity = AaptXml::getIntegerAttribute(tree,
358 SCREEN_DENSITY_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -0800359 if (screenSize > 0 && screenDensity > 0) {
360 if (!first) {
361 printf(",");
362 }
363 first = false;
364 printf("'%d/%d'", screenSize, screenDensity);
365 }
366 }
367 }
368 printf("\n");
369}
370
Dianne Hackborncd154e92017-02-28 17:37:35 -0800371static void printUsesPermission(const String8& name, bool optional=false, int maxSdkVersion=-1,
372 const String8& requiredFeature = String8::empty(),
373 const String8& requiredNotFeature = String8::empty()) {
Adam Lesinski58f1f362013-11-12 12:59:08 -0800374 printf("uses-permission: name='%s'", ResTable::normalizeForOutput(name.string()).string());
375 if (maxSdkVersion != -1) {
376 printf(" maxSdkVersion='%d'", maxSdkVersion);
377 }
Dianne Hackborncd154e92017-02-28 17:37:35 -0800378 if (requiredFeature.length() > 0) {
379 printf(" requiredFeature='%s'", requiredFeature.string());
380 }
381 if (requiredNotFeature.length() > 0) {
382 printf(" requiredNotFeature='%s'", requiredNotFeature.string());
383 }
Adam Lesinski58f1f362013-11-12 12:59:08 -0800384 printf("\n");
385
386 if (optional) {
387 printf("optional-permission: name='%s'",
388 ResTable::normalizeForOutput(name.string()).string());
389 if (maxSdkVersion != -1) {
390 printf(" maxSdkVersion='%d'", maxSdkVersion);
391 }
392 printf("\n");
393 }
394}
395
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800396static void printUsesPermissionSdk23(const String8& name, int maxSdkVersion=-1) {
397 printf("uses-permission-sdk-23: ");
398
399 printf("name='%s'", ResTable::normalizeForOutput(name.string()).string());
400 if (maxSdkVersion != -1) {
401 printf(" maxSdkVersion='%d'", maxSdkVersion);
402 }
403 printf("\n");
404}
405
Adam Lesinski2386df22016-12-28 15:08:58 -0500406static void printUsesImpliedPermission(const String8& name, const String8& reason,
407 const int32_t maxSdkVersion = -1) {
408 printf("uses-implied-permission: name='%s'",
409 ResTable::normalizeForOutput(name.string()).string());
410 if (maxSdkVersion != -1) {
411 printf(" maxSdkVersion='%d'", maxSdkVersion);
412 }
413 printf(" reason='%s'\n", ResTable::normalizeForOutput(reason.string()).string());
Adam Lesinski58f1f362013-11-12 12:59:08 -0800414}
415
Chih-Hung Hsieh9b8528f2016-08-10 14:15:30 -0700416Vector<String8> getNfcAidCategories(AssetManager& assets, const String8& xmlPath, bool offHost,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700417 String8 *outError = NULL)
418{
419 Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER);
420 if (aidAsset == NULL) {
421 if (outError != NULL) *outError = "xml resource does not exist";
422 return Vector<String8>();
423 }
424
425 const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service");
426
427 bool withinApduService = false;
428 Vector<String8> categories;
429
430 String8 error;
431 ResXMLTree tree;
432 tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength());
433
434 size_t len;
435 int depth = 0;
436 ResXMLTree::event_code_t code;
437 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
438 if (code == ResXMLTree::END_TAG) {
439 depth--;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700440 const char16_t* ctag16 = tree.getElementName(&len);
441 if (ctag16 == NULL) {
442 *outError = "failed to get XML element name (bad string pool)";
443 return Vector<String8>();
444 }
445 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700446
447 if (depth == 0 && tag == serviceTagName) {
448 withinApduService = false;
449 }
450
451 } else if (code == ResXMLTree::START_TAG) {
452 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700453 const char16_t* ctag16 = tree.getElementName(&len);
454 if (ctag16 == NULL) {
455 *outError = "failed to get XML element name (bad string pool)";
456 return Vector<String8>();
457 }
458 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700459
460 if (depth == 1) {
461 if (tag == serviceTagName) {
462 withinApduService = true;
463 }
464 } else if (depth == 2 && withinApduService) {
465 if (tag == "aid-group") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700466 String8 category = AaptXml::getAttribute(tree, CATEGORY_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700467 if (error != "") {
468 if (outError != NULL) *outError = error;
469 return Vector<String8>();
470 }
471
472 categories.add(category);
473 }
474 }
475 }
476 }
477 aidAsset->close();
478 return categories;
479}
480
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700481static void printComponentPresence(const char* componentName) {
482 printf("provides-component:'%s'\n", componentName);
483}
484
Adam Lesinski2c72b682014-06-24 09:56:01 -0700485/**
486 * Represents a feature that has been automatically added due to
487 * a pre-requisite or some other reason.
488 */
489struct ImpliedFeature {
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800490 ImpliedFeature() : impliedBySdk23(false) {}
491 ImpliedFeature(const String8& n, bool sdk23) : name(n), impliedBySdk23(sdk23) {}
492
Adam Lesinski2c72b682014-06-24 09:56:01 -0700493 /**
494 * Name of the implied feature.
495 */
496 String8 name;
497
498 /**
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800499 * Was this implied by a permission from SDK 23 (<uses-permission-sdk-23 />)?
500 */
501 bool impliedBySdk23;
502
503 /**
Adam Lesinski2c72b682014-06-24 09:56:01 -0700504 * List of human-readable reasons for why this feature was implied.
505 */
506 SortedVector<String8> reasons;
507};
508
Adam Lesinski694d0a72016-04-06 16:12:04 -0700509struct Feature {
510 Feature() : required(false), version(-1) {}
Chih-Hung Hsiehd53e3be2016-05-03 10:02:51 -0700511 explicit Feature(bool required, int32_t version = -1) : required(required), version(version) {}
Adam Lesinski694d0a72016-04-06 16:12:04 -0700512
513 /**
514 * Whether the feature is required.
515 */
516 bool required;
517
518 /**
519 * What version of the feature is requested.
520 */
521 int32_t version;
522};
523
Adam Lesinski2c72b682014-06-24 09:56:01 -0700524/**
525 * Represents a <feature-group> tag in the AndroidManifest.xml
526 */
527struct FeatureGroup {
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700528 FeatureGroup() : openGLESVersion(-1) {}
529
Adam Lesinski2c72b682014-06-24 09:56:01 -0700530 /**
531 * Human readable label
532 */
533 String8 label;
534
535 /**
536 * Explicit features defined in the group
537 */
Adam Lesinski694d0a72016-04-06 16:12:04 -0700538 KeyedVector<String8, Feature> features;
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700539
540 /**
541 * OpenGL ES version required
542 */
543 int openGLESVersion;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700544};
545
Adam Lesinskica955a42016-08-01 16:44:29 -0700546static bool hasFeature(const char* name, const FeatureGroup& grp,
547 const KeyedVector<String8, ImpliedFeature>& implied) {
548 String8 name8(name);
549 ssize_t idx = grp.features.indexOfKey(name8);
550 if (idx < 0) {
551 idx = implied.indexOfKey(name8);
552 }
553 return idx >= 0;
554}
555
Adam Lesinski2c72b682014-06-24 09:56:01 -0700556static void addImpliedFeature(KeyedVector<String8, ImpliedFeature>* impliedFeatures,
Adam Lesinski43158772015-11-11 15:13:55 -0800557 const char* name, const String8& reason, bool sdk23) {
Adam Lesinski2c72b682014-06-24 09:56:01 -0700558 String8 name8(name);
559 ssize_t idx = impliedFeatures->indexOfKey(name8);
560 if (idx < 0) {
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800561 idx = impliedFeatures->add(name8, ImpliedFeature(name8, sdk23));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700562 }
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800563
564 ImpliedFeature* feature = &impliedFeatures->editValueAt(idx);
565
566 // A non-sdk 23 implied feature takes precedence.
567 if (feature->impliedBySdk23 && !sdk23) {
568 feature->impliedBySdk23 = false;
569 }
Adam Lesinski43158772015-11-11 15:13:55 -0800570 feature->reasons.add(reason);
Adam Lesinski2c72b682014-06-24 09:56:01 -0700571}
572
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800573static void printFeatureGroupImpl(const FeatureGroup& grp,
574 const KeyedVector<String8, ImpliedFeature>* impliedFeatures) {
Adam Lesinski2c72b682014-06-24 09:56:01 -0700575 printf("feature-group: label='%s'\n", grp.label.string());
576
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700577 if (grp.openGLESVersion > 0) {
578 printf(" uses-gl-es: '0x%x'\n", grp.openGLESVersion);
579 }
580
Adam Lesinski2c72b682014-06-24 09:56:01 -0700581 const size_t numFeatures = grp.features.size();
582 for (size_t i = 0; i < numFeatures; i++) {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700583 const Feature& feature = grp.features[i];
584 const bool required = feature.required;
585 const int32_t version = feature.version;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700586
587 const String8& featureName = grp.features.keyAt(i);
Adam Lesinski694d0a72016-04-06 16:12:04 -0700588 printf(" uses-feature%s: name='%s'", (required ? "" : "-not-required"),
Adam Lesinski2c72b682014-06-24 09:56:01 -0700589 ResTable::normalizeForOutput(featureName.string()).string());
Adam Lesinski694d0a72016-04-06 16:12:04 -0700590
591 if (version > 0) {
592 printf(" version='%d'", version);
593 }
594 printf("\n");
Adam Lesinski2c72b682014-06-24 09:56:01 -0700595 }
596
597 const size_t numImpliedFeatures =
598 (impliedFeatures != NULL) ? impliedFeatures->size() : 0;
599 for (size_t i = 0; i < numImpliedFeatures; i++) {
600 const ImpliedFeature& impliedFeature = impliedFeatures->valueAt(i);
601 if (grp.features.indexOfKey(impliedFeature.name) >= 0) {
602 // The feature is explicitly set, no need to use implied
603 // definition.
604 continue;
605 }
606
607 String8 printableFeatureName(ResTable::normalizeForOutput(
608 impliedFeature.name.string()));
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800609 const char* sdk23Suffix = impliedFeature.impliedBySdk23 ? "-sdk-23" : "";
610
611 printf(" uses-feature%s: name='%s'\n", sdk23Suffix, printableFeatureName.string());
612 printf(" uses-implied-feature%s: name='%s' reason='", sdk23Suffix,
613 printableFeatureName.string());
Adam Lesinski2c72b682014-06-24 09:56:01 -0700614 const size_t numReasons = impliedFeature.reasons.size();
615 for (size_t j = 0; j < numReasons; j++) {
616 printf("%s", impliedFeature.reasons[j].string());
617 if (j + 2 < numReasons) {
618 printf(", ");
619 } else if (j + 1 < numReasons) {
620 printf(", and ");
621 }
622 }
623 printf("'\n");
624 }
625}
626
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800627static void printFeatureGroup(const FeatureGroup& grp) {
628 printFeatureGroupImpl(grp, NULL);
629}
630
631static void printDefaultFeatureGroup(const FeatureGroup& grp,
632 const KeyedVector<String8, ImpliedFeature>& impliedFeatures) {
633 printFeatureGroupImpl(grp, &impliedFeatures);
634}
635
Adam Lesinski2c72b682014-06-24 09:56:01 -0700636static void addParentFeatures(FeatureGroup* grp, const String8& name) {
637 if (name == "android.hardware.camera.autofocus" ||
638 name == "android.hardware.camera.flash") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700639 grp->features.add(String8("android.hardware.camera"), Feature(true));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700640 } else if (name == "android.hardware.location.gps" ||
641 name == "android.hardware.location.network") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700642 grp->features.add(String8("android.hardware.location"), Feature(true));
Adam Lesinskica955a42016-08-01 16:44:29 -0700643 } else if (name == "android.hardware.faketouch.multitouch") {
644 grp->features.add(String8("android.hardware.faketouch"), Feature(true));
645 } else if (name == "android.hardware.faketouch.multitouch.distinct" ||
646 name == "android.hardware.faketouch.multitouch.jazzhands") {
647 grp->features.add(String8("android.hardware.faketouch.multitouch"), Feature(true));
648 grp->features.add(String8("android.hardware.faketouch"), Feature(true));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700649 } else if (name == "android.hardware.touchscreen.multitouch") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700650 grp->features.add(String8("android.hardware.touchscreen"), Feature(true));
Adam Lesinskica955a42016-08-01 16:44:29 -0700651 } else if (name == "android.hardware.touchscreen.multitouch.distinct" ||
652 name == "android.hardware.touchscreen.multitouch.jazzhands") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700653 grp->features.add(String8("android.hardware.touchscreen.multitouch"), Feature(true));
654 grp->features.add(String8("android.hardware.touchscreen"), Feature(true));
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700655 } else if (name == "android.hardware.opengles.aep") {
656 const int openGLESVersion31 = 0x00030001;
657 if (openGLESVersion31 > grp->openGLESVersion) {
658 grp->openGLESVersion = openGLESVersion31;
659 }
Adam Lesinski2c72b682014-06-24 09:56:01 -0700660 }
661}
662
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800663static void addImpliedFeaturesForPermission(const int targetSdk, const String8& name,
664 KeyedVector<String8, ImpliedFeature>* impliedFeatures,
665 bool impliedBySdk23Permission) {
666 if (name == "android.permission.CAMERA") {
667 addImpliedFeature(impliedFeatures, "android.hardware.camera",
Adam Lesinski43158772015-11-11 15:13:55 -0800668 String8::format("requested %s permission", name.string()),
669 impliedBySdk23Permission);
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800670 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
Adam Lesinski43158772015-11-11 15:13:55 -0800671 if (targetSdk < SDK_LOLLIPOP) {
672 addImpliedFeature(impliedFeatures, "android.hardware.location.gps",
673 String8::format("requested %s permission", name.string()),
674 impliedBySdk23Permission);
675 addImpliedFeature(impliedFeatures, "android.hardware.location.gps",
676 String8::format("targetSdkVersion < %d", SDK_LOLLIPOP),
677 impliedBySdk23Permission);
678 }
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800679 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.ACCESS_COARSE_LOCATION") {
Adam Lesinski43158772015-11-11 15:13:55 -0800683 if (targetSdk < SDK_LOLLIPOP) {
684 addImpliedFeature(impliedFeatures, "android.hardware.location.network",
685 String8::format("requested %s permission", name.string()),
686 impliedBySdk23Permission);
687 addImpliedFeature(impliedFeatures, "android.hardware.location.network",
688 String8::format("targetSdkVersion < %d", SDK_LOLLIPOP),
689 impliedBySdk23Permission);
690 }
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800691 addImpliedFeature(impliedFeatures, "android.hardware.location",
Adam Lesinski43158772015-11-11 15:13:55 -0800692 String8::format("requested %s permission", name.string()),
693 impliedBySdk23Permission);
694 } else if (name == "android.permission.ACCESS_MOCK_LOCATION" ||
695 name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800696 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
697 addImpliedFeature(impliedFeatures, "android.hardware.location",
Adam Lesinski43158772015-11-11 15:13:55 -0800698 String8::format("requested %s permission", name.string()),
699 impliedBySdk23Permission);
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800700 } else if (name == "android.permission.BLUETOOTH" ||
701 name == "android.permission.BLUETOOTH_ADMIN") {
Adam Lesinski43158772015-11-11 15:13:55 -0800702 if (targetSdk > SDK_DONUT) {
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800703 addImpliedFeature(impliedFeatures, "android.hardware.bluetooth",
Adam Lesinski43158772015-11-11 15:13:55 -0800704 String8::format("requested %s permission", name.string()),
705 impliedBySdk23Permission);
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800706 addImpliedFeature(impliedFeatures, "android.hardware.bluetooth",
Adam Lesinski43158772015-11-11 15:13:55 -0800707 String8::format("targetSdkVersion > %d", SDK_DONUT),
708 impliedBySdk23Permission);
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800709 }
710 } else if (name == "android.permission.RECORD_AUDIO") {
711 addImpliedFeature(impliedFeatures, "android.hardware.microphone",
Adam Lesinski43158772015-11-11 15:13:55 -0800712 String8::format("requested %s permission", name.string()),
713 impliedBySdk23Permission);
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800714 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
715 name == "android.permission.CHANGE_WIFI_STATE" ||
716 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
717 addImpliedFeature(impliedFeatures, "android.hardware.wifi",
Adam Lesinski43158772015-11-11 15:13:55 -0800718 String8::format("requested %s permission", name.string()),
719 impliedBySdk23Permission);
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800720 } else if (name == "android.permission.CALL_PHONE" ||
721 name == "android.permission.CALL_PRIVILEGED" ||
722 name == "android.permission.MODIFY_PHONE_STATE" ||
723 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
724 name == "android.permission.READ_SMS" ||
725 name == "android.permission.RECEIVE_SMS" ||
726 name == "android.permission.RECEIVE_MMS" ||
727 name == "android.permission.RECEIVE_WAP_PUSH" ||
728 name == "android.permission.SEND_SMS" ||
729 name == "android.permission.WRITE_APN_SETTINGS" ||
730 name == "android.permission.WRITE_SMS") {
731 addImpliedFeature(impliedFeatures, "android.hardware.telephony",
Adam Lesinski43158772015-11-11 15:13:55 -0800732 String8("requested a telephony permission"),
733 impliedBySdk23Permission);
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800734 }
735}
736
Adam Lesinski282e1812014-01-23 18:17:42 -0800737/*
738 * Handle the "dump" command, to extract select data from an archive.
739 */
740extern char CONSOLE_DATA[2925]; // see EOF
741int doDump(Bundle* bundle)
742{
743 status_t result = UNKNOWN_ERROR;
Adam Lesinski282e1812014-01-23 18:17:42 -0800744
745 if (bundle->getFileSpecCount() < 1) {
746 fprintf(stderr, "ERROR: no dump option specified\n");
747 return 1;
748 }
749
750 if (bundle->getFileSpecCount() < 2) {
751 fprintf(stderr, "ERROR: no dump file specified\n");
752 return 1;
753 }
754
755 const char* option = bundle->getFileSpecEntry(0);
756 const char* filename = bundle->getFileSpecEntry(1);
757
758 AssetManager assets;
Narayan Kamathf85e41f2014-01-24 14:14:30 +0000759 int32_t assetsCookie;
Adam Lesinski282e1812014-01-23 18:17:42 -0800760
Donald Chai94251fa2017-10-12 21:00:45 -0700761 // Add any dependencies passed in.
Adam Lesinski57fe4832017-05-10 15:42:22 -0700762 for (size_t i = 0; i < bundle->getPackageIncludes().size(); i++) {
763 const String8& assetPath = bundle->getPackageIncludes()[i];
764 if (!assets.addAssetPath(assetPath, NULL)) {
765 fprintf(stderr, "ERROR: included asset path %s could not be loaded\n", assetPath.string());
766 return 1;
767 }
768 }
769
Donald Chai94251fa2017-10-12 21:00:45 -0700770 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
771 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
772 return 1;
773 }
774
Adam Lesinski282e1812014-01-23 18:17:42 -0800775 // Make a dummy config for retrieving resources... we need to supply
776 // non-default values for some configs so that we can retrieve resources
777 // in the app that don't have a default. The most important of these is
778 // the API version because key resources like icons will have an implicit
779 // version if they are using newer config types like density.
780 ResTable_config config;
Narayan Kamath91447d82014-01-21 15:32:36 +0000781 memset(&config, 0, sizeof(ResTable_config));
Adam Lesinski282e1812014-01-23 18:17:42 -0800782 config.language[0] = 'e';
783 config.language[1] = 'n';
784 config.country[0] = 'U';
785 config.country[1] = 'S';
786 config.orientation = ResTable_config::ORIENTATION_PORT;
787 config.density = ResTable_config::DENSITY_MEDIUM;
788 config.sdkVersion = 10000; // Very high.
789 config.screenWidthDp = 320;
790 config.screenHeightDp = 480;
791 config.smallestScreenWidthDp = 320;
Adam Lesinskic2dea8d2014-08-04 16:40:41 -0700792 config.screenLayout |= ResTable_config::SCREENSIZE_NORMAL;
Adam Lesinski282e1812014-01-23 18:17:42 -0800793 assets.setConfiguration(config);
794
795 const ResTable& res = assets.getResources(false);
Dan Albert68001652014-09-09 09:51:01 -0700796 if (res.getError() != NO_ERROR) {
Adam Lesinski25e9d552014-05-19 15:01:43 -0700797 fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
Adam Lesinski63e646e2014-07-30 11:40:39 -0700798 return 1;
Adam Lesinski282e1812014-01-23 18:17:42 -0800799 }
800
Adam Lesinski694d0a72016-04-06 16:12:04 -0700801 // Source for AndroidManifest.xml
Adam Lesinski10de3af12016-07-13 10:14:03 -0700802 const String8 manifestFile("AndroidManifest.xml");
Adam Lesinski694d0a72016-04-06 16:12:04 -0700803
Adam Lesinski2cb761e2014-08-15 13:59:02 -0700804 // The dynamicRefTable can be null if there are no resources for this asset cookie.
805 // This fine.
Adam Lesinski63e646e2014-07-30 11:40:39 -0700806 const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700807
808 Asset* asset = NULL;
809
Adam Lesinski282e1812014-01-23 18:17:42 -0800810 if (strcmp("resources", option) == 0) {
Elliott Hughesba3fe562015-08-12 14:49:53 -0700811#ifndef __ANDROID__
Adam Lesinski282e1812014-01-23 18:17:42 -0800812 res.print(bundle->getValues());
813#endif
814
815 } else if (strcmp("strings", option) == 0) {
816 const ResStringPool* pool = res.getTableStringBlock(0);
817 printStringPool(pool);
818
819 } else if (strcmp("xmltree", option) == 0) {
820 if (bundle->getFileSpecCount() < 3) {
821 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
822 goto bail;
823 }
824
825 for (int i=2; i<bundle->getFileSpecCount(); i++) {
826 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700827 ResXMLTree tree(dynamicRefTable);
828 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800829 if (asset == NULL) {
Adam Lesinskifcb5f7b2016-11-02 13:17:10 -0700830 fprintf(stderr, "ERROR: dump failed because resource %s not found\n", resname);
Adam Lesinski282e1812014-01-23 18:17:42 -0800831 goto bail;
832 }
833
834 if (tree.setTo(asset->getBuffer(true),
835 asset->getLength()) != NO_ERROR) {
836 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
837 goto bail;
838 }
839 tree.restart();
840 printXMLBlock(&tree);
841 tree.uninit();
842 delete asset;
843 asset = NULL;
844 }
845
846 } else if (strcmp("xmlstrings", option) == 0) {
847 if (bundle->getFileSpecCount() < 3) {
848 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
849 goto bail;
850 }
851
852 for (int i=2; i<bundle->getFileSpecCount(); i++) {
853 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700854 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800855 if (asset == NULL) {
856 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
857 goto bail;
858 }
859
Adam Lesinski63e646e2014-07-30 11:40:39 -0700860 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800861 if (tree.setTo(asset->getBuffer(true),
862 asset->getLength()) != NO_ERROR) {
863 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
864 goto bail;
865 }
866 printStringPool(&tree.getStrings());
867 delete asset;
868 asset = NULL;
869 }
870
871 } else {
Adam Lesinski63e646e2014-07-30 11:40:39 -0700872 asset = assets.openNonAsset(assetsCookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800873 if (asset == NULL) {
874 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
875 goto bail;
876 }
877
Adam Lesinski63e646e2014-07-30 11:40:39 -0700878 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800879 if (tree.setTo(asset->getBuffer(true),
880 asset->getLength()) != NO_ERROR) {
881 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
882 goto bail;
883 }
884 tree.restart();
885
886 if (strcmp("permissions", option) == 0) {
887 size_t len;
888 ResXMLTree::event_code_t code;
889 int depth = 0;
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800890 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
891 code != ResXMLTree::BAD_DOCUMENT) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800892 if (code == ResXMLTree::END_TAG) {
893 depth--;
894 continue;
895 }
896 if (code != ResXMLTree::START_TAG) {
897 continue;
898 }
899 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700900 const char16_t* ctag16 = tree.getElementName(&len);
901 if (ctag16 == NULL) {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700902 SourcePos(manifestFile, tree.getLineNumber()).error(
903 "ERROR: failed to get XML element name (bad string pool)");
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700904 goto bail;
905 }
906 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800907 //printf("Depth %d tag %s\n", depth, tag.string());
908 if (depth == 1) {
909 if (tag != "manifest") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700910 SourcePos(manifestFile, tree.getLineNumber()).error(
911 "ERROR: manifest does not start with <manifest> tag");
Adam Lesinski282e1812014-01-23 18:17:42 -0800912 goto bail;
913 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700914 String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -0700915 printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800916 } else if (depth == 2) {
917 if (tag == "permission") {
918 String8 error;
919 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
920 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700921 SourcePos(manifestFile, tree.getLineNumber()).error(
922 "ERROR getting 'android:name': %s", error.string());
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800923 goto bail;
924 }
925
926 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700927 SourcePos(manifestFile, tree.getLineNumber()).error(
928 "ERROR: missing 'android:name' for permission");
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800929 goto bail;
930 }
931 printf("permission: %s\n",
932 ResTable::normalizeForOutput(name.string()).string());
933 } else if (tag == "uses-permission") {
934 String8 error;
935 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
936 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700937 SourcePos(manifestFile, tree.getLineNumber()).error(
938 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800939 goto bail;
940 }
941
942 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700943 SourcePos(manifestFile, tree.getLineNumber()).error(
944 "ERROR: missing 'android:name' for uses-permission");
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800945 goto bail;
946 }
947 printUsesPermission(name,
948 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
949 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
950 } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
951 String8 error;
952 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
953 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700954 SourcePos(manifestFile, tree.getLineNumber()).error(
955 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800956 goto bail;
957 }
958
959 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700960 SourcePos(manifestFile, tree.getLineNumber()).error(
961 "ERROR: missing 'android:name' for uses-permission-sdk-23");
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800962 goto bail;
963 }
964 printUsesPermissionSdk23(
965 name,
966 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
Adam Lesinski282e1812014-01-23 18:17:42 -0800967 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800968 }
969 }
970 } else if (strcmp("badging", option) == 0) {
971 Vector<String8> locales;
972 res.getLocales(&locales);
973
974 Vector<ResTable_config> configs;
975 res.getConfigurations(&configs);
976 SortedVector<int> densities;
977 const size_t NC = configs.size();
978 for (size_t i=0; i<NC; i++) {
979 int dens = configs[i].density;
980 if (dens == 0) {
981 dens = 160;
982 }
983 densities.add(dens);
984 }
985
986 size_t len;
987 ResXMLTree::event_code_t code;
988 int depth = 0;
989 String8 error;
990 bool withinActivity = false;
991 bool isMainActivity = false;
992 bool isLauncherActivity = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800993 bool isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800994 bool isSearchable = false;
995 bool withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700996 bool withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700997 bool withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800998 bool withinReceiver = false;
999 bool withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001000 bool withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001001 bool withinIntentFilter = false;
1002 bool hasMainActivity = false;
1003 bool hasOtherActivities = false;
1004 bool hasOtherReceivers = false;
1005 bool hasOtherServices = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001006 bool hasIntentFilter = false;
1007
Adam Lesinski282e1812014-01-23 18:17:42 -08001008 bool hasWallpaperService = false;
1009 bool hasImeService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001010 bool hasAccessibilityService = false;
1011 bool hasPrintService = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001012 bool hasWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001013 bool hasDeviceAdminReceiver = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001014 bool hasPaymentService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001015 bool hasDocumentsProvider = false;
1016 bool hasCameraActivity = false;
1017 bool hasCameraSecureActivity = false;
1018 bool hasLauncher = false;
1019 bool hasNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001020 bool hasDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001021
Adam Lesinski282e1812014-01-23 18:17:42 -08001022 bool actMainActivity = false;
1023 bool actWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001024 bool actDeviceAdminEnabled = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001025 bool actImeService = false;
1026 bool actWallpaperService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001027 bool actAccessibilityService = false;
1028 bool actPrintService = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001029 bool actHostApduService = false;
1030 bool actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001031 bool actDocumentsProvider = false;
1032 bool actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001033 bool actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001034 bool actCamera = false;
1035 bool actCameraSecure = false;
1036 bool catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001037 bool hasMetaHostPaymentCategory = false;
1038 bool hasMetaOffHostPaymentCategory = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001039
1040 // These permissions are required by services implementing services
1041 // the system binds to (IME, Accessibility, PrintServices, etc.)
1042 bool hasBindDeviceAdminPermission = false;
1043 bool hasBindInputMethodPermission = false;
1044 bool hasBindAccessibilityServicePermission = false;
1045 bool hasBindPrintServicePermission = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001046 bool hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001047 bool hasRequiredSafAttributes = false;
1048 bool hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001049 bool hasBindDreamServicePermission = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001050
1051 // These two implement the implicit permissions that are granted
1052 // to pre-1.6 applications.
1053 bool hasWriteExternalStoragePermission = false;
Adam Lesinski2386df22016-12-28 15:08:58 -05001054 int32_t writeExternalStoragePermissionMaxSdkVersion = -1;
Adam Lesinski282e1812014-01-23 18:17:42 -08001055 bool hasReadPhoneStatePermission = false;
1056
1057 // If an app requests write storage, they will also get read storage.
1058 bool hasReadExternalStoragePermission = false;
1059
1060 // Implement transition to read and write call log.
1061 bool hasReadContactsPermission = false;
1062 bool hasWriteContactsPermission = false;
1063 bool hasReadCallLogPermission = false;
1064 bool hasWriteCallLogPermission = false;
1065
Adam Lesinskie47fd122014-08-15 22:25:36 -07001066 // If an app declares itself as multiArch, we report the
1067 // native libraries differently.
1068 bool hasMultiArch = false;
1069
Adam Lesinski282e1812014-01-23 18:17:42 -08001070 // This next group of variables is used to implement a group of
1071 // backward-compatibility heuristics necessitated by the addition of
1072 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
1073 // heuristic is "if an app requests a permission but doesn't explicitly
1074 // request the corresponding <uses-feature>, presume it's there anyway".
Adam Lesinski2c72b682014-06-24 09:56:01 -07001075
Adam Lesinski282e1812014-01-23 18:17:42 -08001076 // 2.2 also added some other features that apps can request, but that
1077 // have no corresponding permission, so we cannot implement any
1078 // back-compatibility heuristic for them. The below are thus unnecessary
1079 // (but are retained here for documentary purposes.)
1080 //bool specCompassFeature = false;
1081 //bool specAccelerometerFeature = false;
1082 //bool specProximityFeature = false;
1083 //bool specAmbientLightFeature = false;
1084 //bool specLiveWallpaperFeature = false;
1085
1086 int targetSdk = 0;
1087 int smallScreen = 1;
1088 int normalScreen = 1;
1089 int largeScreen = 1;
1090 int xlargeScreen = 1;
1091 int anyDensity = 1;
1092 int requiresSmallestWidthDp = 0;
1093 int compatibleWidthLimitDp = 0;
1094 int largestWidthLimitDp = 0;
1095 String8 pkg;
1096 String8 activityName;
1097 String8 activityLabel;
1098 String8 activityIcon;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001099 String8 activityBanner;
Adam Lesinski282e1812014-01-23 18:17:42 -08001100 String8 receiverName;
1101 String8 serviceName;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001102 Vector<String8> supportedInput;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001103
1104 FeatureGroup commonFeatures;
1105 Vector<FeatureGroup> featureGroups;
1106 KeyedVector<String8, ImpliedFeature> impliedFeatures;
1107
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001108 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
1109 code != ResXMLTree::BAD_DOCUMENT) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001110 if (code == ResXMLTree::END_TAG) {
1111 depth--;
1112 if (depth < 2) {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001113 if (withinSupportsInput && !supportedInput.isEmpty()) {
1114 printf("supports-input: '");
1115 const size_t N = supportedInput.size();
1116 for (size_t i=0; i<N; i++) {
Maurice Chu2675f762013-10-22 17:33:11 -07001117 printf("%s", ResTable::normalizeForOutput(
1118 supportedInput[i].string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001119 if (i != N - 1) {
1120 printf("' '");
1121 } else {
1122 printf("'\n");
1123 }
1124 }
1125 supportedInput.clear();
1126 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001127 withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001128 withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001129 withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001130 } else if (depth < 3) {
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001131 if (withinActivity && isMainActivity) {
Maurice Chu2675f762013-10-22 17:33:11 -07001132 String8 aName(getComponentName(pkg, activityName));
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001133 if (isLauncherActivity) {
1134 printf("launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001135 if (aName.length() > 0) {
1136 printf(" name='%s' ",
1137 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001138 }
1139 printf(" label='%s' icon='%s'\n",
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001140 ResTable::normalizeForOutput(activityLabel.string())
1141 .string(),
1142 ResTable::normalizeForOutput(activityIcon.string())
1143 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001144 }
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001145 if (isLeanbackLauncherActivity) {
1146 printf("leanback-launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001147 if (aName.length() > 0) {
1148 printf(" name='%s' ",
1149 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001150 }
1151 printf(" label='%s' icon='%s' banner='%s'\n",
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001152 ResTable::normalizeForOutput(activityLabel.string())
1153 .string(),
1154 ResTable::normalizeForOutput(activityIcon.string())
1155 .string(),
1156 ResTable::normalizeForOutput(activityBanner.string())
1157 .string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001158 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001159 }
1160 if (!hasIntentFilter) {
1161 hasOtherActivities |= withinActivity;
1162 hasOtherReceivers |= withinReceiver;
1163 hasOtherServices |= withinService;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001164 } else {
1165 if (withinService) {
1166 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
1167 hasBindNfcServicePermission);
1168 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
1169 hasBindNfcServicePermission);
1170 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001171 }
1172 withinActivity = false;
1173 withinService = false;
1174 withinReceiver = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001175 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001176 hasIntentFilter = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001177 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001178 } else if (depth < 4) {
1179 if (withinIntentFilter) {
1180 if (withinActivity) {
1181 hasMainActivity |= actMainActivity;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001182 hasLauncher |= catLauncher;
1183 hasCameraActivity |= actCamera;
1184 hasCameraSecureActivity |= actCameraSecure;
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001185 hasOtherActivities |=
1186 !actMainActivity && !actCamera && !actCameraSecure;
Adam Lesinski282e1812014-01-23 18:17:42 -08001187 } else if (withinReceiver) {
1188 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001189 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
1190 hasBindDeviceAdminPermission);
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001191 hasOtherReceivers |=
1192 (!actWidgetReceivers && !actDeviceAdminEnabled);
Adam Lesinski282e1812014-01-23 18:17:42 -08001193 } else if (withinService) {
1194 hasImeService |= actImeService;
1195 hasWallpaperService |= actWallpaperService;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001196 hasAccessibilityService |= (actAccessibilityService &&
1197 hasBindAccessibilityServicePermission);
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001198 hasPrintService |=
1199 (actPrintService && hasBindPrintServicePermission);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001200 hasNotificationListenerService |= actNotificationListenerService &&
1201 hasBindNotificationListenerServicePermission;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001202 hasDreamService |= actDreamService && hasBindDreamServicePermission;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001203 hasOtherServices |= (!actImeService && !actWallpaperService &&
Adam Lesinski94fc9122013-09-30 17:16:09 -07001204 !actAccessibilityService && !actPrintService &&
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001205 !actHostApduService && !actOffHostApduService &&
1206 !actNotificationListenerService);
1207 } else if (withinProvider) {
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001208 hasDocumentsProvider |=
1209 actDocumentsProvider && hasRequiredSafAttributes;
Adam Lesinski282e1812014-01-23 18:17:42 -08001210 }
1211 }
1212 withinIntentFilter = false;
1213 }
1214 continue;
1215 }
1216 if (code != ResXMLTree::START_TAG) {
1217 continue;
1218 }
1219 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001220
1221 const char16_t* ctag16 = tree.getElementName(&len);
1222 if (ctag16 == NULL) {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001223 SourcePos(manifestFile, tree.getLineNumber()).error(
1224 "ERROR: failed to get XML element name (bad string pool)");
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001225 goto bail;
1226 }
1227 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -08001228 //printf("Depth %d, %s\n", depth, tag.string());
1229 if (depth == 1) {
1230 if (tag != "manifest") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001231 SourcePos(manifestFile, tree.getLineNumber()).error(
1232 "ERROR: manifest does not start with <manifest> tag");
Adam Lesinski282e1812014-01-23 18:17:42 -08001233 goto bail;
1234 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001235 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -07001236 printf("package: name='%s' ",
1237 ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001238 int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR,
1239 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001240 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001241 SourcePos(manifestFile, tree.getLineNumber()).error(
1242 "ERROR getting 'android:versionCode' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001243 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001244 goto bail;
1245 }
1246 if (versionCode > 0) {
1247 printf("versionCode='%d' ", versionCode);
1248 } else {
1249 printf("versionCode='' ");
1250 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001251 String8 versionName = AaptXml::getResolvedAttribute(res, tree,
1252 VERSION_NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001253 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001254 SourcePos(manifestFile, tree.getLineNumber()).error(
1255 "ERROR getting 'android:versionName' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001256 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001257 goto bail;
1258 }
Adam Lesinski25d35a92014-08-11 09:41:56 -07001259 printf("versionName='%s'",
Maurice Chu2675f762013-10-22 17:33:11 -07001260 ResTable::normalizeForOutput(versionName.string()).string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001261
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001262 String8 splitName = AaptXml::getAttribute(tree, NULL, "split");
Adam Lesinski25d35a92014-08-11 09:41:56 -07001263 if (!splitName.isEmpty()) {
1264 printf(" split='%s'", ResTable::normalizeForOutput(
1265 splitName.string()).string());
1266 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001267
Adam Lesinski5283fab2014-08-29 11:23:55 -07001268 String8 platformVersionName = AaptXml::getAttribute(tree, NULL,
1269 "platformBuildVersionName");
1270 printf(" platformBuildVersionName='%s'", platformVersionName.string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001271 printf("\n");
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001272
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001273 int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
1274 INSTALL_LOCATION_ATTR, &error);
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001275 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001276 SourcePos(manifestFile, tree.getLineNumber()).error(
1277 "ERROR getting 'android:installLocation' attribute: %s",
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001278 error.string());
1279 goto bail;
1280 }
1281
1282 if (installLocation >= 0) {
1283 printf("install-location:'");
1284 switch (installLocation) {
1285 case 0:
1286 printf("auto");
1287 break;
1288 case 1:
1289 printf("internalOnly");
1290 break;
1291 case 2:
1292 printf("preferExternal");
1293 break;
1294 default:
1295 fprintf(stderr, "Invalid installLocation %d\n", installLocation);
1296 goto bail;
1297 }
1298 printf("'\n");
1299 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001300 } else if (depth == 2) {
1301 withinApplication = false;
1302 if (tag == "application") {
1303 withinApplication = true;
1304
1305 String8 label;
1306 const size_t NL = locales.size();
1307 for (size_t i=0; i<NL; i++) {
1308 const char* localeStr = locales[i].string();
Adam Lesinskia77685f2016-10-03 16:26:28 -07001309 assets.setConfiguration(config, localeStr != NULL ? localeStr : "");
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001310 String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1311 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001312 if (llabel != "") {
1313 if (localeStr == NULL || strlen(localeStr) == 0) {
1314 label = llabel;
Maurice Chu2675f762013-10-22 17:33:11 -07001315 printf("application-label:'%s'\n",
1316 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001317 } else {
1318 if (label == "") {
1319 label = llabel;
1320 }
1321 printf("application-label-%s:'%s'\n", localeStr,
Maurice Chu2675f762013-10-22 17:33:11 -07001322 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001323 }
1324 }
1325 }
1326
1327 ResTable_config tmpConfig = config;
1328 const size_t ND = densities.size();
1329 for (size_t i=0; i<ND; i++) {
1330 tmpConfig.density = densities[i];
1331 assets.setConfiguration(tmpConfig);
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001332 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1333 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001334 if (icon != "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001335 printf("application-icon-%d:'%s'\n", densities[i],
1336 ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001337 }
1338 }
1339 assets.setConfiguration(config);
1340
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001341 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001342 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001343 SourcePos(manifestFile, tree.getLineNumber()).error(
1344 "ERROR getting 'android:icon' attribute: %s", error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001345 goto bail;
1346 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001347 int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0,
1348 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001349 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001350 SourcePos(manifestFile, tree.getLineNumber()).error(
1351 "ERROR getting 'android:testOnly' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001352 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001353 goto bail;
1354 }
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001355
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001356 String8 banner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1357 &error);
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001358 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001359 SourcePos(manifestFile, tree.getLineNumber()).error(
1360 "ERROR getting 'android:banner' attribute: %s", error.string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001361 goto bail;
1362 }
Maurice Chu2675f762013-10-22 17:33:11 -07001363 printf("application: label='%s' ",
1364 ResTable::normalizeForOutput(label.string()).string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001365 printf("icon='%s'", ResTable::normalizeForOutput(icon.string()).string());
1366 if (banner != "") {
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001367 printf(" banner='%s'",
1368 ResTable::normalizeForOutput(banner.string()).string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001369 }
1370 printf("\n");
Adam Lesinski282e1812014-01-23 18:17:42 -08001371 if (testOnly != 0) {
1372 printf("testOnly='%d'\n", testOnly);
1373 }
1374
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001375 int32_t isGame = AaptXml::getResolvedIntegerAttribute(res, tree,
1376 ISGAME_ATTR, 0, &error);
1377 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001378 SourcePos(manifestFile, tree.getLineNumber()).error(
1379 "ERROR getting 'android:isGame' attribute: %s", error.string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001380 goto bail;
1381 }
1382 if (isGame != 0) {
1383 printf("application-isGame\n");
1384 }
1385
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001386 int32_t debuggable = AaptXml::getResolvedIntegerAttribute(res, tree,
1387 DEBUGGABLE_ATTR, 0, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001388 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001389 SourcePos(manifestFile, tree.getLineNumber()).error(
1390 "ERROR getting 'android:debuggable' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001391 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001392 goto bail;
1393 }
1394 if (debuggable != 0) {
1395 printf("application-debuggable\n");
1396 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07001397
1398 // We must search by name because the multiArch flag hasn't been API
1399 // frozen yet.
1400 int32_t multiArchIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
1401 "multiArch");
1402 if (multiArchIndex >= 0) {
1403 Res_value value;
1404 if (tree.getAttributeValue(multiArchIndex, &value) != NO_ERROR) {
1405 if (value.dataType >= Res_value::TYPE_FIRST_INT &&
1406 value.dataType <= Res_value::TYPE_LAST_INT) {
1407 hasMultiArch = value.data;
1408 }
1409 }
1410 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001411 } else if (tag == "uses-sdk") {
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001412 int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR,
1413 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001414 if (error != "") {
1415 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001416 String8 name = AaptXml::getResolvedAttribute(res, tree,
1417 MIN_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001418 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001419 SourcePos(manifestFile, tree.getLineNumber()).error(
1420 "ERROR getting 'android:minSdkVersion' attribute: %s",
Adam Lesinski282e1812014-01-23 18:17:42 -08001421 error.string());
1422 goto bail;
1423 }
1424 if (name == "Donut") targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001425 printf("sdkVersion:'%s'\n",
1426 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001427 } else if (code != -1) {
1428 targetSdk = code;
1429 printf("sdkVersion:'%d'\n", code);
1430 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001431 code = AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -08001432 if (code != -1) {
1433 printf("maxSdkVersion:'%d'\n", code);
1434 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001435 code = AaptXml::getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001436 if (error != "") {
1437 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001438 String8 name = AaptXml::getResolvedAttribute(res, tree,
1439 TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001440 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001441 SourcePos(manifestFile, tree.getLineNumber()).error(
1442 "ERROR getting 'android:targetSdkVersion' attribute: %s",
Adam Lesinski282e1812014-01-23 18:17:42 -08001443 error.string());
1444 goto bail;
1445 }
1446 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001447 printf("targetSdkVersion:'%s'\n",
1448 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001449 } else if (code != -1) {
1450 if (targetSdk < code) {
1451 targetSdk = code;
1452 }
1453 printf("targetSdkVersion:'%d'\n", code);
1454 }
1455 } else if (tag == "uses-configuration") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001456 int32_t reqTouchScreen = AaptXml::getIntegerAttribute(tree,
1457 REQ_TOUCH_SCREEN_ATTR, 0);
1458 int32_t reqKeyboardType = AaptXml::getIntegerAttribute(tree,
1459 REQ_KEYBOARD_TYPE_ATTR, 0);
1460 int32_t reqHardKeyboard = AaptXml::getIntegerAttribute(tree,
1461 REQ_HARD_KEYBOARD_ATTR, 0);
1462 int32_t reqNavigation = AaptXml::getIntegerAttribute(tree,
1463 REQ_NAVIGATION_ATTR, 0);
1464 int32_t reqFiveWayNav = AaptXml::getIntegerAttribute(tree,
1465 REQ_FIVE_WAY_NAV_ATTR, 0);
Adam Lesinski282e1812014-01-23 18:17:42 -08001466 printf("uses-configuration:");
1467 if (reqTouchScreen != 0) {
1468 printf(" reqTouchScreen='%d'", reqTouchScreen);
1469 }
1470 if (reqKeyboardType != 0) {
1471 printf(" reqKeyboardType='%d'", reqKeyboardType);
1472 }
1473 if (reqHardKeyboard != 0) {
1474 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1475 }
1476 if (reqNavigation != 0) {
1477 printf(" reqNavigation='%d'", reqNavigation);
1478 }
1479 if (reqFiveWayNav != 0) {
1480 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1481 }
1482 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001483 } else if (tag == "supports-input") {
1484 withinSupportsInput = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001485 } else if (tag == "supports-screens") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001486 smallScreen = AaptXml::getIntegerAttribute(tree,
1487 SMALL_SCREEN_ATTR, 1);
1488 normalScreen = AaptXml::getIntegerAttribute(tree,
1489 NORMAL_SCREEN_ATTR, 1);
1490 largeScreen = AaptXml::getIntegerAttribute(tree,
1491 LARGE_SCREEN_ATTR, 1);
1492 xlargeScreen = AaptXml::getIntegerAttribute(tree,
1493 XLARGE_SCREEN_ATTR, 1);
1494 anyDensity = AaptXml::getIntegerAttribute(tree,
1495 ANY_DENSITY_ATTR, 1);
1496 requiresSmallestWidthDp = AaptXml::getIntegerAttribute(tree,
1497 REQUIRES_SMALLEST_WIDTH_DP_ATTR, 0);
1498 compatibleWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1499 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, 0);
1500 largestWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1501 LARGEST_WIDTH_LIMIT_DP_ATTR, 0);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001502 } else if (tag == "feature-group") {
1503 withinFeatureGroup = true;
1504 FeatureGroup group;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001505 group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001506 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001507 SourcePos(manifestFile, tree.getLineNumber()).error(
1508 "ERROR getting 'android:label' attribute: %s", error.string());
Adam Lesinski2c72b682014-06-24 09:56:01 -07001509 goto bail;
1510 }
1511 featureGroups.add(group);
1512
Adam Lesinski282e1812014-01-23 18:17:42 -08001513 } else if (tag == "uses-feature") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001514 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001515 if (name != "" && error == "") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001516 const char* androidSchema =
1517 "http://schemas.android.com/apk/res/android";
Adam Lesinski282e1812014-01-23 18:17:42 -08001518
Adam Lesinski694d0a72016-04-06 16:12:04 -07001519 int32_t req = AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1,
1520 &error);
1521 if (error != "") {
1522 SourcePos(manifestFile, tree.getLineNumber()).error(
1523 "failed to read attribute 'android:required': %s",
1524 error.string());
1525 goto bail;
1526 }
1527
1528 int32_t version = AaptXml::getIntegerAttribute(tree, androidSchema,
1529 "version", 0, &error);
1530 if (error != "") {
1531 SourcePos(manifestFile, tree.getLineNumber()).error(
1532 "failed to read attribute 'android:version': %s",
1533 error.string());
1534 goto bail;
1535 }
1536
1537 commonFeatures.features.add(name, Feature(req != 0, version));
Adam Lesinski2c72b682014-06-24 09:56:01 -07001538 if (req) {
1539 addParentFeatures(&commonFeatures, name);
Adam Lesinski282e1812014-01-23 18:17:42 -08001540 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001541 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001542 int vers = AaptXml::getIntegerAttribute(tree,
Adam Lesinski282e1812014-01-23 18:17:42 -08001543 GL_ES_VERSION_ATTR, &error);
1544 if (error == "") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001545 if (vers > commonFeatures.openGLESVersion) {
1546 commonFeatures.openGLESVersion = vers;
1547 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001548 }
1549 }
1550 } else if (tag == "uses-permission") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001551 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001552 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001553 SourcePos(manifestFile, tree.getLineNumber()).error(
1554 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001555 goto bail;
1556 }
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001557
1558 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001559 SourcePos(manifestFile, tree.getLineNumber()).error(
1560 "ERROR: missing 'android:name' for uses-permission");
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001561 goto bail;
1562 }
1563
1564 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, false);
1565
Adam Lesinski2386df22016-12-28 15:08:58 -05001566 const int32_t maxSdkVersion =
1567 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, -1);
Dianne Hackborncd154e92017-02-28 17:37:35 -08001568 const String8 requiredFeature = AaptXml::getAttribute(tree,
1569 REQUIRED_FEATURE_ATTR, &error);
1570 const String8 requiredNotFeature = AaptXml::getAttribute(tree,
1571 REQUIRED_NOT_FEATURE_ATTR, &error);
Adam Lesinski2386df22016-12-28 15:08:58 -05001572
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001573 if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1574 hasWriteExternalStoragePermission = true;
Adam Lesinski2386df22016-12-28 15:08:58 -05001575 writeExternalStoragePermissionMaxSdkVersion = maxSdkVersion;
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001576 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1577 hasReadExternalStoragePermission = true;
1578 } else if (name == "android.permission.READ_PHONE_STATE") {
1579 hasReadPhoneStatePermission = true;
1580 } else if (name == "android.permission.READ_CONTACTS") {
1581 hasReadContactsPermission = true;
1582 } else if (name == "android.permission.WRITE_CONTACTS") {
1583 hasWriteContactsPermission = true;
1584 } else if (name == "android.permission.READ_CALL_LOG") {
1585 hasReadCallLogPermission = true;
1586 } else if (name == "android.permission.WRITE_CALL_LOG") {
1587 hasWriteCallLogPermission = true;
1588 }
1589
1590 printUsesPermission(name,
1591 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
Dianne Hackborncd154e92017-02-28 17:37:35 -08001592 maxSdkVersion, requiredFeature, requiredNotFeature);
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001593
1594 } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
1595 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1596 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001597 SourcePos(manifestFile, tree.getLineNumber()).error(
1598 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001599 goto bail;
1600 }
1601
1602 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001603 SourcePos(manifestFile, tree.getLineNumber()).error(
1604 "ERROR: missing 'android:name' for uses-permission-sdk-23");
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001605 goto bail;
1606 }
1607
1608 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, true);
1609
1610 printUsesPermissionSdk23(
1611 name, AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
1612
Adam Lesinski282e1812014-01-23 18:17:42 -08001613 } else if (tag == "uses-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001614 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001615 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001616 printf("uses-package:'%s'\n",
1617 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001618 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001619 SourcePos(manifestFile, tree.getLineNumber()).error(
1620 "ERROR getting 'android:name' attribute: %s", error.string());
1621 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001622 }
1623 } else if (tag == "original-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001624 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001625 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001626 printf("original-package:'%s'\n",
1627 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001628 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001629 SourcePos(manifestFile, tree.getLineNumber()).error(
1630 "ERROR getting 'android:name' attribute: %s", error.string());
1631 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001632 }
1633 } else if (tag == "supports-gl-texture") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001634 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001635 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001636 printf("supports-gl-texture:'%s'\n",
1637 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001638 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001639 SourcePos(manifestFile, tree.getLineNumber()).error(
1640 "ERROR getting 'android:name' attribute: %s", error.string());
1641 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001642 }
1643 } else if (tag == "compatible-screens") {
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001644 printCompatibleScreens(tree, &error);
1645 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001646 SourcePos(manifestFile, tree.getLineNumber()).error(
1647 "ERROR getting compatible screens: %s", error.string());
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001648 goto bail;
1649 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001650 depth--;
1651 } else if (tag == "package-verifier") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001652 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001653 if (name != "" && error == "") {
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001654 String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR,
1655 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001656 if (publicKey != "" && error == "") {
1657 printf("package-verifier: name='%s' publicKey='%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001658 ResTable::normalizeForOutput(name.string()).string(),
1659 ResTable::normalizeForOutput(publicKey.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001660 }
1661 }
1662 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001663 } else if (depth == 3) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001664 withinActivity = false;
1665 withinReceiver = false;
1666 withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001667 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001668 hasIntentFilter = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001669 hasMetaHostPaymentCategory = false;
1670 hasMetaOffHostPaymentCategory = false;
1671 hasBindDeviceAdminPermission = false;
1672 hasBindInputMethodPermission = false;
1673 hasBindAccessibilityServicePermission = false;
1674 hasBindPrintServicePermission = false;
1675 hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001676 hasRequiredSafAttributes = false;
1677 hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001678 hasBindDreamServicePermission = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001679 if (withinApplication) {
1680 if(tag == "activity") {
1681 withinActivity = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001682 activityName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001683 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001684 SourcePos(manifestFile, tree.getLineNumber()).error(
1685 "ERROR getting 'android:name' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001686 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001687 goto bail;
1688 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001689
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001690 activityLabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1691 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001692 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001693 SourcePos(manifestFile, tree.getLineNumber()).error(
1694 "ERROR getting 'android:label' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001695 error.string());
1696 goto bail;
1697 }
1698
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001699 activityIcon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1700 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001701 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001702 SourcePos(manifestFile, tree.getLineNumber()).error(
1703 "ERROR getting 'android:icon' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001704 error.string());
1705 goto bail;
1706 }
1707
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001708 activityBanner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1709 &error);
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001710 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001711 SourcePos(manifestFile, tree.getLineNumber()).error(
1712 "ERROR getting 'android:banner' attribute: %s",
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001713 error.string());
1714 goto bail;
1715 }
1716
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001717 int32_t orien = AaptXml::getResolvedIntegerAttribute(res, tree,
Michael Wrightec4fdec2013-09-06 16:50:52 -07001718 SCREEN_ORIENTATION_ATTR, &error);
1719 if (error == "") {
1720 if (orien == 0 || orien == 6 || orien == 8) {
1721 // Requests landscape, sensorLandscape, or reverseLandscape.
Adam Lesinski43158772015-11-11 15:13:55 -08001722 addImpliedFeature(
1723 &impliedFeatures, "android.hardware.screen.landscape",
1724 String8("one or more activities have specified a "
1725 "landscape orientation"),
1726 false);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001727 } else if (orien == 1 || orien == 7 || orien == 9) {
1728 // Requests portrait, sensorPortrait, or reversePortrait.
Adam Lesinski43158772015-11-11 15:13:55 -08001729 addImpliedFeature(
1730 &impliedFeatures, "android.hardware.screen.portrait",
1731 String8("one or more activities have specified a "
1732 "portrait orientation"),
1733 false);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001734 }
1735 }
1736 } else if (tag == "uses-library") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001737 String8 libraryName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001738 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001739 SourcePos(manifestFile, tree.getLineNumber()).error(
Michael Wrightec4fdec2013-09-06 16:50:52 -07001740 "ERROR getting 'android:name' attribute for uses-library"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001741 " %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001742 goto bail;
1743 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001744 int req = AaptXml::getIntegerAttribute(tree,
1745 REQUIRED_ATTR, 1);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001746 printf("uses-library%s:'%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001747 req ? "" : "-not-required", ResTable::normalizeForOutput(
1748 libraryName.string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001749 } else if (tag == "receiver") {
1750 withinReceiver = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001751 receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001752
1753 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001754 SourcePos(manifestFile, tree.getLineNumber()).error(
Michael Wrightec4fdec2013-09-06 16:50:52 -07001755 "ERROR getting 'android:name' attribute for receiver:"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001756 " %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001757 goto bail;
1758 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001759
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001760 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1761 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001762 if (error == "") {
1763 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1764 hasBindDeviceAdminPermission = true;
1765 }
1766 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001767 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001768 "ERROR getting 'android:permission' attribute for"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001769 " receiver '%s': %s",
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001770 receiverName.string(), error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001771 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001772 } else if (tag == "service") {
1773 withinService = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001774 serviceName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001775
1776 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001777 SourcePos(manifestFile, tree.getLineNumber()).error(
1778 "ERROR getting 'android:name' attribute for "
1779 "service:%s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001780 goto bail;
1781 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001782
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001783 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1784 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001785 if (error == "") {
1786 if (permission == "android.permission.BIND_INPUT_METHOD") {
1787 hasBindInputMethodPermission = true;
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001788 } else if (permission ==
1789 "android.permission.BIND_ACCESSIBILITY_SERVICE") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001790 hasBindAccessibilityServicePermission = true;
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001791 } else if (permission ==
1792 "android.permission.BIND_PRINT_SERVICE") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001793 hasBindPrintServicePermission = true;
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001794 } else if (permission ==
1795 "android.permission.BIND_NFC_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07001796 hasBindNfcServicePermission = true;
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001797 } else if (permission ==
1798 "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001799 hasBindNotificationListenerServicePermission = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001800 } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
1801 hasBindDreamServicePermission = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001802 }
1803 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001804 SourcePos(manifestFile, tree.getLineNumber()).error(
1805 "ERROR getting 'android:permission' attribute for "
1806 "service '%s': %s", serviceName.string(), error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001807 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001808 } else if (tag == "provider") {
1809 withinProvider = true;
1810
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001811 bool exported = AaptXml::getResolvedIntegerAttribute(res, tree,
1812 EXPORTED_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001813 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001814 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001815 "ERROR getting 'android:exported' attribute for provider:"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001816 " %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001817 goto bail;
1818 }
1819
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001820 bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute(
1821 res, tree, GRANT_URI_PERMISSIONS_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001822 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001823 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001824 "ERROR getting 'android:grantUriPermissions' attribute for "
Adam Lesinski10de3af12016-07-13 10:14:03 -07001825 "provider: %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001826 goto bail;
1827 }
1828
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001829 String8 permission = AaptXml::getResolvedAttribute(res, tree,
1830 PERMISSION_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001831 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001832 SourcePos(manifestFile, tree.getLineNumber()).error(
1833 "ERROR getting 'android:permission' attribute for "
1834 "provider: %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001835 goto bail;
1836 }
1837
1838 hasRequiredSafAttributes |= exported && grantUriPermissions &&
1839 permission == "android.permission.MANAGE_DOCUMENTS";
1840
Michael Wrightec4fdec2013-09-06 16:50:52 -07001841 } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001842 String8 metaDataName = AaptXml::getResolvedAttribute(res, tree,
1843 NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001844 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001845 SourcePos(manifestFile, tree.getLineNumber()).error(
1846 "ERROR getting 'android:name' attribute for "
1847 "meta-data: %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001848 goto bail;
1849 }
Maurice Chu2675f762013-10-22 17:33:11 -07001850 printf("meta-data: name='%s' ",
1851 ResTable::normalizeForOutput(metaDataName.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001852 printResolvedResourceAttribute(res, tree, VALUE_ATTR, String8("value"),
Maurice Chu76327312013-10-16 18:28:46 -07001853 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001854 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001855 // Try looking for a RESOURCE_ATTR
1856 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001857 printResolvedResourceAttribute(res, tree, RESOURCE_ATTR,
Maurice Chu76327312013-10-16 18:28:46 -07001858 String8("resource"), &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001859 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001860 SourcePos(manifestFile, tree.getLineNumber()).error(
1861 "ERROR getting 'android:value' or "
Maurice Chu76327312013-10-16 18:28:46 -07001862 "'android:resource' attribute for "
Adam Lesinski10de3af12016-07-13 10:14:03 -07001863 "meta-data: %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001864 goto bail;
1865 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001866 }
Maurice Chu76327312013-10-16 18:28:46 -07001867 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001868 } else if (withinSupportsInput && tag == "input-type") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001869 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001870 if (name != "" && error == "") {
1871 supportedInput.add(name);
1872 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001873 SourcePos(manifestFile, tree.getLineNumber()).error(
1874 "ERROR getting 'android:name' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001875 error.string());
1876 goto bail;
1877 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001878 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001879 } else if (withinFeatureGroup && tag == "uses-feature") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001880 const String8 androidSchema("http://schemas.android.com/apk/res/android");
Adam Lesinski2c72b682014-06-24 09:56:01 -07001881 FeatureGroup& top = featureGroups.editTop();
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001882
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001883 String8 name = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001884 if (name != "" && error == "") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001885 Feature feature(true);
1886
1887 int32_t featureVers = AaptXml::getIntegerAttribute(
1888 tree, androidSchema.string(), "version", 0, &error);
1889 if (error == "") {
1890 feature.version = featureVers;
1891 } else {
1892 SourcePos(manifestFile, tree.getLineNumber()).error(
1893 "failed to read attribute 'android:version': %s",
1894 error.string());
1895 goto bail;
1896 }
1897
1898 top.features.add(name, feature);
Adam Lesinskid3edfde2014-08-08 17:32:44 -07001899 addParentFeatures(&top, name);
Adam Lesinski694d0a72016-04-06 16:12:04 -07001900
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001901 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001902 int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR,
1903 &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001904 if (error == "") {
1905 if (vers > top.openGLESVersion) {
1906 top.openGLESVersion = vers;
1907 }
1908 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001909 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001910 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001911 } else if (depth == 4) {
1912 if (tag == "intent-filter") {
1913 hasIntentFilter = true;
1914 withinIntentFilter = true;
1915 actMainActivity = false;
1916 actWidgetReceivers = false;
1917 actImeService = false;
1918 actWallpaperService = false;
1919 actAccessibilityService = false;
1920 actPrintService = false;
1921 actDeviceAdminEnabled = false;
1922 actHostApduService = false;
1923 actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001924 actDocumentsProvider = false;
1925 actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001926 actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001927 actCamera = false;
1928 actCameraSecure = false;
1929 catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001930 } else if (withinService && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001931 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001932 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001933 SourcePos(manifestFile, tree.getLineNumber()).error(
1934 "ERROR getting 'android:name' attribute for "
1935 "meta-data tag in service '%s': %s", serviceName.string(),
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001936 error.string());
Adam Lesinski94fc9122013-09-30 17:16:09 -07001937 goto bail;
1938 }
1939
1940 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1941 name == "android.nfc.cardemulation.off_host_apdu_service") {
1942 bool offHost = true;
1943 if (name == "android.nfc.cardemulation.host_apdu_service") {
1944 offHost = false;
1945 }
1946
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001947 String8 xmlPath = AaptXml::getResolvedAttribute(res, tree,
1948 RESOURCE_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001949 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001950 SourcePos(manifestFile, tree.getLineNumber()).error(
1951 "ERROR getting 'android:resource' attribute for "
1952 "meta-data tag in service '%s': %s",
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001953 serviceName.string(), error.string());
Adam Lesinski94fc9122013-09-30 17:16:09 -07001954 goto bail;
1955 }
1956
1957 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1958 offHost, &error);
1959 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001960 SourcePos(manifestFile, tree.getLineNumber()).error(
1961 "ERROR getting AID category for service '%s'",
Adam Lesinski94fc9122013-09-30 17:16:09 -07001962 serviceName.string());
1963 goto bail;
1964 }
1965
1966 const size_t catLen = categories.size();
1967 for (size_t i = 0; i < catLen; i++) {
1968 bool paymentCategory = (categories[i] == "payment");
1969 if (offHost) {
1970 hasMetaOffHostPaymentCategory |= paymentCategory;
1971 } else {
1972 hasMetaHostPaymentCategory |= paymentCategory;
1973 }
1974 }
1975 }
1976 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001977 } else if ((depth == 5) && withinIntentFilter) {
1978 String8 action;
1979 if (tag == "action") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001980 action = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001981 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001982 SourcePos(manifestFile, tree.getLineNumber()).error(
1983 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001984 goto bail;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001985 }
1986
Adam Lesinskia5018c92013-09-30 16:23:15 -07001987 if (withinActivity) {
1988 if (action == "android.intent.action.MAIN") {
1989 isMainActivity = true;
1990 actMainActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001991 } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
1992 action == "android.media.action.VIDEO_CAMERA") {
1993 actCamera = true;
1994 } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
1995 actCameraSecure = true;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001996 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001997 } else if (withinReceiver) {
1998 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1999 actWidgetReceivers = true;
2000 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
2001 actDeviceAdminEnabled = true;
2002 }
2003 } else if (withinService) {
2004 if (action == "android.view.InputMethod") {
2005 actImeService = true;
2006 } else if (action == "android.service.wallpaper.WallpaperService") {
2007 actWallpaperService = true;
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08002008 } else if (action ==
2009 "android.accessibilityservice.AccessibilityService") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07002010 actAccessibilityService = true;
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08002011 } else if (action =="android.printservice.PrintService") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07002012 actPrintService = true;
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08002013 } else if (action ==
2014 "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07002015 actHostApduService = true;
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08002016 } else if (action ==
2017 "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07002018 actOffHostApduService = true;
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08002019 } else if (action ==
2020 "android.service.notification.NotificationListenerService") {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002021 actNotificationListenerService = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04002022 } else if (action == "android.service.dreams.DreamService") {
2023 actDreamService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002024 }
2025 } else if (withinProvider) {
2026 if (action == "android.content.action.DOCUMENTS_PROVIDER") {
2027 actDocumentsProvider = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07002028 }
2029 }
2030 if (action == "android.intent.action.SEARCH") {
2031 isSearchable = true;
2032 }
2033 }
2034
2035 if (tag == "category") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07002036 String8 category = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07002037 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07002038 SourcePos(manifestFile, tree.getLineNumber()).error(
2039 "ERROR getting 'name' attribute: %s", error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07002040 goto bail;
2041 }
2042 if (withinActivity) {
2043 if (category == "android.intent.category.LAUNCHER") {
2044 isLauncherActivity = true;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08002045 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
2046 isLeanbackLauncherActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002047 } else if (category == "android.intent.category.HOME") {
2048 catLauncher = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08002049 }
2050 }
2051 }
2052 }
2053 }
2054
2055 // Pre-1.6 implicitly granted permission compatibility logic
2056 if (targetSdk < 4) {
2057 if (!hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002058 printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
2059 printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
2060 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002061 hasWriteExternalStoragePermission = true;
2062 }
2063 if (!hasReadPhoneStatePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002064 printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
2065 printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
2066 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002067 }
2068 }
2069
2070 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
2071 // force them to always take READ_EXTERNAL_STORAGE as well. We always
2072 // do this (regardless of target API version) because we can't have
2073 // an app with write permission but not read permission.
2074 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
Adam Lesinski2386df22016-12-28 15:08:58 -05002075 printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
2076 false /* optional */, writeExternalStoragePermissionMaxSdkVersion);
Adam Lesinski58f1f362013-11-12 12:59:08 -08002077 printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
Adam Lesinski2386df22016-12-28 15:08:58 -05002078 String8("requested WRITE_EXTERNAL_STORAGE"),
2079 writeExternalStoragePermissionMaxSdkVersion);
Adam Lesinski282e1812014-01-23 18:17:42 -08002080 }
2081
2082 // Pre-JellyBean call log permission compatibility.
2083 if (targetSdk < 16) {
2084 if (!hasReadCallLogPermission && hasReadContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002085 printUsesPermission(String8("android.permission.READ_CALL_LOG"));
2086 printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
2087 String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002088 }
2089 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002090 printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
2091 printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
2092 String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002093 }
2094 }
2095
Adam Lesinskica955a42016-08-01 16:44:29 -07002096 // If the app hasn't declared the touchscreen as a feature requirement (either
2097 // directly or implied, required or not), then the faketouch feature is implied.
2098 if (!hasFeature("android.hardware.touchscreen", commonFeatures, impliedFeatures)) {
2099 addImpliedFeature(&impliedFeatures, "android.hardware.faketouch",
Adam Lesinski43158772015-11-11 15:13:55 -08002100 String8("default feature for all apps"), false);
Adam Lesinskica955a42016-08-01 16:44:29 -07002101 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07002102
2103 const size_t numFeatureGroups = featureGroups.size();
2104 if (numFeatureGroups == 0) {
2105 // If no <feature-group> tags were defined, apply auto-implied features.
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08002106 printDefaultFeatureGroup(commonFeatures, impliedFeatures);
Adam Lesinski2c72b682014-06-24 09:56:01 -07002107
2108 } else {
2109 // <feature-group> tags are defined, so we ignore implied features and
2110 for (size_t i = 0; i < numFeatureGroups; i++) {
2111 FeatureGroup& grp = featureGroups.editItemAt(i);
2112
Adam Lesinskid7a94da2014-07-25 14:38:54 -07002113 if (commonFeatures.openGLESVersion > grp.openGLESVersion) {
2114 grp.openGLESVersion = commonFeatures.openGLESVersion;
2115 }
2116
Adam Lesinski2c72b682014-06-24 09:56:01 -07002117 // Merge the features defined in the top level (not inside a <feature-group>)
2118 // with this feature group.
2119 const size_t numCommonFeatures = commonFeatures.features.size();
2120 for (size_t j = 0; j < numCommonFeatures; j++) {
2121 if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07002122 grp.features.add(commonFeatures.features.keyAt(j),
2123 commonFeatures.features[j]);
Adam Lesinski2c72b682014-06-24 09:56:01 -07002124 }
2125 }
2126
Adam Lesinski73a05112014-12-08 12:53:17 -08002127 if (!grp.features.isEmpty()) {
Adam Lesinski2c72b682014-06-24 09:56:01 -07002128 printFeatureGroup(grp);
Adam Lesinski282e1812014-01-23 18:17:42 -08002129 }
2130 }
2131 }
2132
Adam Lesinski282e1812014-01-23 18:17:42 -08002133
Adam Lesinski282e1812014-01-23 18:17:42 -08002134 if (hasWidgetReceivers) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002135 printComponentPresence("app-widget");
Adam Lesinski282e1812014-01-23 18:17:42 -08002136 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002137 if (hasDeviceAdminReceiver) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002138 printComponentPresence("device-admin");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002139 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002140 if (hasImeService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002141 printComponentPresence("ime");
Adam Lesinski282e1812014-01-23 18:17:42 -08002142 }
2143 if (hasWallpaperService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002144 printComponentPresence("wallpaper");
Adam Lesinski282e1812014-01-23 18:17:42 -08002145 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002146 if (hasAccessibilityService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002147 printComponentPresence("accessibility");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002148 }
2149 if (hasPrintService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002150 printComponentPresence("print-service");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002151 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07002152 if (hasPaymentService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002153 printComponentPresence("payment");
2154 }
2155 if (isSearchable) {
2156 printComponentPresence("search");
2157 }
2158 if (hasDocumentsProvider) {
2159 printComponentPresence("document-provider");
2160 }
2161 if (hasLauncher) {
2162 printComponentPresence("launcher");
2163 }
2164 if (hasNotificationListenerService) {
2165 printComponentPresence("notification-listener");
2166 }
John Spurlockeb8d1be2014-06-25 17:46:15 -04002167 if (hasDreamService) {
2168 printComponentPresence("dream");
2169 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002170 if (hasCameraActivity) {
2171 printComponentPresence("camera");
2172 }
2173 if (hasCameraSecureActivity) {
2174 printComponentPresence("camera-secure");
2175 }
2176
2177 if (hasMainActivity) {
2178 printf("main\n");
Adam Lesinski94fc9122013-09-30 17:16:09 -07002179 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002180 if (hasOtherActivities) {
2181 printf("other-activities\n");
2182 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002183 if (hasOtherReceivers) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002184 printf("other-receivers\n");
2185 }
2186 if (hasOtherServices) {
2187 printf("other-services\n");
2188 }
2189
2190 // For modern apps, if screen size buckets haven't been specified
2191 // but the new width ranges have, then infer the buckets from them.
2192 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
2193 && requiresSmallestWidthDp > 0) {
2194 int compatWidth = compatibleWidthLimitDp;
2195 if (compatWidth <= 0) {
2196 compatWidth = requiresSmallestWidthDp;
2197 }
2198 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
2199 smallScreen = -1;
2200 } else {
2201 smallScreen = 0;
2202 }
2203 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
2204 normalScreen = -1;
2205 } else {
2206 normalScreen = 0;
2207 }
2208 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
2209 largeScreen = -1;
2210 } else {
2211 largeScreen = 0;
2212 }
2213 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
2214 xlargeScreen = -1;
2215 } else {
2216 xlargeScreen = 0;
2217 }
2218 }
2219
2220 // Determine default values for any unspecified screen sizes,
2221 // based on the target SDK of the package. As of 4 (donut)
2222 // the screen size support was introduced, so all default to
2223 // enabled.
2224 if (smallScreen > 0) {
2225 smallScreen = targetSdk >= 4 ? -1 : 0;
2226 }
2227 if (normalScreen > 0) {
2228 normalScreen = -1;
2229 }
2230 if (largeScreen > 0) {
2231 largeScreen = targetSdk >= 4 ? -1 : 0;
2232 }
2233 if (xlargeScreen > 0) {
2234 // Introduced in Gingerbread.
2235 xlargeScreen = targetSdk >= 9 ? -1 : 0;
2236 }
2237 if (anyDensity > 0) {
2238 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
2239 || compatibleWidthLimitDp > 0) ? -1 : 0;
2240 }
2241 printf("supports-screens:");
2242 if (smallScreen != 0) {
2243 printf(" 'small'");
2244 }
2245 if (normalScreen != 0) {
2246 printf(" 'normal'");
2247 }
2248 if (largeScreen != 0) {
2249 printf(" 'large'");
2250 }
2251 if (xlargeScreen != 0) {
2252 printf(" 'xlarge'");
2253 }
2254 printf("\n");
2255 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
2256 if (requiresSmallestWidthDp > 0) {
2257 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
2258 }
2259 if (compatibleWidthLimitDp > 0) {
2260 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
2261 }
2262 if (largestWidthLimitDp > 0) {
2263 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
2264 }
2265
2266 printf("locales:");
2267 const size_t NL = locales.size();
2268 for (size_t i=0; i<NL; i++) {
2269 const char* localeStr = locales[i].string();
2270 if (localeStr == NULL || strlen(localeStr) == 0) {
2271 localeStr = "--_--";
2272 }
2273 printf(" '%s'", localeStr);
2274 }
2275 printf("\n");
2276
2277 printf("densities:");
2278 const size_t ND = densities.size();
2279 for (size_t i=0; i<ND; i++) {
2280 printf(" '%d'", densities[i]);
2281 }
2282 printf("\n");
2283
2284 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
2285 if (dir != NULL) {
2286 if (dir->getFileCount() > 0) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002287 SortedVector<String8> architectures;
Adam Lesinski282e1812014-01-23 18:17:42 -08002288 for (size_t i=0; i<dir->getFileCount(); i++) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002289 architectures.add(ResTable::normalizeForOutput(
2290 dir->getFileName(i).string()));
Adam Lesinski282e1812014-01-23 18:17:42 -08002291 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07002292
2293 bool outputAltNativeCode = false;
2294 // A multiArch package is one that contains 64-bit and
2295 // 32-bit versions of native code and expects 3rd-party
2296 // apps to load these native code libraries. Since most
2297 // 64-bit systems also support 32-bit apps, the apps
2298 // loading this multiArch package's code may be either
2299 // 32-bit or 64-bit.
2300 if (hasMultiArch) {
2301 // If this is a multiArch package, report the 64-bit
2302 // version only. Then as a separate entry, report the
2303 // rest.
2304 //
2305 // If we report the 32-bit architecture, this APK will
2306 // be installed on a 32-bit device, causing a large waste
2307 // of bandwidth and disk space. This assumes that
2308 // the developer of the multiArch package has also
2309 // made a version that is 32-bit only.
2310 String8 intel64("x86_64");
2311 String8 arm64("arm64-v8a");
2312 ssize_t index = architectures.indexOf(intel64);
2313 if (index < 0) {
2314 index = architectures.indexOf(arm64);
2315 }
2316
2317 if (index >= 0) {
2318 printf("native-code: '%s'\n", architectures[index].string());
2319 architectures.removeAt(index);
2320 outputAltNativeCode = true;
2321 }
2322 }
2323
2324 const size_t archCount = architectures.size();
2325 if (archCount > 0) {
2326 if (outputAltNativeCode) {
2327 printf("alt-");
2328 }
2329 printf("native-code:");
2330 for (size_t i = 0; i < archCount; i++) {
2331 printf(" '%s'", architectures[i].string());
2332 }
2333 printf("\n");
2334 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002335 }
2336 delete dir;
2337 }
2338 } else if (strcmp("badger", option) == 0) {
2339 printf("%s", CONSOLE_DATA);
2340 } else if (strcmp("configurations", option) == 0) {
2341 Vector<ResTable_config> configs;
2342 res.getConfigurations(&configs);
2343 const size_t N = configs.size();
2344 for (size_t i=0; i<N; i++) {
2345 printf("%s\n", configs[i].toString().string());
2346 }
2347 } else {
2348 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2349 goto bail;
2350 }
2351 }
2352
2353 result = NO_ERROR;
2354
2355bail:
Adam Lesinski10de3af12016-07-13 10:14:03 -07002356 if (SourcePos::hasErrors()) {
2357 SourcePos::printErrors(stderr);
2358 }
2359
Adam Lesinski282e1812014-01-23 18:17:42 -08002360 if (asset) {
2361 delete asset;
2362 }
2363 return (result != NO_ERROR);
2364}
2365
2366
2367/*
2368 * Handle the "add" command, which wants to add files to a new or
2369 * pre-existing archive.
2370 */
2371int doAdd(Bundle* bundle)
2372{
2373 ZipFile* zip = NULL;
2374 status_t result = UNKNOWN_ERROR;
2375 const char* zipFileName;
2376
2377 if (bundle->getUpdate()) {
2378 /* avoid confusion */
2379 fprintf(stderr, "ERROR: can't use '-u' with add\n");
2380 goto bail;
2381 }
2382
2383 if (bundle->getFileSpecCount() < 1) {
2384 fprintf(stderr, "ERROR: must specify zip file name\n");
2385 goto bail;
2386 }
2387 zipFileName = bundle->getFileSpecEntry(0);
2388
2389 if (bundle->getFileSpecCount() < 2) {
2390 fprintf(stderr, "NOTE: nothing to do\n");
2391 goto bail;
2392 }
2393
2394 zip = openReadWrite(zipFileName, true);
2395 if (zip == NULL) {
2396 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2397 goto bail;
2398 }
2399
2400 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2401 const char* fileName = bundle->getFileSpecEntry(i);
2402
2403 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
2404 printf(" '%s'... (from gzip)\n", fileName);
2405 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
2406 } else {
2407 if (bundle->getJunkPath()) {
2408 String8 storageName = String8(fileName).getPathLeaf();
Maurice Chu2675f762013-10-22 17:33:11 -07002409 printf(" '%s' as '%s'...\n", fileName,
2410 ResTable::normalizeForOutput(storageName.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08002411 result = zip->add(fileName, storageName.string(),
2412 bundle->getCompressionMethod(), NULL);
2413 } else {
2414 printf(" '%s'...\n", fileName);
2415 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2416 }
2417 }
2418 if (result != NO_ERROR) {
2419 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2420 if (result == NAME_NOT_FOUND) {
2421 fprintf(stderr, ": file not found\n");
2422 } else if (result == ALREADY_EXISTS) {
2423 fprintf(stderr, ": already exists in archive\n");
2424 } else {
2425 fprintf(stderr, "\n");
2426 }
2427 goto bail;
2428 }
2429 }
2430
2431 result = NO_ERROR;
2432
2433bail:
2434 delete zip;
2435 return (result != NO_ERROR);
2436}
2437
2438
2439/*
2440 * Delete files from an existing archive.
2441 */
2442int doRemove(Bundle* bundle)
2443{
2444 ZipFile* zip = NULL;
2445 status_t result = UNKNOWN_ERROR;
2446 const char* zipFileName;
2447
2448 if (bundle->getFileSpecCount() < 1) {
2449 fprintf(stderr, "ERROR: must specify zip file name\n");
2450 goto bail;
2451 }
2452 zipFileName = bundle->getFileSpecEntry(0);
2453
2454 if (bundle->getFileSpecCount() < 2) {
2455 fprintf(stderr, "NOTE: nothing to do\n");
2456 goto bail;
2457 }
2458
2459 zip = openReadWrite(zipFileName, false);
2460 if (zip == NULL) {
2461 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2462 zipFileName);
2463 goto bail;
2464 }
2465
2466 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2467 const char* fileName = bundle->getFileSpecEntry(i);
2468 ZipEntry* entry;
2469
2470 entry = zip->getEntryByName(fileName);
2471 if (entry == NULL) {
2472 printf(" '%s' NOT FOUND\n", fileName);
2473 continue;
2474 }
2475
2476 result = zip->remove(entry);
2477
2478 if (result != NO_ERROR) {
2479 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2480 bundle->getFileSpecEntry(i), zipFileName);
2481 goto bail;
2482 }
2483 }
2484
2485 /* update the archive */
2486 zip->flush();
2487
2488bail:
2489 delete zip;
2490 return (result != NO_ERROR);
2491}
2492
Adam Lesinski3921e872014-05-13 10:56:25 -07002493static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002494 const size_t numDirs = dir->getDirs().size();
2495 for (size_t i = 0; i < numDirs; i++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002496 bool ignore = ignoreConfig;
2497 const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2498 const char* dirStr = subDir->getLeaf().string();
2499 if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2500 ignore = true;
2501 }
2502 status_t err = addResourcesToBuilder(subDir, builder, ignore);
Adam Lesinskifab50872014-04-16 14:40:42 -07002503 if (err != NO_ERROR) {
2504 return err;
2505 }
2506 }
2507
2508 const size_t numFiles = dir->getFiles().size();
2509 for (size_t i = 0; i < numFiles; i++) {
2510 sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2511 const size_t numConfigs = gp->getFiles().size();
2512 for (size_t j = 0; j < numConfigs; j++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002513 status_t err = NO_ERROR;
Adam Lesinskic7614e52017-03-16 16:54:23 -07002514 if (ignoreConfig) {
Guang Zhu8c2df712017-03-21 03:53:43 +00002515 err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
Adam Lesinskic7614e52017-03-16 16:54:23 -07002516 } else {
Guang Zhu8c2df712017-03-21 03:53:43 +00002517 err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
Adam Lesinskic7614e52017-03-16 16:54:23 -07002518 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002519 if (err != NO_ERROR) {
2520 fprintf(stderr, "Failed to add %s (%s) to builder.\n",
Guang Zhu8c2df712017-03-21 03:53:43 +00002521 gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
Adam Lesinskifab50872014-04-16 14:40:42 -07002522 return err;
2523 }
2524 }
2525 }
2526 return NO_ERROR;
2527}
2528
2529static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2530 if (split->isBase()) {
2531 return original;
2532 }
2533
2534 String8 ext(original.getPathExtension());
2535 if (ext == String8(".apk")) {
2536 return String8::format("%s_%s%s",
2537 original.getBasePath().string(),
2538 split->getDirectorySafeName().string(),
2539 ext.string());
2540 }
2541
2542 return String8::format("%s_%s", original.string(),
2543 split->getDirectorySafeName().string());
2544}
Adam Lesinski282e1812014-01-23 18:17:42 -08002545
2546/*
2547 * Package up an asset directory and associated application files.
2548 */
2549int doPackage(Bundle* bundle)
2550{
2551 const char* outputAPKFile;
2552 int retVal = 1;
2553 status_t err;
2554 sp<AaptAssets> assets;
2555 int N;
2556 FILE* fp;
2557 String8 dependencyFile;
Adam Lesinskifab50872014-04-16 14:40:42 -07002558 sp<ApkBuilder> builder;
Adam Lesinski282e1812014-01-23 18:17:42 -08002559
Anton Krumina2ef5c02014-03-12 14:46:44 -07002560 // -c en_XA or/and ar_XB means do pseudolocalization
Adam Lesinskifab50872014-04-16 14:40:42 -07002561 sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2562 err = configFilter->parse(bundle->getConfigurations());
Adam Lesinski282e1812014-01-23 18:17:42 -08002563 if (err != NO_ERROR) {
2564 goto bail;
2565 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002566 if (configFilter->containsPseudo()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002567 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2568 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002569 if (configFilter->containsPseudoBidi()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002570 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
Adam Lesinski282e1812014-01-23 18:17:42 -08002571 }
2572
2573 N = bundle->getFileSpecCount();
2574 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
Adam Lesinski09384302014-01-22 16:07:42 -08002575 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002576 fprintf(stderr, "ERROR: no input files\n");
2577 goto bail;
2578 }
2579
2580 outputAPKFile = bundle->getOutputAPKFile();
2581
2582 // Make sure the filenames provided exist and are of the appropriate type.
2583 if (outputAPKFile) {
2584 FileType type;
2585 type = getFileType(outputAPKFile);
2586 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2587 fprintf(stderr,
2588 "ERROR: output file '%s' exists but is not regular file\n",
2589 outputAPKFile);
2590 goto bail;
2591 }
2592 }
2593
2594 // Load the assets.
2595 assets = new AaptAssets();
2596
2597 // Set up the resource gathering in assets if we're going to generate
2598 // dependency files. Every time we encounter a resource while slurping
2599 // the tree, we'll add it to these stores so we have full resource paths
2600 // to write to a dependency file.
2601 if (bundle->getGenDependencies()) {
2602 sp<FilePathStore> resPathStore = new FilePathStore;
2603 assets->setFullResPaths(resPathStore);
2604 sp<FilePathStore> assetPathStore = new FilePathStore;
2605 assets->setFullAssetPaths(assetPathStore);
2606 }
2607
2608 err = assets->slurpFromArgs(bundle);
2609 if (err < 0) {
2610 goto bail;
2611 }
2612
2613 if (bundle->getVerbose()) {
2614 assets->print(String8());
2615 }
2616
Adam Lesinskifab50872014-04-16 14:40:42 -07002617 // Create the ApkBuilder, which will collect the compiled files
2618 // to write to the final APK (or sets of APKs if we are building
2619 // a Split APK.
2620 builder = new ApkBuilder(configFilter);
2621
2622 // If we are generating a Split APK, find out which configurations to split on.
2623 if (bundle->getSplitConfigurations().size() > 0) {
2624 const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2625 const size_t numSplits = splitStrs.size();
2626 for (size_t i = 0; i < numSplits; i++) {
2627 std::set<ConfigDescription> configs;
2628 if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2629 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2630 goto bail;
2631 }
2632
2633 err = builder->createSplitForConfigs(configs);
2634 if (err != NO_ERROR) {
2635 goto bail;
2636 }
2637 }
2638 }
2639
Adam Lesinski282e1812014-01-23 18:17:42 -08002640 // If they asked for any fileAs that need to be compiled, do so.
2641 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002642 err = buildResources(bundle, assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002643 if (err != 0) {
2644 goto bail;
2645 }
2646 }
2647
2648 // At this point we've read everything and processed everything. From here
2649 // on out it's just writing output files.
2650 if (SourcePos::hasErrors()) {
2651 goto bail;
2652 }
2653
2654 // Update symbols with information about which ones are needed as Java symbols.
2655 assets->applyJavaSymbols();
2656 if (SourcePos::hasErrors()) {
2657 goto bail;
2658 }
2659
2660 // If we've been asked to generate a dependency file, do that here
2661 if (bundle->getGenDependencies()) {
2662 // If this is the packaging step, generate the dependency file next to
2663 // the output apk (e.g. bin/resources.ap_.d)
2664 if (outputAPKFile) {
2665 dependencyFile = String8(outputAPKFile);
2666 // Add the .d extension to the dependency file.
2667 dependencyFile.append(".d");
2668 } else {
2669 // Else if this is the R.java dependency generation step,
2670 // generate the dependency file in the R.java package subdirectory
2671 // e.g. gen/com/foo/app/R.java.d
2672 dependencyFile = String8(bundle->getRClassDir());
2673 dependencyFile.appendPath("R.java.d");
2674 }
2675 // Make sure we have a clean dependency file to start with
2676 fp = fopen(dependencyFile, "w");
2677 fclose(fp);
2678 }
2679
2680 // Write out R.java constants
2681 if (!assets->havePrivateSymbols()) {
2682 if (bundle->getCustomPackage() == NULL) {
2683 // Write the R.java file into the appropriate class directory
2684 // e.g. gen/com/foo/app/R.java
Adam Lesinski1e4663852014-08-15 14:47:28 -07002685 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002686 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002687 } else {
2688 const String8 customPkg(bundle->getCustomPackage());
Adam Lesinski1e4663852014-08-15 14:47:28 -07002689 err = writeResourceSymbols(bundle, assets, customPkg, true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002690 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002691 }
2692 if (err < 0) {
2693 goto bail;
2694 }
2695 // If we have library files, we're going to write our R.java file into
2696 // the appropriate class directory for those libraries as well.
2697 // e.g. gen/com/foo/app/lib/R.java
2698 if (bundle->getExtraPackages() != NULL) {
2699 // Split on colon
2700 String8 libs(bundle->getExtraPackages());
2701 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2702 while (packageString != NULL) {
2703 // Write the R.java file out with the correct package name
Marcin Kosiba0f3a5a62014-09-11 13:48:48 +01002704 err = writeResourceSymbols(bundle, assets, String8(packageString), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002705 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002706 if (err < 0) {
2707 goto bail;
2708 }
2709 packageString = strtok(NULL, ":");
2710 }
2711 libs.unlockBuffer();
2712 }
2713 } else {
Adam Lesinski1e4663852014-08-15 14:47:28 -07002714 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002715 if (err < 0) {
2716 goto bail;
2717 }
Adam Lesinski1e4663852014-08-15 14:47:28 -07002718 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002719 if (err < 0) {
2720 goto bail;
2721 }
2722 }
2723
2724 // Write out the ProGuard file
2725 err = writeProguardFile(bundle, assets);
2726 if (err < 0) {
2727 goto bail;
2728 }
2729
Rohit Agrawal86229cb2016-04-21 16:29:58 -07002730 // Write out the Main Dex ProGuard file
2731 err = writeMainDexProguardFile(bundle, assets);
2732 if (err < 0) {
2733 goto bail;
2734 }
2735
Adam Lesinski282e1812014-01-23 18:17:42 -08002736 // Write the apk
2737 if (outputAPKFile) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002738 // Gather all resources and add them to the APK Builder. The builder will then
2739 // figure out which Split they belong in.
2740 err = addResourcesToBuilder(assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002741 if (err != NO_ERROR) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002742 goto bail;
2743 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002744
2745 const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2746 const size_t numSplits = splits.size();
2747 for (size_t i = 0; i < numSplits; i++) {
2748 const sp<ApkSplit>& split = splits[i];
2749 String8 outputPath = buildApkName(String8(outputAPKFile), split);
2750 err = writeAPK(bundle, outputPath, split);
2751 if (err != NO_ERROR) {
2752 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2753 goto bail;
2754 }
2755 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002756 }
2757
2758 // If we've been asked to generate a dependency file, we need to finish up here.
2759 // the writeResourceSymbols and writeAPK functions have already written the target
2760 // half of the dependency file, now we need to write the prerequisites. (files that
2761 // the R.java file or .ap_ file depend on)
2762 if (bundle->getGenDependencies()) {
2763 // Now that writeResourceSymbols or writeAPK has taken care of writing
2764 // the targets to our dependency file, we'll write the prereqs
2765 fp = fopen(dependencyFile, "a+");
2766 fprintf(fp, " : ");
2767 bool includeRaw = (outputAPKFile != NULL);
2768 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2769 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2770 // and therefore was not added to our pathstores during slurping
2771 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2772 fclose(fp);
2773 }
2774
2775 retVal = 0;
2776bail:
2777 if (SourcePos::hasErrors()) {
2778 SourcePos::printErrors(stderr);
2779 }
2780 return retVal;
2781}
2782
2783/*
2784 * Do PNG Crunching
2785 * PRECONDITIONS
2786 * -S flag points to a source directory containing drawable* folders
2787 * -C flag points to destination directory. The folder structure in the
2788 * source directory will be mirrored to the destination (cache) directory
2789 *
2790 * POSTCONDITIONS
2791 * Destination directory will be updated to match the PNG files in
Maurice Chu2675f762013-10-22 17:33:11 -07002792 * the source directory.
Adam Lesinski282e1812014-01-23 18:17:42 -08002793 */
2794int doCrunch(Bundle* bundle)
2795{
2796 fprintf(stdout, "Crunching PNG Files in ");
2797 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2798 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2799
2800 updatePreProcessedCache(bundle);
2801
2802 return NO_ERROR;
2803}
2804
2805/*
2806 * Do PNG Crunching on a single flag
2807 * -i points to a single png file
2808 * -o points to a single png output file
2809 */
2810int doSingleCrunch(Bundle* bundle)
2811{
2812 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2813 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2814
2815 String8 input(bundle->getSingleCrunchInputFile());
2816 String8 output(bundle->getSingleCrunchOutputFile());
2817
2818 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2819 // we can't return the status_t as it gets truncate to the lower 8 bits.
2820 return 42;
2821 }
2822
2823 return NO_ERROR;
2824}
2825
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002826int runInDaemonMode(Bundle* bundle) {
2827 std::cout << "Ready" << std::endl;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002828 for (std::string cmd; std::getline(std::cin, cmd);) {
2829 if (cmd == "quit") {
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002830 return NO_ERROR;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002831 } else if (cmd == "s") {
2832 // Two argument crunch
2833 std::string inputFile, outputFile;
2834 std::getline(std::cin, inputFile);
2835 std::getline(std::cin, outputFile);
2836 bundle->setSingleCrunchInputFile(inputFile.c_str());
2837 bundle->setSingleCrunchOutputFile(outputFile.c_str());
2838 std::cout << "Crunching " << inputFile << std::endl;
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002839 if (doSingleCrunch(bundle) != NO_ERROR) {
2840 std::cout << "Error" << std::endl;
2841 }
2842 std::cout << "Done" << std::endl;
2843 } else {
2844 // in case of invalid command, just bail out.
2845 std::cerr << "Unknown command" << std::endl;
2846 return -1;
2847 }
2848 }
2849 return -1;
2850}
2851
Adam Lesinski282e1812014-01-23 18:17:42 -08002852char CONSOLE_DATA[2925] = {
2853 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2854 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2855 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2856 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2857 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2858 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2859 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2860 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2861 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2862 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2863 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2864 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2865 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2866 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2867 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2868 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2869 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2870 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2871 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2872 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2873 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2874 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2875 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2876 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2877 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2878 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2879 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2880 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2881 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2882 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2883 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2884 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2885 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2886 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2887 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2888 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2889 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2890 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2891 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2892 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2893 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2894 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2895 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2896 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2897 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2898 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2899 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2900 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2901 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2902 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2903 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2904 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2905 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2906 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2907 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2908 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2909 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2910 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2911 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2912 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2913 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2914 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2915 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2916 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2917 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2918 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2919 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2920 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2921 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2922 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2923 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2924 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2925 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2926 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2927 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2928 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2929 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2930 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2931 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2932 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2933 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2934 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2935 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2936 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2937 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2938 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2939 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2940 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2941 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2942 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2943 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2944 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2945 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2946 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2947 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2948 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2949 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2950 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2951 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2952 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2953 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2954 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2955 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2956 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2957 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2958 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2959 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2960 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2961 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2962 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2963 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2964 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2965 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2966 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2967 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2968 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2969 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2970 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2971 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2972 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2973 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2974 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2975 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2976 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2977 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2978 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2979 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2980 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2981 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2982 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2983 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2984 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2985 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2986 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2987 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2988 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2989 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2990 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2991 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2992 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2993 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2994 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2995 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2996 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2997 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2998 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2999 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
3000 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3001 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3002 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
3003 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
3004 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
3005 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
3006 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
3007 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3008 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3009 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
3010 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
3011 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3012 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3013 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3014 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3015 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
3016 };