blob: ba731801e507027599901b174236d49c01058577 [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 Lesinski5f3b2ec2015-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 Lesinski5f3b2ec2015-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 Lesinski5f3b2ec2015-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 Lesinski5f3b2ec2015-12-02 15:40:19 -0800561 idx = impliedFeatures->add(name8, ImpliedFeature(name8, sdk23));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700562 }
Adam Lesinski5f3b2ec2015-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 Lesinski5f3b2ec2015-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 Lesinski5f3b2ec2015-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 Lesinski5f3b2ec2015-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 Lesinski5f3b2ec2015-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 Lesinski5f3b2ec2015-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 Lesinski5f3b2ec2015-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 Lesinski5f3b2ec2015-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 Lesinski5f3b2ec2015-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 Lesinski5f3b2ec2015-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 Lesinski5f3b2ec2015-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 Lesinski5f3b2ec2015-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 Lesinski5f3b2ec2015-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 Lesinski5f3b2ec2015-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 Lesinski5f3b2ec2015-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 Lesinski5f3b2ec2015-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 Lesinski5f3b2ec2015-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 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
761 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
762 return 1;
763 }
764
Adam Lesinski57fe4832017-05-10 15:42:22 -0700765 // Now add any dependencies passed in.
766 for (size_t i = 0; i < bundle->getPackageIncludes().size(); i++) {
767 const String8& assetPath = bundle->getPackageIncludes()[i];
768 if (!assets.addAssetPath(assetPath, NULL)) {
769 fprintf(stderr, "ERROR: included asset path %s could not be loaded\n", assetPath.string());
770 return 1;
771 }
772 }
773
Adam Lesinski282e1812014-01-23 18:17:42 -0800774 // Make a dummy config for retrieving resources... we need to supply
775 // non-default values for some configs so that we can retrieve resources
776 // in the app that don't have a default. The most important of these is
777 // the API version because key resources like icons will have an implicit
778 // version if they are using newer config types like density.
779 ResTable_config config;
Narayan Kamath91447d82014-01-21 15:32:36 +0000780 memset(&config, 0, sizeof(ResTable_config));
Adam Lesinski282e1812014-01-23 18:17:42 -0800781 config.language[0] = 'e';
782 config.language[1] = 'n';
783 config.country[0] = 'U';
784 config.country[1] = 'S';
785 config.orientation = ResTable_config::ORIENTATION_PORT;
786 config.density = ResTable_config::DENSITY_MEDIUM;
787 config.sdkVersion = 10000; // Very high.
788 config.screenWidthDp = 320;
789 config.screenHeightDp = 480;
790 config.smallestScreenWidthDp = 320;
Adam Lesinskic2dea8d2014-08-04 16:40:41 -0700791 config.screenLayout |= ResTable_config::SCREENSIZE_NORMAL;
Adam Lesinski282e1812014-01-23 18:17:42 -0800792 assets.setConfiguration(config);
793
794 const ResTable& res = assets.getResources(false);
Dan Albert68001652014-09-09 09:51:01 -0700795 if (res.getError() != NO_ERROR) {
Adam Lesinski25e9d552014-05-19 15:01:43 -0700796 fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
Adam Lesinski63e646e2014-07-30 11:40:39 -0700797 return 1;
Adam Lesinski282e1812014-01-23 18:17:42 -0800798 }
799
Adam Lesinski694d0a72016-04-06 16:12:04 -0700800 // Source for AndroidManifest.xml
Adam Lesinski10de3af12016-07-13 10:14:03 -0700801 const String8 manifestFile("AndroidManifest.xml");
Adam Lesinski694d0a72016-04-06 16:12:04 -0700802
Adam Lesinski2cb761e2014-08-15 13:59:02 -0700803 // The dynamicRefTable can be null if there are no resources for this asset cookie.
804 // This fine.
Adam Lesinski63e646e2014-07-30 11:40:39 -0700805 const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700806
807 Asset* asset = NULL;
808
Adam Lesinski282e1812014-01-23 18:17:42 -0800809 if (strcmp("resources", option) == 0) {
Elliott Hughesba3fe562015-08-12 14:49:53 -0700810#ifndef __ANDROID__
Adam Lesinski282e1812014-01-23 18:17:42 -0800811 res.print(bundle->getValues());
812#endif
813
814 } else if (strcmp("strings", option) == 0) {
815 const ResStringPool* pool = res.getTableStringBlock(0);
816 printStringPool(pool);
817
818 } else if (strcmp("xmltree", option) == 0) {
819 if (bundle->getFileSpecCount() < 3) {
820 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
821 goto bail;
822 }
823
824 for (int i=2; i<bundle->getFileSpecCount(); i++) {
825 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700826 ResXMLTree tree(dynamicRefTable);
827 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800828 if (asset == NULL) {
Adam Lesinskifcb5f7b2016-11-02 13:17:10 -0700829 fprintf(stderr, "ERROR: dump failed because resource %s not found\n", resname);
Adam Lesinski282e1812014-01-23 18:17:42 -0800830 goto bail;
831 }
832
833 if (tree.setTo(asset->getBuffer(true),
834 asset->getLength()) != NO_ERROR) {
835 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
836 goto bail;
837 }
838 tree.restart();
839 printXMLBlock(&tree);
840 tree.uninit();
841 delete asset;
842 asset = NULL;
843 }
844
845 } else if (strcmp("xmlstrings", option) == 0) {
846 if (bundle->getFileSpecCount() < 3) {
847 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
848 goto bail;
849 }
850
851 for (int i=2; i<bundle->getFileSpecCount(); i++) {
852 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700853 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800854 if (asset == NULL) {
855 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
856 goto bail;
857 }
858
Adam Lesinski63e646e2014-07-30 11:40:39 -0700859 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800860 if (tree.setTo(asset->getBuffer(true),
861 asset->getLength()) != NO_ERROR) {
862 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
863 goto bail;
864 }
865 printStringPool(&tree.getStrings());
866 delete asset;
867 asset = NULL;
868 }
869
870 } else {
Adam Lesinski63e646e2014-07-30 11:40:39 -0700871 asset = assets.openNonAsset(assetsCookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800872 if (asset == NULL) {
873 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
874 goto bail;
875 }
876
Adam Lesinski63e646e2014-07-30 11:40:39 -0700877 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800878 if (tree.setTo(asset->getBuffer(true),
879 asset->getLength()) != NO_ERROR) {
880 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
881 goto bail;
882 }
883 tree.restart();
884
885 if (strcmp("permissions", option) == 0) {
886 size_t len;
887 ResXMLTree::event_code_t code;
888 int depth = 0;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800889 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
890 code != ResXMLTree::BAD_DOCUMENT) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800891 if (code == ResXMLTree::END_TAG) {
892 depth--;
893 continue;
894 }
895 if (code != ResXMLTree::START_TAG) {
896 continue;
897 }
898 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700899 const char16_t* ctag16 = tree.getElementName(&len);
900 if (ctag16 == NULL) {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700901 SourcePos(manifestFile, tree.getLineNumber()).error(
902 "ERROR: failed to get XML element name (bad string pool)");
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700903 goto bail;
904 }
905 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800906 //printf("Depth %d tag %s\n", depth, tag.string());
907 if (depth == 1) {
908 if (tag != "manifest") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700909 SourcePos(manifestFile, tree.getLineNumber()).error(
910 "ERROR: manifest does not start with <manifest> tag");
Adam Lesinski282e1812014-01-23 18:17:42 -0800911 goto bail;
912 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700913 String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -0700914 printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800915 } else if (depth == 2) {
916 if (tag == "permission") {
917 String8 error;
918 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
919 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700920 SourcePos(manifestFile, tree.getLineNumber()).error(
921 "ERROR getting 'android:name': %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800922 goto bail;
923 }
924
925 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700926 SourcePos(manifestFile, tree.getLineNumber()).error(
927 "ERROR: missing 'android:name' for permission");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800928 goto bail;
929 }
930 printf("permission: %s\n",
931 ResTable::normalizeForOutput(name.string()).string());
932 } else if (tag == "uses-permission") {
933 String8 error;
934 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
935 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700936 SourcePos(manifestFile, tree.getLineNumber()).error(
937 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800938 goto bail;
939 }
940
941 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700942 SourcePos(manifestFile, tree.getLineNumber()).error(
943 "ERROR: missing 'android:name' for uses-permission");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800944 goto bail;
945 }
946 printUsesPermission(name,
947 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
948 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
949 } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
950 String8 error;
951 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
952 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700953 SourcePos(manifestFile, tree.getLineNumber()).error(
954 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800955 goto bail;
956 }
957
958 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700959 SourcePos(manifestFile, tree.getLineNumber()).error(
960 "ERROR: missing 'android:name' for uses-permission-sdk-23");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800961 goto bail;
962 }
963 printUsesPermissionSdk23(
964 name,
965 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
Adam Lesinski282e1812014-01-23 18:17:42 -0800966 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800967 }
968 }
969 } else if (strcmp("badging", option) == 0) {
970 Vector<String8> locales;
971 res.getLocales(&locales);
972
973 Vector<ResTable_config> configs;
974 res.getConfigurations(&configs);
975 SortedVector<int> densities;
976 const size_t NC = configs.size();
977 for (size_t i=0; i<NC; i++) {
978 int dens = configs[i].density;
979 if (dens == 0) {
980 dens = 160;
981 }
982 densities.add(dens);
983 }
984
985 size_t len;
986 ResXMLTree::event_code_t code;
987 int depth = 0;
988 String8 error;
989 bool withinActivity = false;
990 bool isMainActivity = false;
991 bool isLauncherActivity = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800992 bool isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800993 bool isSearchable = false;
994 bool withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700995 bool withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700996 bool withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800997 bool withinReceiver = false;
998 bool withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700999 bool withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001000 bool withinIntentFilter = false;
1001 bool hasMainActivity = false;
1002 bool hasOtherActivities = false;
1003 bool hasOtherReceivers = false;
1004 bool hasOtherServices = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001005 bool hasIntentFilter = false;
1006
Adam Lesinski282e1812014-01-23 18:17:42 -08001007 bool hasWallpaperService = false;
1008 bool hasImeService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001009 bool hasAccessibilityService = false;
1010 bool hasPrintService = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001011 bool hasWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001012 bool hasDeviceAdminReceiver = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001013 bool hasPaymentService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001014 bool hasDocumentsProvider = false;
1015 bool hasCameraActivity = false;
1016 bool hasCameraSecureActivity = false;
1017 bool hasLauncher = false;
1018 bool hasNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001019 bool hasDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001020
Adam Lesinski282e1812014-01-23 18:17:42 -08001021 bool actMainActivity = false;
1022 bool actWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001023 bool actDeviceAdminEnabled = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001024 bool actImeService = false;
1025 bool actWallpaperService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001026 bool actAccessibilityService = false;
1027 bool actPrintService = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001028 bool actHostApduService = false;
1029 bool actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001030 bool actDocumentsProvider = false;
1031 bool actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001032 bool actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001033 bool actCamera = false;
1034 bool actCameraSecure = false;
1035 bool catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001036 bool hasMetaHostPaymentCategory = false;
1037 bool hasMetaOffHostPaymentCategory = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001038
1039 // These permissions are required by services implementing services
1040 // the system binds to (IME, Accessibility, PrintServices, etc.)
1041 bool hasBindDeviceAdminPermission = false;
1042 bool hasBindInputMethodPermission = false;
1043 bool hasBindAccessibilityServicePermission = false;
1044 bool hasBindPrintServicePermission = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001045 bool hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001046 bool hasRequiredSafAttributes = false;
1047 bool hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001048 bool hasBindDreamServicePermission = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001049
1050 // These two implement the implicit permissions that are granted
1051 // to pre-1.6 applications.
1052 bool hasWriteExternalStoragePermission = false;
Adam Lesinski2386df22016-12-28 15:08:58 -05001053 int32_t writeExternalStoragePermissionMaxSdkVersion = -1;
Adam Lesinski282e1812014-01-23 18:17:42 -08001054 bool hasReadPhoneStatePermission = false;
1055
1056 // If an app requests write storage, they will also get read storage.
1057 bool hasReadExternalStoragePermission = false;
1058
1059 // Implement transition to read and write call log.
1060 bool hasReadContactsPermission = false;
1061 bool hasWriteContactsPermission = false;
1062 bool hasReadCallLogPermission = false;
1063 bool hasWriteCallLogPermission = false;
1064
Adam Lesinskie47fd122014-08-15 22:25:36 -07001065 // If an app declares itself as multiArch, we report the
1066 // native libraries differently.
1067 bool hasMultiArch = false;
1068
Adam Lesinski282e1812014-01-23 18:17:42 -08001069 // This next group of variables is used to implement a group of
1070 // backward-compatibility heuristics necessitated by the addition of
1071 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
1072 // heuristic is "if an app requests a permission but doesn't explicitly
1073 // request the corresponding <uses-feature>, presume it's there anyway".
Adam Lesinski2c72b682014-06-24 09:56:01 -07001074
Adam Lesinski282e1812014-01-23 18:17:42 -08001075 // 2.2 also added some other features that apps can request, but that
1076 // have no corresponding permission, so we cannot implement any
1077 // back-compatibility heuristic for them. The below are thus unnecessary
1078 // (but are retained here for documentary purposes.)
1079 //bool specCompassFeature = false;
1080 //bool specAccelerometerFeature = false;
1081 //bool specProximityFeature = false;
1082 //bool specAmbientLightFeature = false;
1083 //bool specLiveWallpaperFeature = false;
1084
1085 int targetSdk = 0;
1086 int smallScreen = 1;
1087 int normalScreen = 1;
1088 int largeScreen = 1;
1089 int xlargeScreen = 1;
1090 int anyDensity = 1;
1091 int requiresSmallestWidthDp = 0;
1092 int compatibleWidthLimitDp = 0;
1093 int largestWidthLimitDp = 0;
1094 String8 pkg;
1095 String8 activityName;
1096 String8 activityLabel;
1097 String8 activityIcon;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001098 String8 activityBanner;
Adam Lesinski282e1812014-01-23 18:17:42 -08001099 String8 receiverName;
1100 String8 serviceName;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001101 Vector<String8> supportedInput;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001102
1103 FeatureGroup commonFeatures;
1104 Vector<FeatureGroup> featureGroups;
1105 KeyedVector<String8, ImpliedFeature> impliedFeatures;
1106
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001107 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
1108 code != ResXMLTree::BAD_DOCUMENT) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001109 if (code == ResXMLTree::END_TAG) {
1110 depth--;
1111 if (depth < 2) {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001112 if (withinSupportsInput && !supportedInput.isEmpty()) {
1113 printf("supports-input: '");
1114 const size_t N = supportedInput.size();
1115 for (size_t i=0; i<N; i++) {
Maurice Chu2675f762013-10-22 17:33:11 -07001116 printf("%s", ResTable::normalizeForOutput(
1117 supportedInput[i].string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001118 if (i != N - 1) {
1119 printf("' '");
1120 } else {
1121 printf("'\n");
1122 }
1123 }
1124 supportedInput.clear();
1125 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001126 withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001127 withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001128 withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001129 } else if (depth < 3) {
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001130 if (withinActivity && isMainActivity) {
Maurice Chu2675f762013-10-22 17:33:11 -07001131 String8 aName(getComponentName(pkg, activityName));
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001132 if (isLauncherActivity) {
1133 printf("launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001134 if (aName.length() > 0) {
1135 printf(" name='%s' ",
1136 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001137 }
1138 printf(" label='%s' icon='%s'\n",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001139 ResTable::normalizeForOutput(activityLabel.string())
1140 .string(),
1141 ResTable::normalizeForOutput(activityIcon.string())
1142 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001143 }
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001144 if (isLeanbackLauncherActivity) {
1145 printf("leanback-launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001146 if (aName.length() > 0) {
1147 printf(" name='%s' ",
1148 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001149 }
1150 printf(" label='%s' icon='%s' banner='%s'\n",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001151 ResTable::normalizeForOutput(activityLabel.string())
1152 .string(),
1153 ResTable::normalizeForOutput(activityIcon.string())
1154 .string(),
1155 ResTable::normalizeForOutput(activityBanner.string())
1156 .string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001157 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001158 }
1159 if (!hasIntentFilter) {
1160 hasOtherActivities |= withinActivity;
1161 hasOtherReceivers |= withinReceiver;
1162 hasOtherServices |= withinService;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001163 } else {
1164 if (withinService) {
1165 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
1166 hasBindNfcServicePermission);
1167 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
1168 hasBindNfcServicePermission);
1169 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001170 }
1171 withinActivity = false;
1172 withinService = false;
1173 withinReceiver = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001174 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001175 hasIntentFilter = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001176 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001177 } else if (depth < 4) {
1178 if (withinIntentFilter) {
1179 if (withinActivity) {
1180 hasMainActivity |= actMainActivity;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001181 hasLauncher |= catLauncher;
1182 hasCameraActivity |= actCamera;
1183 hasCameraSecureActivity |= actCameraSecure;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001184 hasOtherActivities |=
1185 !actMainActivity && !actCamera && !actCameraSecure;
Adam Lesinski282e1812014-01-23 18:17:42 -08001186 } else if (withinReceiver) {
1187 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001188 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
1189 hasBindDeviceAdminPermission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001190 hasOtherReceivers |=
1191 (!actWidgetReceivers && !actDeviceAdminEnabled);
Adam Lesinski282e1812014-01-23 18:17:42 -08001192 } else if (withinService) {
1193 hasImeService |= actImeService;
1194 hasWallpaperService |= actWallpaperService;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001195 hasAccessibilityService |= (actAccessibilityService &&
1196 hasBindAccessibilityServicePermission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001197 hasPrintService |=
1198 (actPrintService && hasBindPrintServicePermission);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001199 hasNotificationListenerService |= actNotificationListenerService &&
1200 hasBindNotificationListenerServicePermission;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001201 hasDreamService |= actDreamService && hasBindDreamServicePermission;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001202 hasOtherServices |= (!actImeService && !actWallpaperService &&
Adam Lesinski94fc9122013-09-30 17:16:09 -07001203 !actAccessibilityService && !actPrintService &&
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001204 !actHostApduService && !actOffHostApduService &&
1205 !actNotificationListenerService);
1206 } else if (withinProvider) {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001207 hasDocumentsProvider |=
1208 actDocumentsProvider && hasRequiredSafAttributes;
Adam Lesinski282e1812014-01-23 18:17:42 -08001209 }
1210 }
1211 withinIntentFilter = false;
1212 }
1213 continue;
1214 }
1215 if (code != ResXMLTree::START_TAG) {
1216 continue;
1217 }
1218 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001219
1220 const char16_t* ctag16 = tree.getElementName(&len);
1221 if (ctag16 == NULL) {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001222 SourcePos(manifestFile, tree.getLineNumber()).error(
1223 "ERROR: failed to get XML element name (bad string pool)");
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001224 goto bail;
1225 }
1226 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -08001227 //printf("Depth %d, %s\n", depth, tag.string());
1228 if (depth == 1) {
1229 if (tag != "manifest") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001230 SourcePos(manifestFile, tree.getLineNumber()).error(
1231 "ERROR: manifest does not start with <manifest> tag");
Adam Lesinski282e1812014-01-23 18:17:42 -08001232 goto bail;
1233 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001234 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -07001235 printf("package: name='%s' ",
1236 ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001237 int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR,
1238 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001239 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001240 SourcePos(manifestFile, tree.getLineNumber()).error(
1241 "ERROR getting 'android:versionCode' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001242 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001243 goto bail;
1244 }
1245 if (versionCode > 0) {
1246 printf("versionCode='%d' ", versionCode);
1247 } else {
1248 printf("versionCode='' ");
1249 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001250 String8 versionName = AaptXml::getResolvedAttribute(res, tree,
1251 VERSION_NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001252 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001253 SourcePos(manifestFile, tree.getLineNumber()).error(
1254 "ERROR getting 'android:versionName' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001255 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001256 goto bail;
1257 }
Adam Lesinski25d35a92014-08-11 09:41:56 -07001258 printf("versionName='%s'",
Maurice Chu2675f762013-10-22 17:33:11 -07001259 ResTable::normalizeForOutput(versionName.string()).string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001260
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001261 String8 splitName = AaptXml::getAttribute(tree, NULL, "split");
Adam Lesinski25d35a92014-08-11 09:41:56 -07001262 if (!splitName.isEmpty()) {
1263 printf(" split='%s'", ResTable::normalizeForOutput(
1264 splitName.string()).string());
1265 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001266
Adam Lesinski5283fab2014-08-29 11:23:55 -07001267 String8 platformVersionName = AaptXml::getAttribute(tree, NULL,
1268 "platformBuildVersionName");
1269 printf(" platformBuildVersionName='%s'", platformVersionName.string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001270 printf("\n");
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001271
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001272 int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
1273 INSTALL_LOCATION_ATTR, &error);
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001274 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001275 SourcePos(manifestFile, tree.getLineNumber()).error(
1276 "ERROR getting 'android:installLocation' attribute: %s",
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001277 error.string());
1278 goto bail;
1279 }
1280
1281 if (installLocation >= 0) {
1282 printf("install-location:'");
1283 switch (installLocation) {
1284 case 0:
1285 printf("auto");
1286 break;
1287 case 1:
1288 printf("internalOnly");
1289 break;
1290 case 2:
1291 printf("preferExternal");
1292 break;
1293 default:
1294 fprintf(stderr, "Invalid installLocation %d\n", installLocation);
1295 goto bail;
1296 }
1297 printf("'\n");
1298 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001299 } else if (depth == 2) {
1300 withinApplication = false;
1301 if (tag == "application") {
1302 withinApplication = true;
1303
1304 String8 label;
1305 const size_t NL = locales.size();
1306 for (size_t i=0; i<NL; i++) {
1307 const char* localeStr = locales[i].string();
Adam Lesinskia77685f2016-10-03 16:26:28 -07001308 assets.setConfiguration(config, localeStr != NULL ? localeStr : "");
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001309 String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1310 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001311 if (llabel != "") {
1312 if (localeStr == NULL || strlen(localeStr) == 0) {
1313 label = llabel;
Maurice Chu2675f762013-10-22 17:33:11 -07001314 printf("application-label:'%s'\n",
1315 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001316 } else {
1317 if (label == "") {
1318 label = llabel;
1319 }
1320 printf("application-label-%s:'%s'\n", localeStr,
Maurice Chu2675f762013-10-22 17:33:11 -07001321 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001322 }
1323 }
1324 }
1325
1326 ResTable_config tmpConfig = config;
1327 const size_t ND = densities.size();
1328 for (size_t i=0; i<ND; i++) {
1329 tmpConfig.density = densities[i];
1330 assets.setConfiguration(tmpConfig);
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001331 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1332 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001333 if (icon != "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001334 printf("application-icon-%d:'%s'\n", densities[i],
1335 ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001336 }
1337 }
1338 assets.setConfiguration(config);
1339
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001340 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001341 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001342 SourcePos(manifestFile, tree.getLineNumber()).error(
1343 "ERROR getting 'android:icon' attribute: %s", error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001344 goto bail;
1345 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001346 int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0,
1347 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001348 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001349 SourcePos(manifestFile, tree.getLineNumber()).error(
1350 "ERROR getting 'android:testOnly' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001351 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001352 goto bail;
1353 }
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001354
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001355 String8 banner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1356 &error);
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001357 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001358 SourcePos(manifestFile, tree.getLineNumber()).error(
1359 "ERROR getting 'android:banner' attribute: %s", error.string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001360 goto bail;
1361 }
Maurice Chu2675f762013-10-22 17:33:11 -07001362 printf("application: label='%s' ",
1363 ResTable::normalizeForOutput(label.string()).string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001364 printf("icon='%s'", ResTable::normalizeForOutput(icon.string()).string());
1365 if (banner != "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001366 printf(" banner='%s'",
1367 ResTable::normalizeForOutput(banner.string()).string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001368 }
1369 printf("\n");
Adam Lesinski282e1812014-01-23 18:17:42 -08001370 if (testOnly != 0) {
1371 printf("testOnly='%d'\n", testOnly);
1372 }
1373
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001374 int32_t isGame = AaptXml::getResolvedIntegerAttribute(res, tree,
1375 ISGAME_ATTR, 0, &error);
1376 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001377 SourcePos(manifestFile, tree.getLineNumber()).error(
1378 "ERROR getting 'android:isGame' attribute: %s", error.string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001379 goto bail;
1380 }
1381 if (isGame != 0) {
1382 printf("application-isGame\n");
1383 }
1384
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001385 int32_t debuggable = AaptXml::getResolvedIntegerAttribute(res, tree,
1386 DEBUGGABLE_ATTR, 0, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001387 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001388 SourcePos(manifestFile, tree.getLineNumber()).error(
1389 "ERROR getting 'android:debuggable' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001390 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001391 goto bail;
1392 }
1393 if (debuggable != 0) {
1394 printf("application-debuggable\n");
1395 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07001396
1397 // We must search by name because the multiArch flag hasn't been API
1398 // frozen yet.
1399 int32_t multiArchIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
1400 "multiArch");
1401 if (multiArchIndex >= 0) {
1402 Res_value value;
1403 if (tree.getAttributeValue(multiArchIndex, &value) != NO_ERROR) {
1404 if (value.dataType >= Res_value::TYPE_FIRST_INT &&
1405 value.dataType <= Res_value::TYPE_LAST_INT) {
1406 hasMultiArch = value.data;
1407 }
1408 }
1409 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001410 } else if (tag == "uses-sdk") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001411 int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR,
1412 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001413 if (error != "") {
1414 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001415 String8 name = AaptXml::getResolvedAttribute(res, tree,
1416 MIN_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001417 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001418 SourcePos(manifestFile, tree.getLineNumber()).error(
1419 "ERROR getting 'android:minSdkVersion' attribute: %s",
Adam Lesinski282e1812014-01-23 18:17:42 -08001420 error.string());
1421 goto bail;
1422 }
1423 if (name == "Donut") targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001424 printf("sdkVersion:'%s'\n",
1425 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001426 } else if (code != -1) {
1427 targetSdk = code;
1428 printf("sdkVersion:'%d'\n", code);
1429 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001430 code = AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -08001431 if (code != -1) {
1432 printf("maxSdkVersion:'%d'\n", code);
1433 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001434 code = AaptXml::getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001435 if (error != "") {
1436 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001437 String8 name = AaptXml::getResolvedAttribute(res, tree,
1438 TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001439 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001440 SourcePos(manifestFile, tree.getLineNumber()).error(
1441 "ERROR getting 'android:targetSdkVersion' attribute: %s",
Adam Lesinski282e1812014-01-23 18:17:42 -08001442 error.string());
1443 goto bail;
1444 }
1445 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001446 printf("targetSdkVersion:'%s'\n",
1447 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001448 } else if (code != -1) {
1449 if (targetSdk < code) {
1450 targetSdk = code;
1451 }
1452 printf("targetSdkVersion:'%d'\n", code);
1453 }
1454 } else if (tag == "uses-configuration") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001455 int32_t reqTouchScreen = AaptXml::getIntegerAttribute(tree,
1456 REQ_TOUCH_SCREEN_ATTR, 0);
1457 int32_t reqKeyboardType = AaptXml::getIntegerAttribute(tree,
1458 REQ_KEYBOARD_TYPE_ATTR, 0);
1459 int32_t reqHardKeyboard = AaptXml::getIntegerAttribute(tree,
1460 REQ_HARD_KEYBOARD_ATTR, 0);
1461 int32_t reqNavigation = AaptXml::getIntegerAttribute(tree,
1462 REQ_NAVIGATION_ATTR, 0);
1463 int32_t reqFiveWayNav = AaptXml::getIntegerAttribute(tree,
1464 REQ_FIVE_WAY_NAV_ATTR, 0);
Adam Lesinski282e1812014-01-23 18:17:42 -08001465 printf("uses-configuration:");
1466 if (reqTouchScreen != 0) {
1467 printf(" reqTouchScreen='%d'", reqTouchScreen);
1468 }
1469 if (reqKeyboardType != 0) {
1470 printf(" reqKeyboardType='%d'", reqKeyboardType);
1471 }
1472 if (reqHardKeyboard != 0) {
1473 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1474 }
1475 if (reqNavigation != 0) {
1476 printf(" reqNavigation='%d'", reqNavigation);
1477 }
1478 if (reqFiveWayNav != 0) {
1479 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1480 }
1481 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001482 } else if (tag == "supports-input") {
1483 withinSupportsInput = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001484 } else if (tag == "supports-screens") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001485 smallScreen = AaptXml::getIntegerAttribute(tree,
1486 SMALL_SCREEN_ATTR, 1);
1487 normalScreen = AaptXml::getIntegerAttribute(tree,
1488 NORMAL_SCREEN_ATTR, 1);
1489 largeScreen = AaptXml::getIntegerAttribute(tree,
1490 LARGE_SCREEN_ATTR, 1);
1491 xlargeScreen = AaptXml::getIntegerAttribute(tree,
1492 XLARGE_SCREEN_ATTR, 1);
1493 anyDensity = AaptXml::getIntegerAttribute(tree,
1494 ANY_DENSITY_ATTR, 1);
1495 requiresSmallestWidthDp = AaptXml::getIntegerAttribute(tree,
1496 REQUIRES_SMALLEST_WIDTH_DP_ATTR, 0);
1497 compatibleWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1498 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, 0);
1499 largestWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1500 LARGEST_WIDTH_LIMIT_DP_ATTR, 0);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001501 } else if (tag == "feature-group") {
1502 withinFeatureGroup = true;
1503 FeatureGroup group;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001504 group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001505 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001506 SourcePos(manifestFile, tree.getLineNumber()).error(
1507 "ERROR getting 'android:label' attribute: %s", error.string());
Adam Lesinski2c72b682014-06-24 09:56:01 -07001508 goto bail;
1509 }
1510 featureGroups.add(group);
1511
Adam Lesinski282e1812014-01-23 18:17:42 -08001512 } else if (tag == "uses-feature") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001513 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001514 if (name != "" && error == "") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001515 const char* androidSchema =
1516 "http://schemas.android.com/apk/res/android";
Adam Lesinski282e1812014-01-23 18:17:42 -08001517
Adam Lesinski694d0a72016-04-06 16:12:04 -07001518 int32_t req = AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1,
1519 &error);
1520 if (error != "") {
1521 SourcePos(manifestFile, tree.getLineNumber()).error(
1522 "failed to read attribute 'android:required': %s",
1523 error.string());
1524 goto bail;
1525 }
1526
1527 int32_t version = AaptXml::getIntegerAttribute(tree, androidSchema,
1528 "version", 0, &error);
1529 if (error != "") {
1530 SourcePos(manifestFile, tree.getLineNumber()).error(
1531 "failed to read attribute 'android:version': %s",
1532 error.string());
1533 goto bail;
1534 }
1535
1536 commonFeatures.features.add(name, Feature(req != 0, version));
Adam Lesinski2c72b682014-06-24 09:56:01 -07001537 if (req) {
1538 addParentFeatures(&commonFeatures, name);
Adam Lesinski282e1812014-01-23 18:17:42 -08001539 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001540 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001541 int vers = AaptXml::getIntegerAttribute(tree,
Adam Lesinski282e1812014-01-23 18:17:42 -08001542 GL_ES_VERSION_ATTR, &error);
1543 if (error == "") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001544 if (vers > commonFeatures.openGLESVersion) {
1545 commonFeatures.openGLESVersion = vers;
1546 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001547 }
1548 }
1549 } else if (tag == "uses-permission") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001550 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001551 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001552 SourcePos(manifestFile, tree.getLineNumber()).error(
1553 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001554 goto bail;
1555 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001556
1557 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001558 SourcePos(manifestFile, tree.getLineNumber()).error(
1559 "ERROR: missing 'android:name' for uses-permission");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001560 goto bail;
1561 }
1562
1563 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, false);
1564
Adam Lesinski2386df22016-12-28 15:08:58 -05001565 const int32_t maxSdkVersion =
1566 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, -1);
Dianne Hackborncd154e92017-02-28 17:37:35 -08001567 const String8 requiredFeature = AaptXml::getAttribute(tree,
1568 REQUIRED_FEATURE_ATTR, &error);
1569 const String8 requiredNotFeature = AaptXml::getAttribute(tree,
1570 REQUIRED_NOT_FEATURE_ATTR, &error);
Adam Lesinski2386df22016-12-28 15:08:58 -05001571
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001572 if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1573 hasWriteExternalStoragePermission = true;
Adam Lesinski2386df22016-12-28 15:08:58 -05001574 writeExternalStoragePermissionMaxSdkVersion = maxSdkVersion;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001575 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1576 hasReadExternalStoragePermission = true;
1577 } else if (name == "android.permission.READ_PHONE_STATE") {
1578 hasReadPhoneStatePermission = true;
1579 } else if (name == "android.permission.READ_CONTACTS") {
1580 hasReadContactsPermission = true;
1581 } else if (name == "android.permission.WRITE_CONTACTS") {
1582 hasWriteContactsPermission = true;
1583 } else if (name == "android.permission.READ_CALL_LOG") {
1584 hasReadCallLogPermission = true;
1585 } else if (name == "android.permission.WRITE_CALL_LOG") {
1586 hasWriteCallLogPermission = true;
1587 }
1588
1589 printUsesPermission(name,
1590 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
Dianne Hackborncd154e92017-02-28 17:37:35 -08001591 maxSdkVersion, requiredFeature, requiredNotFeature);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001592
1593 } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
1594 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1595 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001596 SourcePos(manifestFile, tree.getLineNumber()).error(
1597 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001598 goto bail;
1599 }
1600
1601 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001602 SourcePos(manifestFile, tree.getLineNumber()).error(
1603 "ERROR: missing 'android:name' for uses-permission-sdk-23");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001604 goto bail;
1605 }
1606
1607 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, true);
1608
1609 printUsesPermissionSdk23(
1610 name, AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
1611
Adam Lesinski282e1812014-01-23 18:17:42 -08001612 } else if (tag == "uses-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001613 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001614 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001615 printf("uses-package:'%s'\n",
1616 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001617 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001618 SourcePos(manifestFile, tree.getLineNumber()).error(
1619 "ERROR getting 'android:name' attribute: %s", error.string());
1620 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001621 }
1622 } else if (tag == "original-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001623 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001624 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001625 printf("original-package:'%s'\n",
1626 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001627 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001628 SourcePos(manifestFile, tree.getLineNumber()).error(
1629 "ERROR getting 'android:name' attribute: %s", error.string());
1630 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001631 }
1632 } else if (tag == "supports-gl-texture") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001633 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001634 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001635 printf("supports-gl-texture:'%s'\n",
1636 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001637 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001638 SourcePos(manifestFile, tree.getLineNumber()).error(
1639 "ERROR getting 'android:name' attribute: %s", error.string());
1640 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001641 }
1642 } else if (tag == "compatible-screens") {
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001643 printCompatibleScreens(tree, &error);
1644 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001645 SourcePos(manifestFile, tree.getLineNumber()).error(
1646 "ERROR getting compatible screens: %s", error.string());
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001647 goto bail;
1648 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001649 depth--;
1650 } else if (tag == "package-verifier") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001651 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001652 if (name != "" && error == "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001653 String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR,
1654 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001655 if (publicKey != "" && error == "") {
1656 printf("package-verifier: name='%s' publicKey='%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001657 ResTable::normalizeForOutput(name.string()).string(),
1658 ResTable::normalizeForOutput(publicKey.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001659 }
1660 }
1661 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001662 } else if (depth == 3) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001663 withinActivity = false;
1664 withinReceiver = false;
1665 withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001666 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001667 hasIntentFilter = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001668 hasMetaHostPaymentCategory = false;
1669 hasMetaOffHostPaymentCategory = false;
1670 hasBindDeviceAdminPermission = false;
1671 hasBindInputMethodPermission = false;
1672 hasBindAccessibilityServicePermission = false;
1673 hasBindPrintServicePermission = false;
1674 hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001675 hasRequiredSafAttributes = false;
1676 hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001677 hasBindDreamServicePermission = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001678 if (withinApplication) {
1679 if(tag == "activity") {
1680 withinActivity = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001681 activityName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001682 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001683 SourcePos(manifestFile, tree.getLineNumber()).error(
1684 "ERROR getting 'android:name' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001685 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001686 goto bail;
1687 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001688
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001689 activityLabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1690 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001691 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001692 SourcePos(manifestFile, tree.getLineNumber()).error(
1693 "ERROR getting 'android:label' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001694 error.string());
1695 goto bail;
1696 }
1697
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001698 activityIcon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1699 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001700 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001701 SourcePos(manifestFile, tree.getLineNumber()).error(
1702 "ERROR getting 'android:icon' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001703 error.string());
1704 goto bail;
1705 }
1706
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001707 activityBanner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1708 &error);
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001709 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001710 SourcePos(manifestFile, tree.getLineNumber()).error(
1711 "ERROR getting 'android:banner' attribute: %s",
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001712 error.string());
1713 goto bail;
1714 }
1715
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001716 int32_t orien = AaptXml::getResolvedIntegerAttribute(res, tree,
Michael Wrightec4fdec2013-09-06 16:50:52 -07001717 SCREEN_ORIENTATION_ATTR, &error);
1718 if (error == "") {
1719 if (orien == 0 || orien == 6 || orien == 8) {
1720 // Requests landscape, sensorLandscape, or reverseLandscape.
Adam Lesinski43158772015-11-11 15:13:55 -08001721 addImpliedFeature(
1722 &impliedFeatures, "android.hardware.screen.landscape",
1723 String8("one or more activities have specified a "
1724 "landscape orientation"),
1725 false);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001726 } else if (orien == 1 || orien == 7 || orien == 9) {
1727 // Requests portrait, sensorPortrait, or reversePortrait.
Adam Lesinski43158772015-11-11 15:13:55 -08001728 addImpliedFeature(
1729 &impliedFeatures, "android.hardware.screen.portrait",
1730 String8("one or more activities have specified a "
1731 "portrait orientation"),
1732 false);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001733 }
1734 }
1735 } else if (tag == "uses-library") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001736 String8 libraryName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001737 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001738 SourcePos(manifestFile, tree.getLineNumber()).error(
Michael Wrightec4fdec2013-09-06 16:50:52 -07001739 "ERROR getting 'android:name' attribute for uses-library"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001740 " %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001741 goto bail;
1742 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001743 int req = AaptXml::getIntegerAttribute(tree,
1744 REQUIRED_ATTR, 1);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001745 printf("uses-library%s:'%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001746 req ? "" : "-not-required", ResTable::normalizeForOutput(
1747 libraryName.string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001748 } else if (tag == "receiver") {
1749 withinReceiver = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001750 receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001751
1752 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001753 SourcePos(manifestFile, tree.getLineNumber()).error(
Michael Wrightec4fdec2013-09-06 16:50:52 -07001754 "ERROR getting 'android:name' attribute for receiver:"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001755 " %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001756 goto bail;
1757 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001758
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001759 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1760 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001761 if (error == "") {
1762 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1763 hasBindDeviceAdminPermission = true;
1764 }
1765 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001766 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001767 "ERROR getting 'android:permission' attribute for"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001768 " receiver '%s': %s",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001769 receiverName.string(), error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001770 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001771 } else if (tag == "service") {
1772 withinService = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001773 serviceName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001774
1775 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001776 SourcePos(manifestFile, tree.getLineNumber()).error(
1777 "ERROR getting 'android:name' attribute for "
1778 "service:%s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001779 goto bail;
1780 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001781
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001782 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1783 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001784 if (error == "") {
1785 if (permission == "android.permission.BIND_INPUT_METHOD") {
1786 hasBindInputMethodPermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001787 } else if (permission ==
1788 "android.permission.BIND_ACCESSIBILITY_SERVICE") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001789 hasBindAccessibilityServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001790 } else if (permission ==
1791 "android.permission.BIND_PRINT_SERVICE") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001792 hasBindPrintServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001793 } else if (permission ==
1794 "android.permission.BIND_NFC_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07001795 hasBindNfcServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001796 } else if (permission ==
1797 "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001798 hasBindNotificationListenerServicePermission = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001799 } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
1800 hasBindDreamServicePermission = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001801 }
1802 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001803 SourcePos(manifestFile, tree.getLineNumber()).error(
1804 "ERROR getting 'android:permission' attribute for "
1805 "service '%s': %s", serviceName.string(), error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001806 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001807 } else if (tag == "provider") {
1808 withinProvider = true;
1809
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001810 bool exported = AaptXml::getResolvedIntegerAttribute(res, tree,
1811 EXPORTED_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001812 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001813 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001814 "ERROR getting 'android:exported' attribute for provider:"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001815 " %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001816 goto bail;
1817 }
1818
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001819 bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute(
1820 res, tree, GRANT_URI_PERMISSIONS_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001821 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001822 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001823 "ERROR getting 'android:grantUriPermissions' attribute for "
Adam Lesinski10de3af12016-07-13 10:14:03 -07001824 "provider: %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001825 goto bail;
1826 }
1827
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001828 String8 permission = AaptXml::getResolvedAttribute(res, tree,
1829 PERMISSION_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001830 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001831 SourcePos(manifestFile, tree.getLineNumber()).error(
1832 "ERROR getting 'android:permission' attribute for "
1833 "provider: %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001834 goto bail;
1835 }
1836
1837 hasRequiredSafAttributes |= exported && grantUriPermissions &&
1838 permission == "android.permission.MANAGE_DOCUMENTS";
1839
Michael Wrightec4fdec2013-09-06 16:50:52 -07001840 } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001841 String8 metaDataName = AaptXml::getResolvedAttribute(res, tree,
1842 NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001843 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001844 SourcePos(manifestFile, tree.getLineNumber()).error(
1845 "ERROR getting 'android:name' attribute for "
1846 "meta-data: %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001847 goto bail;
1848 }
Maurice Chu2675f762013-10-22 17:33:11 -07001849 printf("meta-data: name='%s' ",
1850 ResTable::normalizeForOutput(metaDataName.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001851 printResolvedResourceAttribute(res, tree, VALUE_ATTR, String8("value"),
Maurice Chu76327312013-10-16 18:28:46 -07001852 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001853 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001854 // Try looking for a RESOURCE_ATTR
1855 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001856 printResolvedResourceAttribute(res, tree, RESOURCE_ATTR,
Maurice Chu76327312013-10-16 18:28:46 -07001857 String8("resource"), &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001858 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001859 SourcePos(manifestFile, tree.getLineNumber()).error(
1860 "ERROR getting 'android:value' or "
Maurice Chu76327312013-10-16 18:28:46 -07001861 "'android:resource' attribute for "
Adam Lesinski10de3af12016-07-13 10:14:03 -07001862 "meta-data: %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001863 goto bail;
1864 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001865 }
Maurice Chu76327312013-10-16 18:28:46 -07001866 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001867 } else if (withinSupportsInput && tag == "input-type") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001868 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001869 if (name != "" && error == "") {
1870 supportedInput.add(name);
1871 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001872 SourcePos(manifestFile, tree.getLineNumber()).error(
1873 "ERROR getting 'android:name' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001874 error.string());
1875 goto bail;
1876 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001877 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001878 } else if (withinFeatureGroup && tag == "uses-feature") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001879 const String8 androidSchema("http://schemas.android.com/apk/res/android");
Adam Lesinski2c72b682014-06-24 09:56:01 -07001880 FeatureGroup& top = featureGroups.editTop();
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001881
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001882 String8 name = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001883 if (name != "" && error == "") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001884 Feature feature(true);
1885
1886 int32_t featureVers = AaptXml::getIntegerAttribute(
1887 tree, androidSchema.string(), "version", 0, &error);
1888 if (error == "") {
1889 feature.version = featureVers;
1890 } else {
1891 SourcePos(manifestFile, tree.getLineNumber()).error(
1892 "failed to read attribute 'android:version': %s",
1893 error.string());
1894 goto bail;
1895 }
1896
1897 top.features.add(name, feature);
Adam Lesinskid3edfde2014-08-08 17:32:44 -07001898 addParentFeatures(&top, name);
Adam Lesinski694d0a72016-04-06 16:12:04 -07001899
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001900 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001901 int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR,
1902 &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001903 if (error == "") {
1904 if (vers > top.openGLESVersion) {
1905 top.openGLESVersion = vers;
1906 }
1907 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001908 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001909 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001910 } else if (depth == 4) {
1911 if (tag == "intent-filter") {
1912 hasIntentFilter = true;
1913 withinIntentFilter = true;
1914 actMainActivity = false;
1915 actWidgetReceivers = false;
1916 actImeService = false;
1917 actWallpaperService = false;
1918 actAccessibilityService = false;
1919 actPrintService = false;
1920 actDeviceAdminEnabled = false;
1921 actHostApduService = false;
1922 actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001923 actDocumentsProvider = false;
1924 actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001925 actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001926 actCamera = false;
1927 actCameraSecure = false;
1928 catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001929 } else if (withinService && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001930 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001931 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001932 SourcePos(manifestFile, tree.getLineNumber()).error(
1933 "ERROR getting 'android:name' attribute for "
1934 "meta-data tag in service '%s': %s", serviceName.string(),
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001935 error.string());
Adam Lesinski94fc9122013-09-30 17:16:09 -07001936 goto bail;
1937 }
1938
1939 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1940 name == "android.nfc.cardemulation.off_host_apdu_service") {
1941 bool offHost = true;
1942 if (name == "android.nfc.cardemulation.host_apdu_service") {
1943 offHost = false;
1944 }
1945
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001946 String8 xmlPath = AaptXml::getResolvedAttribute(res, tree,
1947 RESOURCE_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001948 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001949 SourcePos(manifestFile, tree.getLineNumber()).error(
1950 "ERROR getting 'android:resource' attribute for "
1951 "meta-data tag in service '%s': %s",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001952 serviceName.string(), error.string());
Adam Lesinski94fc9122013-09-30 17:16:09 -07001953 goto bail;
1954 }
1955
1956 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1957 offHost, &error);
1958 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001959 SourcePos(manifestFile, tree.getLineNumber()).error(
1960 "ERROR getting AID category for service '%s'",
Adam Lesinski94fc9122013-09-30 17:16:09 -07001961 serviceName.string());
1962 goto bail;
1963 }
1964
1965 const size_t catLen = categories.size();
1966 for (size_t i = 0; i < catLen; i++) {
1967 bool paymentCategory = (categories[i] == "payment");
1968 if (offHost) {
1969 hasMetaOffHostPaymentCategory |= paymentCategory;
1970 } else {
1971 hasMetaHostPaymentCategory |= paymentCategory;
1972 }
1973 }
1974 }
1975 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001976 } else if ((depth == 5) && withinIntentFilter) {
1977 String8 action;
1978 if (tag == "action") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001979 action = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001980 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001981 SourcePos(manifestFile, tree.getLineNumber()).error(
1982 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001983 goto bail;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001984 }
1985
Adam Lesinskia5018c92013-09-30 16:23:15 -07001986 if (withinActivity) {
1987 if (action == "android.intent.action.MAIN") {
1988 isMainActivity = true;
1989 actMainActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001990 } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
1991 action == "android.media.action.VIDEO_CAMERA") {
1992 actCamera = true;
1993 } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
1994 actCameraSecure = true;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001995 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001996 } else if (withinReceiver) {
1997 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1998 actWidgetReceivers = true;
1999 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
2000 actDeviceAdminEnabled = true;
2001 }
2002 } else if (withinService) {
2003 if (action == "android.view.InputMethod") {
2004 actImeService = true;
2005 } else if (action == "android.service.wallpaper.WallpaperService") {
2006 actWallpaperService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002007 } else if (action ==
2008 "android.accessibilityservice.AccessibilityService") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07002009 actAccessibilityService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002010 } else if (action =="android.printservice.PrintService") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07002011 actPrintService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002012 } else if (action ==
2013 "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07002014 actHostApduService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002015 } else if (action ==
2016 "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07002017 actOffHostApduService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002018 } else if (action ==
2019 "android.service.notification.NotificationListenerService") {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002020 actNotificationListenerService = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04002021 } else if (action == "android.service.dreams.DreamService") {
2022 actDreamService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002023 }
2024 } else if (withinProvider) {
2025 if (action == "android.content.action.DOCUMENTS_PROVIDER") {
2026 actDocumentsProvider = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07002027 }
2028 }
2029 if (action == "android.intent.action.SEARCH") {
2030 isSearchable = true;
2031 }
2032 }
2033
2034 if (tag == "category") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07002035 String8 category = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07002036 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07002037 SourcePos(manifestFile, tree.getLineNumber()).error(
2038 "ERROR getting 'name' attribute: %s", error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07002039 goto bail;
2040 }
2041 if (withinActivity) {
2042 if (category == "android.intent.category.LAUNCHER") {
2043 isLauncherActivity = true;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08002044 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
2045 isLeanbackLauncherActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002046 } else if (category == "android.intent.category.HOME") {
2047 catLauncher = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08002048 }
2049 }
2050 }
2051 }
2052 }
2053
2054 // Pre-1.6 implicitly granted permission compatibility logic
2055 if (targetSdk < 4) {
2056 if (!hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002057 printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
2058 printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
2059 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002060 hasWriteExternalStoragePermission = true;
2061 }
2062 if (!hasReadPhoneStatePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002063 printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
2064 printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
2065 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002066 }
2067 }
2068
2069 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
2070 // force them to always take READ_EXTERNAL_STORAGE as well. We always
2071 // do this (regardless of target API version) because we can't have
2072 // an app with write permission but not read permission.
2073 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
Adam Lesinski2386df22016-12-28 15:08:58 -05002074 printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
2075 false /* optional */, writeExternalStoragePermissionMaxSdkVersion);
Adam Lesinski58f1f362013-11-12 12:59:08 -08002076 printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
Adam Lesinski2386df22016-12-28 15:08:58 -05002077 String8("requested WRITE_EXTERNAL_STORAGE"),
2078 writeExternalStoragePermissionMaxSdkVersion);
Adam Lesinski282e1812014-01-23 18:17:42 -08002079 }
2080
2081 // Pre-JellyBean call log permission compatibility.
2082 if (targetSdk < 16) {
2083 if (!hasReadCallLogPermission && hasReadContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002084 printUsesPermission(String8("android.permission.READ_CALL_LOG"));
2085 printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
2086 String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002087 }
2088 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002089 printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
2090 printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
2091 String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002092 }
2093 }
2094
Adam Lesinskica955a42016-08-01 16:44:29 -07002095 // If the app hasn't declared the touchscreen as a feature requirement (either
2096 // directly or implied, required or not), then the faketouch feature is implied.
2097 if (!hasFeature("android.hardware.touchscreen", commonFeatures, impliedFeatures)) {
2098 addImpliedFeature(&impliedFeatures, "android.hardware.faketouch",
Adam Lesinski43158772015-11-11 15:13:55 -08002099 String8("default feature for all apps"), false);
Adam Lesinskica955a42016-08-01 16:44:29 -07002100 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07002101
2102 const size_t numFeatureGroups = featureGroups.size();
2103 if (numFeatureGroups == 0) {
2104 // If no <feature-group> tags were defined, apply auto-implied features.
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002105 printDefaultFeatureGroup(commonFeatures, impliedFeatures);
Adam Lesinski2c72b682014-06-24 09:56:01 -07002106
2107 } else {
2108 // <feature-group> tags are defined, so we ignore implied features and
2109 for (size_t i = 0; i < numFeatureGroups; i++) {
2110 FeatureGroup& grp = featureGroups.editItemAt(i);
2111
Adam Lesinskid7a94da2014-07-25 14:38:54 -07002112 if (commonFeatures.openGLESVersion > grp.openGLESVersion) {
2113 grp.openGLESVersion = commonFeatures.openGLESVersion;
2114 }
2115
Adam Lesinski2c72b682014-06-24 09:56:01 -07002116 // Merge the features defined in the top level (not inside a <feature-group>)
2117 // with this feature group.
2118 const size_t numCommonFeatures = commonFeatures.features.size();
2119 for (size_t j = 0; j < numCommonFeatures; j++) {
2120 if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07002121 grp.features.add(commonFeatures.features.keyAt(j),
2122 commonFeatures.features[j]);
Adam Lesinski2c72b682014-06-24 09:56:01 -07002123 }
2124 }
2125
Adam Lesinski73a05112014-12-08 12:53:17 -08002126 if (!grp.features.isEmpty()) {
Adam Lesinski2c72b682014-06-24 09:56:01 -07002127 printFeatureGroup(grp);
Adam Lesinski282e1812014-01-23 18:17:42 -08002128 }
2129 }
2130 }
2131
Adam Lesinski282e1812014-01-23 18:17:42 -08002132
Adam Lesinski282e1812014-01-23 18:17:42 -08002133 if (hasWidgetReceivers) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002134 printComponentPresence("app-widget");
Adam Lesinski282e1812014-01-23 18:17:42 -08002135 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002136 if (hasDeviceAdminReceiver) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002137 printComponentPresence("device-admin");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002138 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002139 if (hasImeService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002140 printComponentPresence("ime");
Adam Lesinski282e1812014-01-23 18:17:42 -08002141 }
2142 if (hasWallpaperService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002143 printComponentPresence("wallpaper");
Adam Lesinski282e1812014-01-23 18:17:42 -08002144 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002145 if (hasAccessibilityService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002146 printComponentPresence("accessibility");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002147 }
2148 if (hasPrintService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002149 printComponentPresence("print-service");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002150 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07002151 if (hasPaymentService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002152 printComponentPresence("payment");
2153 }
2154 if (isSearchable) {
2155 printComponentPresence("search");
2156 }
2157 if (hasDocumentsProvider) {
2158 printComponentPresence("document-provider");
2159 }
2160 if (hasLauncher) {
2161 printComponentPresence("launcher");
2162 }
2163 if (hasNotificationListenerService) {
2164 printComponentPresence("notification-listener");
2165 }
John Spurlockeb8d1be2014-06-25 17:46:15 -04002166 if (hasDreamService) {
2167 printComponentPresence("dream");
2168 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002169 if (hasCameraActivity) {
2170 printComponentPresence("camera");
2171 }
2172 if (hasCameraSecureActivity) {
2173 printComponentPresence("camera-secure");
2174 }
2175
2176 if (hasMainActivity) {
2177 printf("main\n");
Adam Lesinski94fc9122013-09-30 17:16:09 -07002178 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002179 if (hasOtherActivities) {
2180 printf("other-activities\n");
2181 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002182 if (hasOtherReceivers) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002183 printf("other-receivers\n");
2184 }
2185 if (hasOtherServices) {
2186 printf("other-services\n");
2187 }
2188
2189 // For modern apps, if screen size buckets haven't been specified
2190 // but the new width ranges have, then infer the buckets from them.
2191 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
2192 && requiresSmallestWidthDp > 0) {
2193 int compatWidth = compatibleWidthLimitDp;
2194 if (compatWidth <= 0) {
2195 compatWidth = requiresSmallestWidthDp;
2196 }
2197 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
2198 smallScreen = -1;
2199 } else {
2200 smallScreen = 0;
2201 }
2202 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
2203 normalScreen = -1;
2204 } else {
2205 normalScreen = 0;
2206 }
2207 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
2208 largeScreen = -1;
2209 } else {
2210 largeScreen = 0;
2211 }
2212 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
2213 xlargeScreen = -1;
2214 } else {
2215 xlargeScreen = 0;
2216 }
2217 }
2218
2219 // Determine default values for any unspecified screen sizes,
2220 // based on the target SDK of the package. As of 4 (donut)
2221 // the screen size support was introduced, so all default to
2222 // enabled.
2223 if (smallScreen > 0) {
2224 smallScreen = targetSdk >= 4 ? -1 : 0;
2225 }
2226 if (normalScreen > 0) {
2227 normalScreen = -1;
2228 }
2229 if (largeScreen > 0) {
2230 largeScreen = targetSdk >= 4 ? -1 : 0;
2231 }
2232 if (xlargeScreen > 0) {
2233 // Introduced in Gingerbread.
2234 xlargeScreen = targetSdk >= 9 ? -1 : 0;
2235 }
2236 if (anyDensity > 0) {
2237 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
2238 || compatibleWidthLimitDp > 0) ? -1 : 0;
2239 }
2240 printf("supports-screens:");
2241 if (smallScreen != 0) {
2242 printf(" 'small'");
2243 }
2244 if (normalScreen != 0) {
2245 printf(" 'normal'");
2246 }
2247 if (largeScreen != 0) {
2248 printf(" 'large'");
2249 }
2250 if (xlargeScreen != 0) {
2251 printf(" 'xlarge'");
2252 }
2253 printf("\n");
2254 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
2255 if (requiresSmallestWidthDp > 0) {
2256 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
2257 }
2258 if (compatibleWidthLimitDp > 0) {
2259 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
2260 }
2261 if (largestWidthLimitDp > 0) {
2262 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
2263 }
2264
2265 printf("locales:");
2266 const size_t NL = locales.size();
2267 for (size_t i=0; i<NL; i++) {
2268 const char* localeStr = locales[i].string();
2269 if (localeStr == NULL || strlen(localeStr) == 0) {
2270 localeStr = "--_--";
2271 }
2272 printf(" '%s'", localeStr);
2273 }
2274 printf("\n");
2275
2276 printf("densities:");
2277 const size_t ND = densities.size();
2278 for (size_t i=0; i<ND; i++) {
2279 printf(" '%d'", densities[i]);
2280 }
2281 printf("\n");
2282
2283 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
2284 if (dir != NULL) {
2285 if (dir->getFileCount() > 0) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002286 SortedVector<String8> architectures;
Adam Lesinski282e1812014-01-23 18:17:42 -08002287 for (size_t i=0; i<dir->getFileCount(); i++) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002288 architectures.add(ResTable::normalizeForOutput(
2289 dir->getFileName(i).string()));
Adam Lesinski282e1812014-01-23 18:17:42 -08002290 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07002291
2292 bool outputAltNativeCode = false;
2293 // A multiArch package is one that contains 64-bit and
2294 // 32-bit versions of native code and expects 3rd-party
2295 // apps to load these native code libraries. Since most
2296 // 64-bit systems also support 32-bit apps, the apps
2297 // loading this multiArch package's code may be either
2298 // 32-bit or 64-bit.
2299 if (hasMultiArch) {
2300 // If this is a multiArch package, report the 64-bit
2301 // version only. Then as a separate entry, report the
2302 // rest.
2303 //
2304 // If we report the 32-bit architecture, this APK will
2305 // be installed on a 32-bit device, causing a large waste
2306 // of bandwidth and disk space. This assumes that
2307 // the developer of the multiArch package has also
2308 // made a version that is 32-bit only.
2309 String8 intel64("x86_64");
2310 String8 arm64("arm64-v8a");
2311 ssize_t index = architectures.indexOf(intel64);
2312 if (index < 0) {
2313 index = architectures.indexOf(arm64);
2314 }
2315
2316 if (index >= 0) {
2317 printf("native-code: '%s'\n", architectures[index].string());
2318 architectures.removeAt(index);
2319 outputAltNativeCode = true;
2320 }
2321 }
2322
2323 const size_t archCount = architectures.size();
2324 if (archCount > 0) {
2325 if (outputAltNativeCode) {
2326 printf("alt-");
2327 }
2328 printf("native-code:");
2329 for (size_t i = 0; i < archCount; i++) {
2330 printf(" '%s'", architectures[i].string());
2331 }
2332 printf("\n");
2333 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002334 }
2335 delete dir;
2336 }
2337 } else if (strcmp("badger", option) == 0) {
2338 printf("%s", CONSOLE_DATA);
2339 } else if (strcmp("configurations", option) == 0) {
2340 Vector<ResTable_config> configs;
2341 res.getConfigurations(&configs);
2342 const size_t N = configs.size();
2343 for (size_t i=0; i<N; i++) {
2344 printf("%s\n", configs[i].toString().string());
2345 }
2346 } else {
2347 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2348 goto bail;
2349 }
2350 }
2351
2352 result = NO_ERROR;
2353
2354bail:
Adam Lesinski10de3af12016-07-13 10:14:03 -07002355 if (SourcePos::hasErrors()) {
2356 SourcePos::printErrors(stderr);
2357 }
2358
Adam Lesinski282e1812014-01-23 18:17:42 -08002359 if (asset) {
2360 delete asset;
2361 }
2362 return (result != NO_ERROR);
2363}
2364
2365
2366/*
2367 * Handle the "add" command, which wants to add files to a new or
2368 * pre-existing archive.
2369 */
2370int doAdd(Bundle* bundle)
2371{
2372 ZipFile* zip = NULL;
2373 status_t result = UNKNOWN_ERROR;
2374 const char* zipFileName;
2375
2376 if (bundle->getUpdate()) {
2377 /* avoid confusion */
2378 fprintf(stderr, "ERROR: can't use '-u' with add\n");
2379 goto bail;
2380 }
2381
2382 if (bundle->getFileSpecCount() < 1) {
2383 fprintf(stderr, "ERROR: must specify zip file name\n");
2384 goto bail;
2385 }
2386 zipFileName = bundle->getFileSpecEntry(0);
2387
2388 if (bundle->getFileSpecCount() < 2) {
2389 fprintf(stderr, "NOTE: nothing to do\n");
2390 goto bail;
2391 }
2392
2393 zip = openReadWrite(zipFileName, true);
2394 if (zip == NULL) {
2395 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2396 goto bail;
2397 }
2398
2399 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2400 const char* fileName = bundle->getFileSpecEntry(i);
2401
2402 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
2403 printf(" '%s'... (from gzip)\n", fileName);
2404 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
2405 } else {
2406 if (bundle->getJunkPath()) {
2407 String8 storageName = String8(fileName).getPathLeaf();
Maurice Chu2675f762013-10-22 17:33:11 -07002408 printf(" '%s' as '%s'...\n", fileName,
2409 ResTable::normalizeForOutput(storageName.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08002410 result = zip->add(fileName, storageName.string(),
2411 bundle->getCompressionMethod(), NULL);
2412 } else {
2413 printf(" '%s'...\n", fileName);
2414 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2415 }
2416 }
2417 if (result != NO_ERROR) {
2418 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2419 if (result == NAME_NOT_FOUND) {
2420 fprintf(stderr, ": file not found\n");
2421 } else if (result == ALREADY_EXISTS) {
2422 fprintf(stderr, ": already exists in archive\n");
2423 } else {
2424 fprintf(stderr, "\n");
2425 }
2426 goto bail;
2427 }
2428 }
2429
2430 result = NO_ERROR;
2431
2432bail:
2433 delete zip;
2434 return (result != NO_ERROR);
2435}
2436
2437
2438/*
2439 * Delete files from an existing archive.
2440 */
2441int doRemove(Bundle* bundle)
2442{
2443 ZipFile* zip = NULL;
2444 status_t result = UNKNOWN_ERROR;
2445 const char* zipFileName;
2446
2447 if (bundle->getFileSpecCount() < 1) {
2448 fprintf(stderr, "ERROR: must specify zip file name\n");
2449 goto bail;
2450 }
2451 zipFileName = bundle->getFileSpecEntry(0);
2452
2453 if (bundle->getFileSpecCount() < 2) {
2454 fprintf(stderr, "NOTE: nothing to do\n");
2455 goto bail;
2456 }
2457
2458 zip = openReadWrite(zipFileName, false);
2459 if (zip == NULL) {
2460 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2461 zipFileName);
2462 goto bail;
2463 }
2464
2465 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2466 const char* fileName = bundle->getFileSpecEntry(i);
2467 ZipEntry* entry;
2468
2469 entry = zip->getEntryByName(fileName);
2470 if (entry == NULL) {
2471 printf(" '%s' NOT FOUND\n", fileName);
2472 continue;
2473 }
2474
2475 result = zip->remove(entry);
2476
2477 if (result != NO_ERROR) {
2478 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2479 bundle->getFileSpecEntry(i), zipFileName);
2480 goto bail;
2481 }
2482 }
2483
2484 /* update the archive */
2485 zip->flush();
2486
2487bail:
2488 delete zip;
2489 return (result != NO_ERROR);
2490}
2491
Adam Lesinski3921e872014-05-13 10:56:25 -07002492static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002493 const size_t numDirs = dir->getDirs().size();
2494 for (size_t i = 0; i < numDirs; i++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002495 bool ignore = ignoreConfig;
2496 const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2497 const char* dirStr = subDir->getLeaf().string();
2498 if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2499 ignore = true;
2500 }
2501 status_t err = addResourcesToBuilder(subDir, builder, ignore);
Adam Lesinskifab50872014-04-16 14:40:42 -07002502 if (err != NO_ERROR) {
2503 return err;
2504 }
2505 }
2506
2507 const size_t numFiles = dir->getFiles().size();
2508 for (size_t i = 0; i < numFiles; i++) {
2509 sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2510 const size_t numConfigs = gp->getFiles().size();
2511 for (size_t j = 0; j < numConfigs; j++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002512 status_t err = NO_ERROR;
Adam Lesinskic7614e52017-03-16 16:54:23 -07002513 if (ignoreConfig) {
Guang Zhu8c2df712017-03-21 03:53:43 +00002514 err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
Adam Lesinskic7614e52017-03-16 16:54:23 -07002515 } else {
Guang Zhu8c2df712017-03-21 03:53:43 +00002516 err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
Adam Lesinskic7614e52017-03-16 16:54:23 -07002517 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002518 if (err != NO_ERROR) {
2519 fprintf(stderr, "Failed to add %s (%s) to builder.\n",
Guang Zhu8c2df712017-03-21 03:53:43 +00002520 gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
Adam Lesinskifab50872014-04-16 14:40:42 -07002521 return err;
2522 }
2523 }
2524 }
2525 return NO_ERROR;
2526}
2527
2528static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2529 if (split->isBase()) {
2530 return original;
2531 }
2532
2533 String8 ext(original.getPathExtension());
2534 if (ext == String8(".apk")) {
2535 return String8::format("%s_%s%s",
2536 original.getBasePath().string(),
2537 split->getDirectorySafeName().string(),
2538 ext.string());
2539 }
2540
2541 return String8::format("%s_%s", original.string(),
2542 split->getDirectorySafeName().string());
2543}
Adam Lesinski282e1812014-01-23 18:17:42 -08002544
2545/*
2546 * Package up an asset directory and associated application files.
2547 */
2548int doPackage(Bundle* bundle)
2549{
2550 const char* outputAPKFile;
2551 int retVal = 1;
2552 status_t err;
2553 sp<AaptAssets> assets;
2554 int N;
2555 FILE* fp;
2556 String8 dependencyFile;
Adam Lesinskifab50872014-04-16 14:40:42 -07002557 sp<ApkBuilder> builder;
Adam Lesinski282e1812014-01-23 18:17:42 -08002558
Anton Krumina2ef5c02014-03-12 14:46:44 -07002559 // -c en_XA or/and ar_XB means do pseudolocalization
Adam Lesinskifab50872014-04-16 14:40:42 -07002560 sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2561 err = configFilter->parse(bundle->getConfigurations());
Adam Lesinski282e1812014-01-23 18:17:42 -08002562 if (err != NO_ERROR) {
2563 goto bail;
2564 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002565 if (configFilter->containsPseudo()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002566 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2567 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002568 if (configFilter->containsPseudoBidi()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002569 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
Adam Lesinski282e1812014-01-23 18:17:42 -08002570 }
2571
2572 N = bundle->getFileSpecCount();
2573 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
Adam Lesinski09384302014-01-22 16:07:42 -08002574 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002575 fprintf(stderr, "ERROR: no input files\n");
2576 goto bail;
2577 }
2578
2579 outputAPKFile = bundle->getOutputAPKFile();
2580
2581 // Make sure the filenames provided exist and are of the appropriate type.
2582 if (outputAPKFile) {
2583 FileType type;
2584 type = getFileType(outputAPKFile);
2585 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2586 fprintf(stderr,
2587 "ERROR: output file '%s' exists but is not regular file\n",
2588 outputAPKFile);
2589 goto bail;
2590 }
2591 }
2592
2593 // Load the assets.
2594 assets = new AaptAssets();
2595
2596 // Set up the resource gathering in assets if we're going to generate
2597 // dependency files. Every time we encounter a resource while slurping
2598 // the tree, we'll add it to these stores so we have full resource paths
2599 // to write to a dependency file.
2600 if (bundle->getGenDependencies()) {
2601 sp<FilePathStore> resPathStore = new FilePathStore;
2602 assets->setFullResPaths(resPathStore);
2603 sp<FilePathStore> assetPathStore = new FilePathStore;
2604 assets->setFullAssetPaths(assetPathStore);
2605 }
2606
2607 err = assets->slurpFromArgs(bundle);
2608 if (err < 0) {
2609 goto bail;
2610 }
2611
2612 if (bundle->getVerbose()) {
2613 assets->print(String8());
2614 }
2615
Adam Lesinskifab50872014-04-16 14:40:42 -07002616 // Create the ApkBuilder, which will collect the compiled files
2617 // to write to the final APK (or sets of APKs if we are building
2618 // a Split APK.
2619 builder = new ApkBuilder(configFilter);
2620
2621 // If we are generating a Split APK, find out which configurations to split on.
2622 if (bundle->getSplitConfigurations().size() > 0) {
2623 const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2624 const size_t numSplits = splitStrs.size();
2625 for (size_t i = 0; i < numSplits; i++) {
2626 std::set<ConfigDescription> configs;
2627 if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2628 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2629 goto bail;
2630 }
2631
2632 err = builder->createSplitForConfigs(configs);
2633 if (err != NO_ERROR) {
2634 goto bail;
2635 }
2636 }
2637 }
2638
Adam Lesinski282e1812014-01-23 18:17:42 -08002639 // If they asked for any fileAs that need to be compiled, do so.
2640 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002641 err = buildResources(bundle, assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002642 if (err != 0) {
2643 goto bail;
2644 }
2645 }
2646
2647 // At this point we've read everything and processed everything. From here
2648 // on out it's just writing output files.
2649 if (SourcePos::hasErrors()) {
2650 goto bail;
2651 }
2652
2653 // Update symbols with information about which ones are needed as Java symbols.
2654 assets->applyJavaSymbols();
2655 if (SourcePos::hasErrors()) {
2656 goto bail;
2657 }
2658
2659 // If we've been asked to generate a dependency file, do that here
2660 if (bundle->getGenDependencies()) {
2661 // If this is the packaging step, generate the dependency file next to
2662 // the output apk (e.g. bin/resources.ap_.d)
2663 if (outputAPKFile) {
2664 dependencyFile = String8(outputAPKFile);
2665 // Add the .d extension to the dependency file.
2666 dependencyFile.append(".d");
2667 } else {
2668 // Else if this is the R.java dependency generation step,
2669 // generate the dependency file in the R.java package subdirectory
2670 // e.g. gen/com/foo/app/R.java.d
2671 dependencyFile = String8(bundle->getRClassDir());
2672 dependencyFile.appendPath("R.java.d");
2673 }
2674 // Make sure we have a clean dependency file to start with
2675 fp = fopen(dependencyFile, "w");
2676 fclose(fp);
2677 }
2678
2679 // Write out R.java constants
2680 if (!assets->havePrivateSymbols()) {
2681 if (bundle->getCustomPackage() == NULL) {
2682 // Write the R.java file into the appropriate class directory
2683 // e.g. gen/com/foo/app/R.java
Adam Lesinski1e4663852014-08-15 14:47:28 -07002684 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002685 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002686 } else {
2687 const String8 customPkg(bundle->getCustomPackage());
Adam Lesinski1e4663852014-08-15 14:47:28 -07002688 err = writeResourceSymbols(bundle, assets, customPkg, true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002689 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002690 }
2691 if (err < 0) {
2692 goto bail;
2693 }
2694 // If we have library files, we're going to write our R.java file into
2695 // the appropriate class directory for those libraries as well.
2696 // e.g. gen/com/foo/app/lib/R.java
2697 if (bundle->getExtraPackages() != NULL) {
2698 // Split on colon
2699 String8 libs(bundle->getExtraPackages());
2700 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2701 while (packageString != NULL) {
2702 // Write the R.java file out with the correct package name
Marcin Kosiba0f3a5a62014-09-11 13:48:48 +01002703 err = writeResourceSymbols(bundle, assets, String8(packageString), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002704 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002705 if (err < 0) {
2706 goto bail;
2707 }
2708 packageString = strtok(NULL, ":");
2709 }
2710 libs.unlockBuffer();
2711 }
2712 } else {
Adam Lesinski1e4663852014-08-15 14:47:28 -07002713 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002714 if (err < 0) {
2715 goto bail;
2716 }
Adam Lesinski1e4663852014-08-15 14:47:28 -07002717 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002718 if (err < 0) {
2719 goto bail;
2720 }
2721 }
2722
2723 // Write out the ProGuard file
2724 err = writeProguardFile(bundle, assets);
2725 if (err < 0) {
2726 goto bail;
2727 }
2728
Rohit Agrawal86229cb2016-04-21 16:29:58 -07002729 // Write out the Main Dex ProGuard file
2730 err = writeMainDexProguardFile(bundle, assets);
2731 if (err < 0) {
2732 goto bail;
2733 }
2734
Adam Lesinski282e1812014-01-23 18:17:42 -08002735 // Write the apk
2736 if (outputAPKFile) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002737 // Gather all resources and add them to the APK Builder. The builder will then
2738 // figure out which Split they belong in.
2739 err = addResourcesToBuilder(assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002740 if (err != NO_ERROR) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002741 goto bail;
2742 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002743
2744 const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2745 const size_t numSplits = splits.size();
2746 for (size_t i = 0; i < numSplits; i++) {
2747 const sp<ApkSplit>& split = splits[i];
2748 String8 outputPath = buildApkName(String8(outputAPKFile), split);
2749 err = writeAPK(bundle, outputPath, split);
2750 if (err != NO_ERROR) {
2751 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2752 goto bail;
2753 }
2754 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002755 }
2756
2757 // If we've been asked to generate a dependency file, we need to finish up here.
2758 // the writeResourceSymbols and writeAPK functions have already written the target
2759 // half of the dependency file, now we need to write the prerequisites. (files that
2760 // the R.java file or .ap_ file depend on)
2761 if (bundle->getGenDependencies()) {
2762 // Now that writeResourceSymbols or writeAPK has taken care of writing
2763 // the targets to our dependency file, we'll write the prereqs
2764 fp = fopen(dependencyFile, "a+");
2765 fprintf(fp, " : ");
2766 bool includeRaw = (outputAPKFile != NULL);
2767 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2768 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2769 // and therefore was not added to our pathstores during slurping
2770 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2771 fclose(fp);
2772 }
2773
2774 retVal = 0;
2775bail:
2776 if (SourcePos::hasErrors()) {
2777 SourcePos::printErrors(stderr);
2778 }
2779 return retVal;
2780}
2781
2782/*
2783 * Do PNG Crunching
2784 * PRECONDITIONS
2785 * -S flag points to a source directory containing drawable* folders
2786 * -C flag points to destination directory. The folder structure in the
2787 * source directory will be mirrored to the destination (cache) directory
2788 *
2789 * POSTCONDITIONS
2790 * Destination directory will be updated to match the PNG files in
Maurice Chu2675f762013-10-22 17:33:11 -07002791 * the source directory.
Adam Lesinski282e1812014-01-23 18:17:42 -08002792 */
2793int doCrunch(Bundle* bundle)
2794{
2795 fprintf(stdout, "Crunching PNG Files in ");
2796 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2797 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2798
2799 updatePreProcessedCache(bundle);
2800
2801 return NO_ERROR;
2802}
2803
2804/*
2805 * Do PNG Crunching on a single flag
2806 * -i points to a single png file
2807 * -o points to a single png output file
2808 */
2809int doSingleCrunch(Bundle* bundle)
2810{
2811 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2812 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2813
2814 String8 input(bundle->getSingleCrunchInputFile());
2815 String8 output(bundle->getSingleCrunchOutputFile());
2816
2817 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2818 // we can't return the status_t as it gets truncate to the lower 8 bits.
2819 return 42;
2820 }
2821
2822 return NO_ERROR;
2823}
2824
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002825int runInDaemonMode(Bundle* bundle) {
2826 std::cout << "Ready" << std::endl;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002827 for (std::string cmd; std::getline(std::cin, cmd);) {
2828 if (cmd == "quit") {
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002829 return NO_ERROR;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002830 } else if (cmd == "s") {
2831 // Two argument crunch
2832 std::string inputFile, outputFile;
2833 std::getline(std::cin, inputFile);
2834 std::getline(std::cin, outputFile);
2835 bundle->setSingleCrunchInputFile(inputFile.c_str());
2836 bundle->setSingleCrunchOutputFile(outputFile.c_str());
2837 std::cout << "Crunching " << inputFile << std::endl;
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002838 if (doSingleCrunch(bundle) != NO_ERROR) {
2839 std::cout << "Error" << std::endl;
2840 }
2841 std::cout << "Done" << std::endl;
2842 } else {
2843 // in case of invalid command, just bail out.
2844 std::cerr << "Unknown command" << std::endl;
2845 return -1;
2846 }
2847 }
2848 return -1;
2849}
2850
Adam Lesinski282e1812014-01-23 18:17:42 -08002851char CONSOLE_DATA[2925] = {
2852 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2853 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2854 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2855 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2856 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2857 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2858 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2859 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2860 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2861 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2862 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2863 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2864 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2865 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2866 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2867 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2868 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2869 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2870 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2871 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2872 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2873 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2874 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2875 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2876 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2877 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2878 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2879 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2880 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2881 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2882 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2883 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2884 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2885 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2886 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2887 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2888 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2889 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2890 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2891 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2892 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2893 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2894 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2895 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2896 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2897 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2898 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2899 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2900 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2901 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2902 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2903 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2904 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2905 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2906 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2907 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2908 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2909 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2910 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2911 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2912 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2913 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2914 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2915 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2916 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2917 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2918 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2919 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2920 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2921 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2922 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2923 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2924 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2925 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2926 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2927 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2928 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2929 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2930 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2931 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2932 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2933 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2934 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2935 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2936 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2937 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2938 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2939 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2940 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2941 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2942 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2943 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2944 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2945 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2946 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2947 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2948 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2949 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2950 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2951 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2952 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2953 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2954 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2955 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2956 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2957 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2958 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2959 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2960 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2961 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2962 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2963 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2964 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2965 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2966 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2967 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2968 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2969 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2970 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2971 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2972 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2973 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2974 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2975 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2976 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2977 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2978 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2979 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2980 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2981 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2982 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2983 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2984 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2985 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2986 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2987 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2988 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2989 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2990 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2991 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2992 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2993 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2994 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2995 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2996 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2997 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2998 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2999 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3000 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3001 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
3002 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
3003 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
3004 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
3005 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
3006 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3007 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3008 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
3009 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
3010 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3011 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3012 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3013 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3014 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
3015 };