blob: fdcc7aa03775f9cc7d30f4f01b5ee73bd89bb536 [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
765 // Make a dummy config for retrieving resources... we need to supply
766 // non-default values for some configs so that we can retrieve resources
767 // in the app that don't have a default. The most important of these is
768 // the API version because key resources like icons will have an implicit
769 // version if they are using newer config types like density.
770 ResTable_config config;
Narayan Kamath91447d82014-01-21 15:32:36 +0000771 memset(&config, 0, sizeof(ResTable_config));
Adam Lesinski282e1812014-01-23 18:17:42 -0800772 config.language[0] = 'e';
773 config.language[1] = 'n';
774 config.country[0] = 'U';
775 config.country[1] = 'S';
776 config.orientation = ResTable_config::ORIENTATION_PORT;
777 config.density = ResTable_config::DENSITY_MEDIUM;
778 config.sdkVersion = 10000; // Very high.
779 config.screenWidthDp = 320;
780 config.screenHeightDp = 480;
781 config.smallestScreenWidthDp = 320;
Adam Lesinskic2dea8d2014-08-04 16:40:41 -0700782 config.screenLayout |= ResTable_config::SCREENSIZE_NORMAL;
Adam Lesinski282e1812014-01-23 18:17:42 -0800783 assets.setConfiguration(config);
784
785 const ResTable& res = assets.getResources(false);
Dan Albert68001652014-09-09 09:51:01 -0700786 if (res.getError() != NO_ERROR) {
Adam Lesinski25e9d552014-05-19 15:01:43 -0700787 fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
Adam Lesinski63e646e2014-07-30 11:40:39 -0700788 return 1;
Adam Lesinski282e1812014-01-23 18:17:42 -0800789 }
790
Adam Lesinski694d0a72016-04-06 16:12:04 -0700791 // Source for AndroidManifest.xml
Adam Lesinski10de3af12016-07-13 10:14:03 -0700792 const String8 manifestFile("AndroidManifest.xml");
Adam Lesinski694d0a72016-04-06 16:12:04 -0700793
Adam Lesinski2cb761e2014-08-15 13:59:02 -0700794 // The dynamicRefTable can be null if there are no resources for this asset cookie.
795 // This fine.
Adam Lesinski63e646e2014-07-30 11:40:39 -0700796 const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700797
798 Asset* asset = NULL;
799
Adam Lesinski282e1812014-01-23 18:17:42 -0800800 if (strcmp("resources", option) == 0) {
Elliott Hughesba3fe562015-08-12 14:49:53 -0700801#ifndef __ANDROID__
Adam Lesinski282e1812014-01-23 18:17:42 -0800802 res.print(bundle->getValues());
803#endif
804
805 } else if (strcmp("strings", option) == 0) {
806 const ResStringPool* pool = res.getTableStringBlock(0);
807 printStringPool(pool);
808
809 } else if (strcmp("xmltree", option) == 0) {
810 if (bundle->getFileSpecCount() < 3) {
811 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
812 goto bail;
813 }
814
815 for (int i=2; i<bundle->getFileSpecCount(); i++) {
816 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700817 ResXMLTree tree(dynamicRefTable);
818 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800819 if (asset == NULL) {
Adam Lesinskifcb5f7b2016-11-02 13:17:10 -0700820 fprintf(stderr, "ERROR: dump failed because resource %s not found\n", resname);
Adam Lesinski282e1812014-01-23 18:17:42 -0800821 goto bail;
822 }
823
824 if (tree.setTo(asset->getBuffer(true),
825 asset->getLength()) != NO_ERROR) {
826 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
827 goto bail;
828 }
829 tree.restart();
830 printXMLBlock(&tree);
831 tree.uninit();
832 delete asset;
833 asset = NULL;
834 }
835
836 } else if (strcmp("xmlstrings", option) == 0) {
837 if (bundle->getFileSpecCount() < 3) {
838 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
839 goto bail;
840 }
841
842 for (int i=2; i<bundle->getFileSpecCount(); i++) {
843 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700844 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800845 if (asset == NULL) {
846 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
847 goto bail;
848 }
849
Adam Lesinski63e646e2014-07-30 11:40:39 -0700850 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800851 if (tree.setTo(asset->getBuffer(true),
852 asset->getLength()) != NO_ERROR) {
853 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
854 goto bail;
855 }
856 printStringPool(&tree.getStrings());
857 delete asset;
858 asset = NULL;
859 }
860
861 } else {
Adam Lesinski63e646e2014-07-30 11:40:39 -0700862 asset = assets.openNonAsset(assetsCookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800863 if (asset == NULL) {
864 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
865 goto bail;
866 }
867
Adam Lesinski63e646e2014-07-30 11:40:39 -0700868 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800869 if (tree.setTo(asset->getBuffer(true),
870 asset->getLength()) != NO_ERROR) {
871 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
872 goto bail;
873 }
874 tree.restart();
875
876 if (strcmp("permissions", option) == 0) {
877 size_t len;
878 ResXMLTree::event_code_t code;
879 int depth = 0;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800880 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
881 code != ResXMLTree::BAD_DOCUMENT) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800882 if (code == ResXMLTree::END_TAG) {
883 depth--;
884 continue;
885 }
886 if (code != ResXMLTree::START_TAG) {
887 continue;
888 }
889 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700890 const char16_t* ctag16 = tree.getElementName(&len);
891 if (ctag16 == NULL) {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700892 SourcePos(manifestFile, tree.getLineNumber()).error(
893 "ERROR: failed to get XML element name (bad string pool)");
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700894 goto bail;
895 }
896 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800897 //printf("Depth %d tag %s\n", depth, tag.string());
898 if (depth == 1) {
899 if (tag != "manifest") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700900 SourcePos(manifestFile, tree.getLineNumber()).error(
901 "ERROR: manifest does not start with <manifest> tag");
Adam Lesinski282e1812014-01-23 18:17:42 -0800902 goto bail;
903 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700904 String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -0700905 printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800906 } else if (depth == 2) {
907 if (tag == "permission") {
908 String8 error;
909 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
910 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700911 SourcePos(manifestFile, tree.getLineNumber()).error(
912 "ERROR getting 'android:name': %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800913 goto bail;
914 }
915
916 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700917 SourcePos(manifestFile, tree.getLineNumber()).error(
918 "ERROR: missing 'android:name' for permission");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800919 goto bail;
920 }
921 printf("permission: %s\n",
922 ResTable::normalizeForOutput(name.string()).string());
923 } else if (tag == "uses-permission") {
924 String8 error;
925 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
926 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700927 SourcePos(manifestFile, tree.getLineNumber()).error(
928 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800929 goto bail;
930 }
931
932 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700933 SourcePos(manifestFile, tree.getLineNumber()).error(
934 "ERROR: missing 'android:name' for uses-permission");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800935 goto bail;
936 }
937 printUsesPermission(name,
938 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
939 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
940 } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
941 String8 error;
942 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
943 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700944 SourcePos(manifestFile, tree.getLineNumber()).error(
945 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800946 goto bail;
947 }
948
949 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700950 SourcePos(manifestFile, tree.getLineNumber()).error(
951 "ERROR: missing 'android:name' for uses-permission-sdk-23");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800952 goto bail;
953 }
954 printUsesPermissionSdk23(
955 name,
956 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
Adam Lesinski282e1812014-01-23 18:17:42 -0800957 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800958 }
959 }
960 } else if (strcmp("badging", option) == 0) {
961 Vector<String8> locales;
962 res.getLocales(&locales);
963
964 Vector<ResTable_config> configs;
965 res.getConfigurations(&configs);
966 SortedVector<int> densities;
967 const size_t NC = configs.size();
968 for (size_t i=0; i<NC; i++) {
969 int dens = configs[i].density;
970 if (dens == 0) {
971 dens = 160;
972 }
973 densities.add(dens);
974 }
975
976 size_t len;
977 ResXMLTree::event_code_t code;
978 int depth = 0;
979 String8 error;
980 bool withinActivity = false;
981 bool isMainActivity = false;
982 bool isLauncherActivity = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800983 bool isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800984 bool isSearchable = false;
985 bool withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700986 bool withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700987 bool withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800988 bool withinReceiver = false;
989 bool withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700990 bool withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800991 bool withinIntentFilter = false;
992 bool hasMainActivity = false;
993 bool hasOtherActivities = false;
994 bool hasOtherReceivers = false;
995 bool hasOtherServices = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700996 bool hasIntentFilter = false;
997
Adam Lesinski282e1812014-01-23 18:17:42 -0800998 bool hasWallpaperService = false;
999 bool hasImeService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001000 bool hasAccessibilityService = false;
1001 bool hasPrintService = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001002 bool hasWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001003 bool hasDeviceAdminReceiver = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001004 bool hasPaymentService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001005 bool hasDocumentsProvider = false;
1006 bool hasCameraActivity = false;
1007 bool hasCameraSecureActivity = false;
1008 bool hasLauncher = false;
1009 bool hasNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001010 bool hasDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001011
Adam Lesinski282e1812014-01-23 18:17:42 -08001012 bool actMainActivity = false;
1013 bool actWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001014 bool actDeviceAdminEnabled = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001015 bool actImeService = false;
1016 bool actWallpaperService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001017 bool actAccessibilityService = false;
1018 bool actPrintService = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001019 bool actHostApduService = false;
1020 bool actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001021 bool actDocumentsProvider = false;
1022 bool actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001023 bool actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001024 bool actCamera = false;
1025 bool actCameraSecure = false;
1026 bool catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001027 bool hasMetaHostPaymentCategory = false;
1028 bool hasMetaOffHostPaymentCategory = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001029
1030 // These permissions are required by services implementing services
1031 // the system binds to (IME, Accessibility, PrintServices, etc.)
1032 bool hasBindDeviceAdminPermission = false;
1033 bool hasBindInputMethodPermission = false;
1034 bool hasBindAccessibilityServicePermission = false;
1035 bool hasBindPrintServicePermission = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001036 bool hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001037 bool hasRequiredSafAttributes = false;
1038 bool hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001039 bool hasBindDreamServicePermission = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001040
1041 // These two implement the implicit permissions that are granted
1042 // to pre-1.6 applications.
1043 bool hasWriteExternalStoragePermission = false;
Adam Lesinski2386df22016-12-28 15:08:58 -05001044 int32_t writeExternalStoragePermissionMaxSdkVersion = -1;
Adam Lesinski282e1812014-01-23 18:17:42 -08001045 bool hasReadPhoneStatePermission = false;
1046
1047 // If an app requests write storage, they will also get read storage.
1048 bool hasReadExternalStoragePermission = false;
1049
1050 // Implement transition to read and write call log.
1051 bool hasReadContactsPermission = false;
1052 bool hasWriteContactsPermission = false;
1053 bool hasReadCallLogPermission = false;
1054 bool hasWriteCallLogPermission = false;
1055
Adam Lesinskie47fd122014-08-15 22:25:36 -07001056 // If an app declares itself as multiArch, we report the
1057 // native libraries differently.
1058 bool hasMultiArch = false;
1059
Adam Lesinski282e1812014-01-23 18:17:42 -08001060 // This next group of variables is used to implement a group of
1061 // backward-compatibility heuristics necessitated by the addition of
1062 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
1063 // heuristic is "if an app requests a permission but doesn't explicitly
1064 // request the corresponding <uses-feature>, presume it's there anyway".
Adam Lesinski2c72b682014-06-24 09:56:01 -07001065
Adam Lesinski282e1812014-01-23 18:17:42 -08001066 // 2.2 also added some other features that apps can request, but that
1067 // have no corresponding permission, so we cannot implement any
1068 // back-compatibility heuristic for them. The below are thus unnecessary
1069 // (but are retained here for documentary purposes.)
1070 //bool specCompassFeature = false;
1071 //bool specAccelerometerFeature = false;
1072 //bool specProximityFeature = false;
1073 //bool specAmbientLightFeature = false;
1074 //bool specLiveWallpaperFeature = false;
1075
1076 int targetSdk = 0;
1077 int smallScreen = 1;
1078 int normalScreen = 1;
1079 int largeScreen = 1;
1080 int xlargeScreen = 1;
1081 int anyDensity = 1;
1082 int requiresSmallestWidthDp = 0;
1083 int compatibleWidthLimitDp = 0;
1084 int largestWidthLimitDp = 0;
1085 String8 pkg;
1086 String8 activityName;
1087 String8 activityLabel;
1088 String8 activityIcon;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001089 String8 activityBanner;
Adam Lesinski282e1812014-01-23 18:17:42 -08001090 String8 receiverName;
1091 String8 serviceName;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001092 Vector<String8> supportedInput;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001093
1094 FeatureGroup commonFeatures;
1095 Vector<FeatureGroup> featureGroups;
1096 KeyedVector<String8, ImpliedFeature> impliedFeatures;
1097
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001098 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
1099 code != ResXMLTree::BAD_DOCUMENT) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001100 if (code == ResXMLTree::END_TAG) {
1101 depth--;
1102 if (depth < 2) {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001103 if (withinSupportsInput && !supportedInput.isEmpty()) {
1104 printf("supports-input: '");
1105 const size_t N = supportedInput.size();
1106 for (size_t i=0; i<N; i++) {
Maurice Chu2675f762013-10-22 17:33:11 -07001107 printf("%s", ResTable::normalizeForOutput(
1108 supportedInput[i].string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001109 if (i != N - 1) {
1110 printf("' '");
1111 } else {
1112 printf("'\n");
1113 }
1114 }
1115 supportedInput.clear();
1116 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001117 withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001118 withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001119 withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001120 } else if (depth < 3) {
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001121 if (withinActivity && isMainActivity) {
Maurice Chu2675f762013-10-22 17:33:11 -07001122 String8 aName(getComponentName(pkg, activityName));
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001123 if (isLauncherActivity) {
1124 printf("launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001125 if (aName.length() > 0) {
1126 printf(" name='%s' ",
1127 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001128 }
1129 printf(" label='%s' icon='%s'\n",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001130 ResTable::normalizeForOutput(activityLabel.string())
1131 .string(),
1132 ResTable::normalizeForOutput(activityIcon.string())
1133 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001134 }
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001135 if (isLeanbackLauncherActivity) {
1136 printf("leanback-launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001137 if (aName.length() > 0) {
1138 printf(" name='%s' ",
1139 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001140 }
1141 printf(" label='%s' icon='%s' banner='%s'\n",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001142 ResTable::normalizeForOutput(activityLabel.string())
1143 .string(),
1144 ResTable::normalizeForOutput(activityIcon.string())
1145 .string(),
1146 ResTable::normalizeForOutput(activityBanner.string())
1147 .string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001148 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001149 }
1150 if (!hasIntentFilter) {
1151 hasOtherActivities |= withinActivity;
1152 hasOtherReceivers |= withinReceiver;
1153 hasOtherServices |= withinService;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001154 } else {
1155 if (withinService) {
1156 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
1157 hasBindNfcServicePermission);
1158 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
1159 hasBindNfcServicePermission);
1160 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001161 }
1162 withinActivity = false;
1163 withinService = false;
1164 withinReceiver = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001165 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001166 hasIntentFilter = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001167 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001168 } else if (depth < 4) {
1169 if (withinIntentFilter) {
1170 if (withinActivity) {
1171 hasMainActivity |= actMainActivity;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001172 hasLauncher |= catLauncher;
1173 hasCameraActivity |= actCamera;
1174 hasCameraSecureActivity |= actCameraSecure;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001175 hasOtherActivities |=
1176 !actMainActivity && !actCamera && !actCameraSecure;
Adam Lesinski282e1812014-01-23 18:17:42 -08001177 } else if (withinReceiver) {
1178 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001179 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
1180 hasBindDeviceAdminPermission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001181 hasOtherReceivers |=
1182 (!actWidgetReceivers && !actDeviceAdminEnabled);
Adam Lesinski282e1812014-01-23 18:17:42 -08001183 } else if (withinService) {
1184 hasImeService |= actImeService;
1185 hasWallpaperService |= actWallpaperService;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001186 hasAccessibilityService |= (actAccessibilityService &&
1187 hasBindAccessibilityServicePermission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001188 hasPrintService |=
1189 (actPrintService && hasBindPrintServicePermission);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001190 hasNotificationListenerService |= actNotificationListenerService &&
1191 hasBindNotificationListenerServicePermission;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001192 hasDreamService |= actDreamService && hasBindDreamServicePermission;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001193 hasOtherServices |= (!actImeService && !actWallpaperService &&
Adam Lesinski94fc9122013-09-30 17:16:09 -07001194 !actAccessibilityService && !actPrintService &&
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001195 !actHostApduService && !actOffHostApduService &&
1196 !actNotificationListenerService);
1197 } else if (withinProvider) {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001198 hasDocumentsProvider |=
1199 actDocumentsProvider && hasRequiredSafAttributes;
Adam Lesinski282e1812014-01-23 18:17:42 -08001200 }
1201 }
1202 withinIntentFilter = false;
1203 }
1204 continue;
1205 }
1206 if (code != ResXMLTree::START_TAG) {
1207 continue;
1208 }
1209 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001210
1211 const char16_t* ctag16 = tree.getElementName(&len);
1212 if (ctag16 == NULL) {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001213 SourcePos(manifestFile, tree.getLineNumber()).error(
1214 "ERROR: failed to get XML element name (bad string pool)");
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001215 goto bail;
1216 }
1217 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -08001218 //printf("Depth %d, %s\n", depth, tag.string());
1219 if (depth == 1) {
1220 if (tag != "manifest") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001221 SourcePos(manifestFile, tree.getLineNumber()).error(
1222 "ERROR: manifest does not start with <manifest> tag");
Adam Lesinski282e1812014-01-23 18:17:42 -08001223 goto bail;
1224 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001225 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -07001226 printf("package: name='%s' ",
1227 ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001228 int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR,
1229 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001230 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001231 SourcePos(manifestFile, tree.getLineNumber()).error(
1232 "ERROR getting 'android:versionCode' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001233 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001234 goto bail;
1235 }
1236 if (versionCode > 0) {
1237 printf("versionCode='%d' ", versionCode);
1238 } else {
1239 printf("versionCode='' ");
1240 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001241 String8 versionName = AaptXml::getResolvedAttribute(res, tree,
1242 VERSION_NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001243 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001244 SourcePos(manifestFile, tree.getLineNumber()).error(
1245 "ERROR getting 'android:versionName' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001246 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001247 goto bail;
1248 }
Adam Lesinski25d35a92014-08-11 09:41:56 -07001249 printf("versionName='%s'",
Maurice Chu2675f762013-10-22 17:33:11 -07001250 ResTable::normalizeForOutput(versionName.string()).string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001251
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001252 String8 splitName = AaptXml::getAttribute(tree, NULL, "split");
Adam Lesinski25d35a92014-08-11 09:41:56 -07001253 if (!splitName.isEmpty()) {
1254 printf(" split='%s'", ResTable::normalizeForOutput(
1255 splitName.string()).string());
1256 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001257
Adam Lesinski5283fab2014-08-29 11:23:55 -07001258 String8 platformVersionName = AaptXml::getAttribute(tree, NULL,
1259 "platformBuildVersionName");
1260 printf(" platformBuildVersionName='%s'", platformVersionName.string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001261 printf("\n");
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001262
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001263 int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
1264 INSTALL_LOCATION_ATTR, &error);
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001265 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001266 SourcePos(manifestFile, tree.getLineNumber()).error(
1267 "ERROR getting 'android:installLocation' attribute: %s",
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001268 error.string());
1269 goto bail;
1270 }
1271
1272 if (installLocation >= 0) {
1273 printf("install-location:'");
1274 switch (installLocation) {
1275 case 0:
1276 printf("auto");
1277 break;
1278 case 1:
1279 printf("internalOnly");
1280 break;
1281 case 2:
1282 printf("preferExternal");
1283 break;
1284 default:
1285 fprintf(stderr, "Invalid installLocation %d\n", installLocation);
1286 goto bail;
1287 }
1288 printf("'\n");
1289 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001290 } else if (depth == 2) {
1291 withinApplication = false;
1292 if (tag == "application") {
1293 withinApplication = true;
1294
1295 String8 label;
1296 const size_t NL = locales.size();
1297 for (size_t i=0; i<NL; i++) {
1298 const char* localeStr = locales[i].string();
Adam Lesinskia77685f2016-10-03 16:26:28 -07001299 assets.setConfiguration(config, localeStr != NULL ? localeStr : "");
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001300 String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1301 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001302 if (llabel != "") {
1303 if (localeStr == NULL || strlen(localeStr) == 0) {
1304 label = llabel;
Maurice Chu2675f762013-10-22 17:33:11 -07001305 printf("application-label:'%s'\n",
1306 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001307 } else {
1308 if (label == "") {
1309 label = llabel;
1310 }
1311 printf("application-label-%s:'%s'\n", localeStr,
Maurice Chu2675f762013-10-22 17:33:11 -07001312 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001313 }
1314 }
1315 }
1316
1317 ResTable_config tmpConfig = config;
1318 const size_t ND = densities.size();
1319 for (size_t i=0; i<ND; i++) {
1320 tmpConfig.density = densities[i];
1321 assets.setConfiguration(tmpConfig);
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001322 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1323 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001324 if (icon != "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001325 printf("application-icon-%d:'%s'\n", densities[i],
1326 ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001327 }
1328 }
1329 assets.setConfiguration(config);
1330
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001331 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001332 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001333 SourcePos(manifestFile, tree.getLineNumber()).error(
1334 "ERROR getting 'android:icon' attribute: %s", error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001335 goto bail;
1336 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001337 int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0,
1338 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001339 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001340 SourcePos(manifestFile, tree.getLineNumber()).error(
1341 "ERROR getting 'android:testOnly' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001342 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001343 goto bail;
1344 }
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001345
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001346 String8 banner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1347 &error);
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001348 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001349 SourcePos(manifestFile, tree.getLineNumber()).error(
1350 "ERROR getting 'android:banner' attribute: %s", error.string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001351 goto bail;
1352 }
Maurice Chu2675f762013-10-22 17:33:11 -07001353 printf("application: label='%s' ",
1354 ResTable::normalizeForOutput(label.string()).string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001355 printf("icon='%s'", ResTable::normalizeForOutput(icon.string()).string());
1356 if (banner != "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001357 printf(" banner='%s'",
1358 ResTable::normalizeForOutput(banner.string()).string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001359 }
1360 printf("\n");
Adam Lesinski282e1812014-01-23 18:17:42 -08001361 if (testOnly != 0) {
1362 printf("testOnly='%d'\n", testOnly);
1363 }
1364
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001365 int32_t isGame = AaptXml::getResolvedIntegerAttribute(res, tree,
1366 ISGAME_ATTR, 0, &error);
1367 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001368 SourcePos(manifestFile, tree.getLineNumber()).error(
1369 "ERROR getting 'android:isGame' attribute: %s", error.string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001370 goto bail;
1371 }
1372 if (isGame != 0) {
1373 printf("application-isGame\n");
1374 }
1375
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001376 int32_t debuggable = AaptXml::getResolvedIntegerAttribute(res, tree,
1377 DEBUGGABLE_ATTR, 0, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001378 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001379 SourcePos(manifestFile, tree.getLineNumber()).error(
1380 "ERROR getting 'android:debuggable' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001381 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001382 goto bail;
1383 }
1384 if (debuggable != 0) {
1385 printf("application-debuggable\n");
1386 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07001387
1388 // We must search by name because the multiArch flag hasn't been API
1389 // frozen yet.
1390 int32_t multiArchIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
1391 "multiArch");
1392 if (multiArchIndex >= 0) {
1393 Res_value value;
1394 if (tree.getAttributeValue(multiArchIndex, &value) != NO_ERROR) {
1395 if (value.dataType >= Res_value::TYPE_FIRST_INT &&
1396 value.dataType <= Res_value::TYPE_LAST_INT) {
1397 hasMultiArch = value.data;
1398 }
1399 }
1400 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001401 } else if (tag == "uses-sdk") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001402 int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR,
1403 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001404 if (error != "") {
1405 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001406 String8 name = AaptXml::getResolvedAttribute(res, tree,
1407 MIN_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001408 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001409 SourcePos(manifestFile, tree.getLineNumber()).error(
1410 "ERROR getting 'android:minSdkVersion' attribute: %s",
Adam Lesinski282e1812014-01-23 18:17:42 -08001411 error.string());
1412 goto bail;
1413 }
1414 if (name == "Donut") targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001415 printf("sdkVersion:'%s'\n",
1416 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001417 } else if (code != -1) {
1418 targetSdk = code;
1419 printf("sdkVersion:'%d'\n", code);
1420 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001421 code = AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -08001422 if (code != -1) {
1423 printf("maxSdkVersion:'%d'\n", code);
1424 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001425 code = AaptXml::getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001426 if (error != "") {
1427 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001428 String8 name = AaptXml::getResolvedAttribute(res, tree,
1429 TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001430 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001431 SourcePos(manifestFile, tree.getLineNumber()).error(
1432 "ERROR getting 'android:targetSdkVersion' attribute: %s",
Adam Lesinski282e1812014-01-23 18:17:42 -08001433 error.string());
1434 goto bail;
1435 }
1436 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001437 printf("targetSdkVersion:'%s'\n",
1438 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001439 } else if (code != -1) {
1440 if (targetSdk < code) {
1441 targetSdk = code;
1442 }
1443 printf("targetSdkVersion:'%d'\n", code);
1444 }
1445 } else if (tag == "uses-configuration") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001446 int32_t reqTouchScreen = AaptXml::getIntegerAttribute(tree,
1447 REQ_TOUCH_SCREEN_ATTR, 0);
1448 int32_t reqKeyboardType = AaptXml::getIntegerAttribute(tree,
1449 REQ_KEYBOARD_TYPE_ATTR, 0);
1450 int32_t reqHardKeyboard = AaptXml::getIntegerAttribute(tree,
1451 REQ_HARD_KEYBOARD_ATTR, 0);
1452 int32_t reqNavigation = AaptXml::getIntegerAttribute(tree,
1453 REQ_NAVIGATION_ATTR, 0);
1454 int32_t reqFiveWayNav = AaptXml::getIntegerAttribute(tree,
1455 REQ_FIVE_WAY_NAV_ATTR, 0);
Adam Lesinski282e1812014-01-23 18:17:42 -08001456 printf("uses-configuration:");
1457 if (reqTouchScreen != 0) {
1458 printf(" reqTouchScreen='%d'", reqTouchScreen);
1459 }
1460 if (reqKeyboardType != 0) {
1461 printf(" reqKeyboardType='%d'", reqKeyboardType);
1462 }
1463 if (reqHardKeyboard != 0) {
1464 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1465 }
1466 if (reqNavigation != 0) {
1467 printf(" reqNavigation='%d'", reqNavigation);
1468 }
1469 if (reqFiveWayNav != 0) {
1470 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1471 }
1472 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001473 } else if (tag == "supports-input") {
1474 withinSupportsInput = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001475 } else if (tag == "supports-screens") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001476 smallScreen = AaptXml::getIntegerAttribute(tree,
1477 SMALL_SCREEN_ATTR, 1);
1478 normalScreen = AaptXml::getIntegerAttribute(tree,
1479 NORMAL_SCREEN_ATTR, 1);
1480 largeScreen = AaptXml::getIntegerAttribute(tree,
1481 LARGE_SCREEN_ATTR, 1);
1482 xlargeScreen = AaptXml::getIntegerAttribute(tree,
1483 XLARGE_SCREEN_ATTR, 1);
1484 anyDensity = AaptXml::getIntegerAttribute(tree,
1485 ANY_DENSITY_ATTR, 1);
1486 requiresSmallestWidthDp = AaptXml::getIntegerAttribute(tree,
1487 REQUIRES_SMALLEST_WIDTH_DP_ATTR, 0);
1488 compatibleWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1489 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, 0);
1490 largestWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1491 LARGEST_WIDTH_LIMIT_DP_ATTR, 0);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001492 } else if (tag == "feature-group") {
1493 withinFeatureGroup = true;
1494 FeatureGroup group;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001495 group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001496 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001497 SourcePos(manifestFile, tree.getLineNumber()).error(
1498 "ERROR getting 'android:label' attribute: %s", error.string());
Adam Lesinski2c72b682014-06-24 09:56:01 -07001499 goto bail;
1500 }
1501 featureGroups.add(group);
1502
Adam Lesinski282e1812014-01-23 18:17:42 -08001503 } else if (tag == "uses-feature") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001504 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001505 if (name != "" && error == "") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001506 const char* androidSchema =
1507 "http://schemas.android.com/apk/res/android";
Adam Lesinski282e1812014-01-23 18:17:42 -08001508
Adam Lesinski694d0a72016-04-06 16:12:04 -07001509 int32_t req = AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1,
1510 &error);
1511 if (error != "") {
1512 SourcePos(manifestFile, tree.getLineNumber()).error(
1513 "failed to read attribute 'android:required': %s",
1514 error.string());
1515 goto bail;
1516 }
1517
1518 int32_t version = AaptXml::getIntegerAttribute(tree, androidSchema,
1519 "version", 0, &error);
1520 if (error != "") {
1521 SourcePos(manifestFile, tree.getLineNumber()).error(
1522 "failed to read attribute 'android:version': %s",
1523 error.string());
1524 goto bail;
1525 }
1526
1527 commonFeatures.features.add(name, Feature(req != 0, version));
Adam Lesinski2c72b682014-06-24 09:56:01 -07001528 if (req) {
1529 addParentFeatures(&commonFeatures, name);
Adam Lesinski282e1812014-01-23 18:17:42 -08001530 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001531 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001532 int vers = AaptXml::getIntegerAttribute(tree,
Adam Lesinski282e1812014-01-23 18:17:42 -08001533 GL_ES_VERSION_ATTR, &error);
1534 if (error == "") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001535 if (vers > commonFeatures.openGLESVersion) {
1536 commonFeatures.openGLESVersion = vers;
1537 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001538 }
1539 }
1540 } else if (tag == "uses-permission") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001541 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001542 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001543 SourcePos(manifestFile, tree.getLineNumber()).error(
1544 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001545 goto bail;
1546 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001547
1548 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001549 SourcePos(manifestFile, tree.getLineNumber()).error(
1550 "ERROR: missing 'android:name' for uses-permission");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001551 goto bail;
1552 }
1553
1554 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, false);
1555
Adam Lesinski2386df22016-12-28 15:08:58 -05001556 const int32_t maxSdkVersion =
1557 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, -1);
Dianne Hackborncd154e92017-02-28 17:37:35 -08001558 const String8 requiredFeature = AaptXml::getAttribute(tree,
1559 REQUIRED_FEATURE_ATTR, &error);
1560 const String8 requiredNotFeature = AaptXml::getAttribute(tree,
1561 REQUIRED_NOT_FEATURE_ATTR, &error);
Adam Lesinski2386df22016-12-28 15:08:58 -05001562
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001563 if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1564 hasWriteExternalStoragePermission = true;
Adam Lesinski2386df22016-12-28 15:08:58 -05001565 writeExternalStoragePermissionMaxSdkVersion = maxSdkVersion;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001566 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1567 hasReadExternalStoragePermission = true;
1568 } else if (name == "android.permission.READ_PHONE_STATE") {
1569 hasReadPhoneStatePermission = true;
1570 } else if (name == "android.permission.READ_CONTACTS") {
1571 hasReadContactsPermission = true;
1572 } else if (name == "android.permission.WRITE_CONTACTS") {
1573 hasWriteContactsPermission = true;
1574 } else if (name == "android.permission.READ_CALL_LOG") {
1575 hasReadCallLogPermission = true;
1576 } else if (name == "android.permission.WRITE_CALL_LOG") {
1577 hasWriteCallLogPermission = true;
1578 }
1579
1580 printUsesPermission(name,
1581 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
Dianne Hackborncd154e92017-02-28 17:37:35 -08001582 maxSdkVersion, requiredFeature, requiredNotFeature);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001583
1584 } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
1585 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1586 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001587 SourcePos(manifestFile, tree.getLineNumber()).error(
1588 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001589 goto bail;
1590 }
1591
1592 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001593 SourcePos(manifestFile, tree.getLineNumber()).error(
1594 "ERROR: missing 'android:name' for uses-permission-sdk-23");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001595 goto bail;
1596 }
1597
1598 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, true);
1599
1600 printUsesPermissionSdk23(
1601 name, AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
1602
Adam Lesinski282e1812014-01-23 18:17:42 -08001603 } else if (tag == "uses-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001604 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001605 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001606 printf("uses-package:'%s'\n",
1607 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001608 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001609 SourcePos(manifestFile, tree.getLineNumber()).error(
1610 "ERROR getting 'android:name' attribute: %s", error.string());
1611 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001612 }
1613 } else if (tag == "original-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001614 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001615 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001616 printf("original-package:'%s'\n",
1617 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001618 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001619 SourcePos(manifestFile, tree.getLineNumber()).error(
1620 "ERROR getting 'android:name' attribute: %s", error.string());
1621 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001622 }
1623 } else if (tag == "supports-gl-texture") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001624 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001625 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001626 printf("supports-gl-texture:'%s'\n",
1627 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001628 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001629 SourcePos(manifestFile, tree.getLineNumber()).error(
1630 "ERROR getting 'android:name' attribute: %s", error.string());
1631 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001632 }
1633 } else if (tag == "compatible-screens") {
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001634 printCompatibleScreens(tree, &error);
1635 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001636 SourcePos(manifestFile, tree.getLineNumber()).error(
1637 "ERROR getting compatible screens: %s", error.string());
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001638 goto bail;
1639 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001640 depth--;
1641 } else if (tag == "package-verifier") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001642 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001643 if (name != "" && error == "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001644 String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR,
1645 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001646 if (publicKey != "" && error == "") {
1647 printf("package-verifier: name='%s' publicKey='%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001648 ResTable::normalizeForOutput(name.string()).string(),
1649 ResTable::normalizeForOutput(publicKey.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001650 }
1651 }
1652 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001653 } else if (depth == 3) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001654 withinActivity = false;
1655 withinReceiver = false;
1656 withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001657 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001658 hasIntentFilter = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001659 hasMetaHostPaymentCategory = false;
1660 hasMetaOffHostPaymentCategory = false;
1661 hasBindDeviceAdminPermission = false;
1662 hasBindInputMethodPermission = false;
1663 hasBindAccessibilityServicePermission = false;
1664 hasBindPrintServicePermission = false;
1665 hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001666 hasRequiredSafAttributes = false;
1667 hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001668 hasBindDreamServicePermission = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001669 if (withinApplication) {
1670 if(tag == "activity") {
1671 withinActivity = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001672 activityName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001673 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001674 SourcePos(manifestFile, tree.getLineNumber()).error(
1675 "ERROR getting 'android:name' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001676 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001677 goto bail;
1678 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001679
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001680 activityLabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1681 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001682 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001683 SourcePos(manifestFile, tree.getLineNumber()).error(
1684 "ERROR getting 'android:label' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001685 error.string());
1686 goto bail;
1687 }
1688
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001689 activityIcon = AaptXml::getResolvedAttribute(res, tree, ICON_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:icon' 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 activityBanner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1699 &error);
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001700 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001701 SourcePos(manifestFile, tree.getLineNumber()).error(
1702 "ERROR getting 'android:banner' attribute: %s",
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001703 error.string());
1704 goto bail;
1705 }
1706
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001707 int32_t orien = AaptXml::getResolvedIntegerAttribute(res, tree,
Michael Wrightec4fdec2013-09-06 16:50:52 -07001708 SCREEN_ORIENTATION_ATTR, &error);
1709 if (error == "") {
1710 if (orien == 0 || orien == 6 || orien == 8) {
1711 // Requests landscape, sensorLandscape, or reverseLandscape.
Adam Lesinski43158772015-11-11 15:13:55 -08001712 addImpliedFeature(
1713 &impliedFeatures, "android.hardware.screen.landscape",
1714 String8("one or more activities have specified a "
1715 "landscape orientation"),
1716 false);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001717 } else if (orien == 1 || orien == 7 || orien == 9) {
1718 // Requests portrait, sensorPortrait, or reversePortrait.
Adam Lesinski43158772015-11-11 15:13:55 -08001719 addImpliedFeature(
1720 &impliedFeatures, "android.hardware.screen.portrait",
1721 String8("one or more activities have specified a "
1722 "portrait orientation"),
1723 false);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001724 }
1725 }
1726 } else if (tag == "uses-library") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001727 String8 libraryName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001728 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001729 SourcePos(manifestFile, tree.getLineNumber()).error(
Michael Wrightec4fdec2013-09-06 16:50:52 -07001730 "ERROR getting 'android:name' attribute for uses-library"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001731 " %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001732 goto bail;
1733 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001734 int req = AaptXml::getIntegerAttribute(tree,
1735 REQUIRED_ATTR, 1);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001736 printf("uses-library%s:'%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001737 req ? "" : "-not-required", ResTable::normalizeForOutput(
1738 libraryName.string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001739 } else if (tag == "receiver") {
1740 withinReceiver = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001741 receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001742
1743 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001744 SourcePos(manifestFile, tree.getLineNumber()).error(
Michael Wrightec4fdec2013-09-06 16:50:52 -07001745 "ERROR getting 'android:name' attribute for receiver:"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001746 " %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001747 goto bail;
1748 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001749
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001750 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1751 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001752 if (error == "") {
1753 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1754 hasBindDeviceAdminPermission = true;
1755 }
1756 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001757 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001758 "ERROR getting 'android:permission' attribute for"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001759 " receiver '%s': %s",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001760 receiverName.string(), error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001761 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001762 } else if (tag == "service") {
1763 withinService = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001764 serviceName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001765
1766 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001767 SourcePos(manifestFile, tree.getLineNumber()).error(
1768 "ERROR getting 'android:name' attribute for "
1769 "service:%s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001770 goto bail;
1771 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001772
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001773 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1774 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001775 if (error == "") {
1776 if (permission == "android.permission.BIND_INPUT_METHOD") {
1777 hasBindInputMethodPermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001778 } else if (permission ==
1779 "android.permission.BIND_ACCESSIBILITY_SERVICE") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001780 hasBindAccessibilityServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001781 } else if (permission ==
1782 "android.permission.BIND_PRINT_SERVICE") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001783 hasBindPrintServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001784 } else if (permission ==
1785 "android.permission.BIND_NFC_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07001786 hasBindNfcServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001787 } else if (permission ==
1788 "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001789 hasBindNotificationListenerServicePermission = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001790 } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
1791 hasBindDreamServicePermission = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001792 }
1793 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001794 SourcePos(manifestFile, tree.getLineNumber()).error(
1795 "ERROR getting 'android:permission' attribute for "
1796 "service '%s': %s", serviceName.string(), error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001797 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001798 } else if (tag == "provider") {
1799 withinProvider = true;
1800
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001801 bool exported = AaptXml::getResolvedIntegerAttribute(res, tree,
1802 EXPORTED_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001803 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001804 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001805 "ERROR getting 'android:exported' attribute for provider:"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001806 " %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001807 goto bail;
1808 }
1809
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001810 bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute(
1811 res, tree, GRANT_URI_PERMISSIONS_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:grantUriPermissions' attribute for "
Adam Lesinski10de3af12016-07-13 10:14:03 -07001815 "provider: %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001816 goto bail;
1817 }
1818
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001819 String8 permission = AaptXml::getResolvedAttribute(res, tree,
1820 PERMISSION_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(
1823 "ERROR getting 'android:permission' attribute for "
1824 "provider: %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001825 goto bail;
1826 }
1827
1828 hasRequiredSafAttributes |= exported && grantUriPermissions &&
1829 permission == "android.permission.MANAGE_DOCUMENTS";
1830
Michael Wrightec4fdec2013-09-06 16:50:52 -07001831 } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001832 String8 metaDataName = AaptXml::getResolvedAttribute(res, tree,
1833 NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001834 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001835 SourcePos(manifestFile, tree.getLineNumber()).error(
1836 "ERROR getting 'android:name' attribute for "
1837 "meta-data: %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001838 goto bail;
1839 }
Maurice Chu2675f762013-10-22 17:33:11 -07001840 printf("meta-data: name='%s' ",
1841 ResTable::normalizeForOutput(metaDataName.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001842 printResolvedResourceAttribute(res, tree, VALUE_ATTR, String8("value"),
Maurice Chu76327312013-10-16 18:28:46 -07001843 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001844 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001845 // Try looking for a RESOURCE_ATTR
1846 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001847 printResolvedResourceAttribute(res, tree, RESOURCE_ATTR,
Maurice Chu76327312013-10-16 18:28:46 -07001848 String8("resource"), &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001849 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001850 SourcePos(manifestFile, tree.getLineNumber()).error(
1851 "ERROR getting 'android:value' or "
Maurice Chu76327312013-10-16 18:28:46 -07001852 "'android:resource' attribute for "
Adam Lesinski10de3af12016-07-13 10:14:03 -07001853 "meta-data: %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001854 goto bail;
1855 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001856 }
Maurice Chu76327312013-10-16 18:28:46 -07001857 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001858 } else if (withinSupportsInput && tag == "input-type") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001859 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001860 if (name != "" && error == "") {
1861 supportedInput.add(name);
1862 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001863 SourcePos(manifestFile, tree.getLineNumber()).error(
1864 "ERROR getting 'android:name' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001865 error.string());
1866 goto bail;
1867 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001868 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001869 } else if (withinFeatureGroup && tag == "uses-feature") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001870 const String8 androidSchema("http://schemas.android.com/apk/res/android");
Adam Lesinski2c72b682014-06-24 09:56:01 -07001871 FeatureGroup& top = featureGroups.editTop();
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001872
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001873 String8 name = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001874 if (name != "" && error == "") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001875 Feature feature(true);
1876
1877 int32_t featureVers = AaptXml::getIntegerAttribute(
1878 tree, androidSchema.string(), "version", 0, &error);
1879 if (error == "") {
1880 feature.version = featureVers;
1881 } else {
1882 SourcePos(manifestFile, tree.getLineNumber()).error(
1883 "failed to read attribute 'android:version': %s",
1884 error.string());
1885 goto bail;
1886 }
1887
1888 top.features.add(name, feature);
Adam Lesinskid3edfde2014-08-08 17:32:44 -07001889 addParentFeatures(&top, name);
Adam Lesinski694d0a72016-04-06 16:12:04 -07001890
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001891 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001892 int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR,
1893 &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001894 if (error == "") {
1895 if (vers > top.openGLESVersion) {
1896 top.openGLESVersion = vers;
1897 }
1898 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001899 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001900 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001901 } else if (depth == 4) {
1902 if (tag == "intent-filter") {
1903 hasIntentFilter = true;
1904 withinIntentFilter = true;
1905 actMainActivity = false;
1906 actWidgetReceivers = false;
1907 actImeService = false;
1908 actWallpaperService = false;
1909 actAccessibilityService = false;
1910 actPrintService = false;
1911 actDeviceAdminEnabled = false;
1912 actHostApduService = false;
1913 actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001914 actDocumentsProvider = false;
1915 actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001916 actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001917 actCamera = false;
1918 actCameraSecure = false;
1919 catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001920 } else if (withinService && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001921 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001922 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001923 SourcePos(manifestFile, tree.getLineNumber()).error(
1924 "ERROR getting 'android:name' attribute for "
1925 "meta-data tag in service '%s': %s", serviceName.string(),
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001926 error.string());
Adam Lesinski94fc9122013-09-30 17:16:09 -07001927 goto bail;
1928 }
1929
1930 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1931 name == "android.nfc.cardemulation.off_host_apdu_service") {
1932 bool offHost = true;
1933 if (name == "android.nfc.cardemulation.host_apdu_service") {
1934 offHost = false;
1935 }
1936
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001937 String8 xmlPath = AaptXml::getResolvedAttribute(res, tree,
1938 RESOURCE_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001939 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001940 SourcePos(manifestFile, tree.getLineNumber()).error(
1941 "ERROR getting 'android:resource' attribute for "
1942 "meta-data tag in service '%s': %s",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001943 serviceName.string(), error.string());
Adam Lesinski94fc9122013-09-30 17:16:09 -07001944 goto bail;
1945 }
1946
1947 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1948 offHost, &error);
1949 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001950 SourcePos(manifestFile, tree.getLineNumber()).error(
1951 "ERROR getting AID category for service '%s'",
Adam Lesinski94fc9122013-09-30 17:16:09 -07001952 serviceName.string());
1953 goto bail;
1954 }
1955
1956 const size_t catLen = categories.size();
1957 for (size_t i = 0; i < catLen; i++) {
1958 bool paymentCategory = (categories[i] == "payment");
1959 if (offHost) {
1960 hasMetaOffHostPaymentCategory |= paymentCategory;
1961 } else {
1962 hasMetaHostPaymentCategory |= paymentCategory;
1963 }
1964 }
1965 }
1966 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001967 } else if ((depth == 5) && withinIntentFilter) {
1968 String8 action;
1969 if (tag == "action") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001970 action = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001971 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001972 SourcePos(manifestFile, tree.getLineNumber()).error(
1973 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001974 goto bail;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001975 }
1976
Adam Lesinskia5018c92013-09-30 16:23:15 -07001977 if (withinActivity) {
1978 if (action == "android.intent.action.MAIN") {
1979 isMainActivity = true;
1980 actMainActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001981 } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
1982 action == "android.media.action.VIDEO_CAMERA") {
1983 actCamera = true;
1984 } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
1985 actCameraSecure = true;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001986 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001987 } else if (withinReceiver) {
1988 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1989 actWidgetReceivers = true;
1990 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1991 actDeviceAdminEnabled = true;
1992 }
1993 } else if (withinService) {
1994 if (action == "android.view.InputMethod") {
1995 actImeService = true;
1996 } else if (action == "android.service.wallpaper.WallpaperService") {
1997 actWallpaperService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001998 } else if (action ==
1999 "android.accessibilityservice.AccessibilityService") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07002000 actAccessibilityService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002001 } else if (action =="android.printservice.PrintService") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07002002 actPrintService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002003 } else if (action ==
2004 "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07002005 actHostApduService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002006 } else if (action ==
2007 "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07002008 actOffHostApduService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002009 } else if (action ==
2010 "android.service.notification.NotificationListenerService") {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002011 actNotificationListenerService = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04002012 } else if (action == "android.service.dreams.DreamService") {
2013 actDreamService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002014 }
2015 } else if (withinProvider) {
2016 if (action == "android.content.action.DOCUMENTS_PROVIDER") {
2017 actDocumentsProvider = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07002018 }
2019 }
2020 if (action == "android.intent.action.SEARCH") {
2021 isSearchable = true;
2022 }
2023 }
2024
2025 if (tag == "category") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07002026 String8 category = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07002027 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07002028 SourcePos(manifestFile, tree.getLineNumber()).error(
2029 "ERROR getting 'name' attribute: %s", error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07002030 goto bail;
2031 }
2032 if (withinActivity) {
2033 if (category == "android.intent.category.LAUNCHER") {
2034 isLauncherActivity = true;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08002035 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
2036 isLeanbackLauncherActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002037 } else if (category == "android.intent.category.HOME") {
2038 catLauncher = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08002039 }
2040 }
2041 }
2042 }
2043 }
2044
2045 // Pre-1.6 implicitly granted permission compatibility logic
2046 if (targetSdk < 4) {
2047 if (!hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002048 printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
2049 printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
2050 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002051 hasWriteExternalStoragePermission = true;
2052 }
2053 if (!hasReadPhoneStatePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002054 printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
2055 printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
2056 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002057 }
2058 }
2059
2060 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
2061 // force them to always take READ_EXTERNAL_STORAGE as well. We always
2062 // do this (regardless of target API version) because we can't have
2063 // an app with write permission but not read permission.
2064 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
Adam Lesinski2386df22016-12-28 15:08:58 -05002065 printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
2066 false /* optional */, writeExternalStoragePermissionMaxSdkVersion);
Adam Lesinski58f1f362013-11-12 12:59:08 -08002067 printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
Adam Lesinski2386df22016-12-28 15:08:58 -05002068 String8("requested WRITE_EXTERNAL_STORAGE"),
2069 writeExternalStoragePermissionMaxSdkVersion);
Adam Lesinski282e1812014-01-23 18:17:42 -08002070 }
2071
2072 // Pre-JellyBean call log permission compatibility.
2073 if (targetSdk < 16) {
2074 if (!hasReadCallLogPermission && hasReadContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002075 printUsesPermission(String8("android.permission.READ_CALL_LOG"));
2076 printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
2077 String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002078 }
2079 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002080 printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
2081 printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
2082 String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002083 }
2084 }
2085
Adam Lesinskica955a42016-08-01 16:44:29 -07002086 // If the app hasn't declared the touchscreen as a feature requirement (either
2087 // directly or implied, required or not), then the faketouch feature is implied.
2088 if (!hasFeature("android.hardware.touchscreen", commonFeatures, impliedFeatures)) {
2089 addImpliedFeature(&impliedFeatures, "android.hardware.faketouch",
Adam Lesinski43158772015-11-11 15:13:55 -08002090 String8("default feature for all apps"), false);
Adam Lesinskica955a42016-08-01 16:44:29 -07002091 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07002092
2093 const size_t numFeatureGroups = featureGroups.size();
2094 if (numFeatureGroups == 0) {
2095 // If no <feature-group> tags were defined, apply auto-implied features.
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002096 printDefaultFeatureGroup(commonFeatures, impliedFeatures);
Adam Lesinski2c72b682014-06-24 09:56:01 -07002097
2098 } else {
2099 // <feature-group> tags are defined, so we ignore implied features and
2100 for (size_t i = 0; i < numFeatureGroups; i++) {
2101 FeatureGroup& grp = featureGroups.editItemAt(i);
2102
Adam Lesinskid7a94da2014-07-25 14:38:54 -07002103 if (commonFeatures.openGLESVersion > grp.openGLESVersion) {
2104 grp.openGLESVersion = commonFeatures.openGLESVersion;
2105 }
2106
Adam Lesinski2c72b682014-06-24 09:56:01 -07002107 // Merge the features defined in the top level (not inside a <feature-group>)
2108 // with this feature group.
2109 const size_t numCommonFeatures = commonFeatures.features.size();
2110 for (size_t j = 0; j < numCommonFeatures; j++) {
2111 if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07002112 grp.features.add(commonFeatures.features.keyAt(j),
2113 commonFeatures.features[j]);
Adam Lesinski2c72b682014-06-24 09:56:01 -07002114 }
2115 }
2116
Adam Lesinski73a05112014-12-08 12:53:17 -08002117 if (!grp.features.isEmpty()) {
Adam Lesinski2c72b682014-06-24 09:56:01 -07002118 printFeatureGroup(grp);
Adam Lesinski282e1812014-01-23 18:17:42 -08002119 }
2120 }
2121 }
2122
Adam Lesinski282e1812014-01-23 18:17:42 -08002123
Adam Lesinski282e1812014-01-23 18:17:42 -08002124 if (hasWidgetReceivers) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002125 printComponentPresence("app-widget");
Adam Lesinski282e1812014-01-23 18:17:42 -08002126 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002127 if (hasDeviceAdminReceiver) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002128 printComponentPresence("device-admin");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002129 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002130 if (hasImeService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002131 printComponentPresence("ime");
Adam Lesinski282e1812014-01-23 18:17:42 -08002132 }
2133 if (hasWallpaperService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002134 printComponentPresence("wallpaper");
Adam Lesinski282e1812014-01-23 18:17:42 -08002135 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002136 if (hasAccessibilityService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002137 printComponentPresence("accessibility");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002138 }
2139 if (hasPrintService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002140 printComponentPresence("print-service");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002141 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07002142 if (hasPaymentService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002143 printComponentPresence("payment");
2144 }
2145 if (isSearchable) {
2146 printComponentPresence("search");
2147 }
2148 if (hasDocumentsProvider) {
2149 printComponentPresence("document-provider");
2150 }
2151 if (hasLauncher) {
2152 printComponentPresence("launcher");
2153 }
2154 if (hasNotificationListenerService) {
2155 printComponentPresence("notification-listener");
2156 }
John Spurlockeb8d1be2014-06-25 17:46:15 -04002157 if (hasDreamService) {
2158 printComponentPresence("dream");
2159 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002160 if (hasCameraActivity) {
2161 printComponentPresence("camera");
2162 }
2163 if (hasCameraSecureActivity) {
2164 printComponentPresence("camera-secure");
2165 }
2166
2167 if (hasMainActivity) {
2168 printf("main\n");
Adam Lesinski94fc9122013-09-30 17:16:09 -07002169 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002170 if (hasOtherActivities) {
2171 printf("other-activities\n");
2172 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002173 if (hasOtherReceivers) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002174 printf("other-receivers\n");
2175 }
2176 if (hasOtherServices) {
2177 printf("other-services\n");
2178 }
2179
2180 // For modern apps, if screen size buckets haven't been specified
2181 // but the new width ranges have, then infer the buckets from them.
2182 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
2183 && requiresSmallestWidthDp > 0) {
2184 int compatWidth = compatibleWidthLimitDp;
2185 if (compatWidth <= 0) {
2186 compatWidth = requiresSmallestWidthDp;
2187 }
2188 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
2189 smallScreen = -1;
2190 } else {
2191 smallScreen = 0;
2192 }
2193 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
2194 normalScreen = -1;
2195 } else {
2196 normalScreen = 0;
2197 }
2198 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
2199 largeScreen = -1;
2200 } else {
2201 largeScreen = 0;
2202 }
2203 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
2204 xlargeScreen = -1;
2205 } else {
2206 xlargeScreen = 0;
2207 }
2208 }
2209
2210 // Determine default values for any unspecified screen sizes,
2211 // based on the target SDK of the package. As of 4 (donut)
2212 // the screen size support was introduced, so all default to
2213 // enabled.
2214 if (smallScreen > 0) {
2215 smallScreen = targetSdk >= 4 ? -1 : 0;
2216 }
2217 if (normalScreen > 0) {
2218 normalScreen = -1;
2219 }
2220 if (largeScreen > 0) {
2221 largeScreen = targetSdk >= 4 ? -1 : 0;
2222 }
2223 if (xlargeScreen > 0) {
2224 // Introduced in Gingerbread.
2225 xlargeScreen = targetSdk >= 9 ? -1 : 0;
2226 }
2227 if (anyDensity > 0) {
2228 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
2229 || compatibleWidthLimitDp > 0) ? -1 : 0;
2230 }
2231 printf("supports-screens:");
2232 if (smallScreen != 0) {
2233 printf(" 'small'");
2234 }
2235 if (normalScreen != 0) {
2236 printf(" 'normal'");
2237 }
2238 if (largeScreen != 0) {
2239 printf(" 'large'");
2240 }
2241 if (xlargeScreen != 0) {
2242 printf(" 'xlarge'");
2243 }
2244 printf("\n");
2245 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
2246 if (requiresSmallestWidthDp > 0) {
2247 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
2248 }
2249 if (compatibleWidthLimitDp > 0) {
2250 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
2251 }
2252 if (largestWidthLimitDp > 0) {
2253 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
2254 }
2255
2256 printf("locales:");
2257 const size_t NL = locales.size();
2258 for (size_t i=0; i<NL; i++) {
2259 const char* localeStr = locales[i].string();
2260 if (localeStr == NULL || strlen(localeStr) == 0) {
2261 localeStr = "--_--";
2262 }
2263 printf(" '%s'", localeStr);
2264 }
2265 printf("\n");
2266
2267 printf("densities:");
2268 const size_t ND = densities.size();
2269 for (size_t i=0; i<ND; i++) {
2270 printf(" '%d'", densities[i]);
2271 }
2272 printf("\n");
2273
2274 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
2275 if (dir != NULL) {
2276 if (dir->getFileCount() > 0) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002277 SortedVector<String8> architectures;
Adam Lesinski282e1812014-01-23 18:17:42 -08002278 for (size_t i=0; i<dir->getFileCount(); i++) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002279 architectures.add(ResTable::normalizeForOutput(
2280 dir->getFileName(i).string()));
Adam Lesinski282e1812014-01-23 18:17:42 -08002281 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07002282
2283 bool outputAltNativeCode = false;
2284 // A multiArch package is one that contains 64-bit and
2285 // 32-bit versions of native code and expects 3rd-party
2286 // apps to load these native code libraries. Since most
2287 // 64-bit systems also support 32-bit apps, the apps
2288 // loading this multiArch package's code may be either
2289 // 32-bit or 64-bit.
2290 if (hasMultiArch) {
2291 // If this is a multiArch package, report the 64-bit
2292 // version only. Then as a separate entry, report the
2293 // rest.
2294 //
2295 // If we report the 32-bit architecture, this APK will
2296 // be installed on a 32-bit device, causing a large waste
2297 // of bandwidth and disk space. This assumes that
2298 // the developer of the multiArch package has also
2299 // made a version that is 32-bit only.
2300 String8 intel64("x86_64");
2301 String8 arm64("arm64-v8a");
2302 ssize_t index = architectures.indexOf(intel64);
2303 if (index < 0) {
2304 index = architectures.indexOf(arm64);
2305 }
2306
2307 if (index >= 0) {
2308 printf("native-code: '%s'\n", architectures[index].string());
2309 architectures.removeAt(index);
2310 outputAltNativeCode = true;
2311 }
2312 }
2313
2314 const size_t archCount = architectures.size();
2315 if (archCount > 0) {
2316 if (outputAltNativeCode) {
2317 printf("alt-");
2318 }
2319 printf("native-code:");
2320 for (size_t i = 0; i < archCount; i++) {
2321 printf(" '%s'", architectures[i].string());
2322 }
2323 printf("\n");
2324 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002325 }
2326 delete dir;
2327 }
2328 } else if (strcmp("badger", option) == 0) {
2329 printf("%s", CONSOLE_DATA);
2330 } else if (strcmp("configurations", option) == 0) {
2331 Vector<ResTable_config> configs;
2332 res.getConfigurations(&configs);
2333 const size_t N = configs.size();
2334 for (size_t i=0; i<N; i++) {
2335 printf("%s\n", configs[i].toString().string());
2336 }
2337 } else {
2338 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2339 goto bail;
2340 }
2341 }
2342
2343 result = NO_ERROR;
2344
2345bail:
Adam Lesinski10de3af12016-07-13 10:14:03 -07002346 if (SourcePos::hasErrors()) {
2347 SourcePos::printErrors(stderr);
2348 }
2349
Adam Lesinski282e1812014-01-23 18:17:42 -08002350 if (asset) {
2351 delete asset;
2352 }
2353 return (result != NO_ERROR);
2354}
2355
2356
2357/*
2358 * Handle the "add" command, which wants to add files to a new or
2359 * pre-existing archive.
2360 */
2361int doAdd(Bundle* bundle)
2362{
2363 ZipFile* zip = NULL;
2364 status_t result = UNKNOWN_ERROR;
2365 const char* zipFileName;
2366
2367 if (bundle->getUpdate()) {
2368 /* avoid confusion */
2369 fprintf(stderr, "ERROR: can't use '-u' with add\n");
2370 goto bail;
2371 }
2372
2373 if (bundle->getFileSpecCount() < 1) {
2374 fprintf(stderr, "ERROR: must specify zip file name\n");
2375 goto bail;
2376 }
2377 zipFileName = bundle->getFileSpecEntry(0);
2378
2379 if (bundle->getFileSpecCount() < 2) {
2380 fprintf(stderr, "NOTE: nothing to do\n");
2381 goto bail;
2382 }
2383
2384 zip = openReadWrite(zipFileName, true);
2385 if (zip == NULL) {
2386 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2387 goto bail;
2388 }
2389
2390 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2391 const char* fileName = bundle->getFileSpecEntry(i);
2392
2393 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
2394 printf(" '%s'... (from gzip)\n", fileName);
2395 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
2396 } else {
2397 if (bundle->getJunkPath()) {
2398 String8 storageName = String8(fileName).getPathLeaf();
Maurice Chu2675f762013-10-22 17:33:11 -07002399 printf(" '%s' as '%s'...\n", fileName,
2400 ResTable::normalizeForOutput(storageName.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08002401 result = zip->add(fileName, storageName.string(),
2402 bundle->getCompressionMethod(), NULL);
2403 } else {
2404 printf(" '%s'...\n", fileName);
2405 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2406 }
2407 }
2408 if (result != NO_ERROR) {
2409 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2410 if (result == NAME_NOT_FOUND) {
2411 fprintf(stderr, ": file not found\n");
2412 } else if (result == ALREADY_EXISTS) {
2413 fprintf(stderr, ": already exists in archive\n");
2414 } else {
2415 fprintf(stderr, "\n");
2416 }
2417 goto bail;
2418 }
2419 }
2420
2421 result = NO_ERROR;
2422
2423bail:
2424 delete zip;
2425 return (result != NO_ERROR);
2426}
2427
2428
2429/*
2430 * Delete files from an existing archive.
2431 */
2432int doRemove(Bundle* bundle)
2433{
2434 ZipFile* zip = NULL;
2435 status_t result = UNKNOWN_ERROR;
2436 const char* zipFileName;
2437
2438 if (bundle->getFileSpecCount() < 1) {
2439 fprintf(stderr, "ERROR: must specify zip file name\n");
2440 goto bail;
2441 }
2442 zipFileName = bundle->getFileSpecEntry(0);
2443
2444 if (bundle->getFileSpecCount() < 2) {
2445 fprintf(stderr, "NOTE: nothing to do\n");
2446 goto bail;
2447 }
2448
2449 zip = openReadWrite(zipFileName, false);
2450 if (zip == NULL) {
2451 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2452 zipFileName);
2453 goto bail;
2454 }
2455
2456 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2457 const char* fileName = bundle->getFileSpecEntry(i);
2458 ZipEntry* entry;
2459
2460 entry = zip->getEntryByName(fileName);
2461 if (entry == NULL) {
2462 printf(" '%s' NOT FOUND\n", fileName);
2463 continue;
2464 }
2465
2466 result = zip->remove(entry);
2467
2468 if (result != NO_ERROR) {
2469 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2470 bundle->getFileSpecEntry(i), zipFileName);
2471 goto bail;
2472 }
2473 }
2474
2475 /* update the archive */
2476 zip->flush();
2477
2478bail:
2479 delete zip;
2480 return (result != NO_ERROR);
2481}
2482
Adam Lesinski3921e872014-05-13 10:56:25 -07002483static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002484 const size_t numDirs = dir->getDirs().size();
2485 for (size_t i = 0; i < numDirs; i++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002486 bool ignore = ignoreConfig;
2487 const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2488 const char* dirStr = subDir->getLeaf().string();
2489 if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2490 ignore = true;
2491 }
2492 status_t err = addResourcesToBuilder(subDir, builder, ignore);
Adam Lesinskifab50872014-04-16 14:40:42 -07002493 if (err != NO_ERROR) {
2494 return err;
2495 }
2496 }
2497
2498 const size_t numFiles = dir->getFiles().size();
2499 for (size_t i = 0; i < numFiles; i++) {
2500 sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2501 const size_t numConfigs = gp->getFiles().size();
2502 for (size_t j = 0; j < numConfigs; j++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002503 status_t err = NO_ERROR;
Adam Lesinskic7614e52017-03-16 16:54:23 -07002504 const sp<AaptFile>& file = gp->getFiles().valueAt(j);
2505 if (!file->hasData()) {
2506 // Empty files do not get written.
2507 continue;
Adam Lesinski3921e872014-05-13 10:56:25 -07002508 }
Adam Lesinskic7614e52017-03-16 16:54:23 -07002509
2510 if (ignoreConfig) {
2511 err = builder->getBaseSplit()->addEntry(gp->getPath(), file);
2512 } else {
2513 err = builder->addEntry(gp->getPath(), file);
2514 }
2515
Adam Lesinskifab50872014-04-16 14:40:42 -07002516 if (err != NO_ERROR) {
2517 fprintf(stderr, "Failed to add %s (%s) to builder.\n",
Adam Lesinskic7614e52017-03-16 16:54:23 -07002518 gp->getPath().string(), file->getPrintableSource().string());
Adam Lesinskifab50872014-04-16 14:40:42 -07002519 return err;
2520 }
2521 }
2522 }
2523 return NO_ERROR;
2524}
2525
2526static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2527 if (split->isBase()) {
2528 return original;
2529 }
2530
2531 String8 ext(original.getPathExtension());
2532 if (ext == String8(".apk")) {
2533 return String8::format("%s_%s%s",
2534 original.getBasePath().string(),
2535 split->getDirectorySafeName().string(),
2536 ext.string());
2537 }
2538
2539 return String8::format("%s_%s", original.string(),
2540 split->getDirectorySafeName().string());
2541}
Adam Lesinski282e1812014-01-23 18:17:42 -08002542
2543/*
2544 * Package up an asset directory and associated application files.
2545 */
2546int doPackage(Bundle* bundle)
2547{
2548 const char* outputAPKFile;
2549 int retVal = 1;
2550 status_t err;
2551 sp<AaptAssets> assets;
2552 int N;
2553 FILE* fp;
2554 String8 dependencyFile;
Adam Lesinskifab50872014-04-16 14:40:42 -07002555 sp<ApkBuilder> builder;
Adam Lesinski282e1812014-01-23 18:17:42 -08002556
Anton Krumina2ef5c02014-03-12 14:46:44 -07002557 // -c en_XA or/and ar_XB means do pseudolocalization
Adam Lesinskifab50872014-04-16 14:40:42 -07002558 sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2559 err = configFilter->parse(bundle->getConfigurations());
Adam Lesinski282e1812014-01-23 18:17:42 -08002560 if (err != NO_ERROR) {
2561 goto bail;
2562 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002563 if (configFilter->containsPseudo()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002564 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2565 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002566 if (configFilter->containsPseudoBidi()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002567 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
Adam Lesinski282e1812014-01-23 18:17:42 -08002568 }
2569
2570 N = bundle->getFileSpecCount();
2571 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
Adam Lesinski09384302014-01-22 16:07:42 -08002572 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002573 fprintf(stderr, "ERROR: no input files\n");
2574 goto bail;
2575 }
2576
2577 outputAPKFile = bundle->getOutputAPKFile();
2578
2579 // Make sure the filenames provided exist and are of the appropriate type.
2580 if (outputAPKFile) {
2581 FileType type;
2582 type = getFileType(outputAPKFile);
2583 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2584 fprintf(stderr,
2585 "ERROR: output file '%s' exists but is not regular file\n",
2586 outputAPKFile);
2587 goto bail;
2588 }
2589 }
2590
2591 // Load the assets.
2592 assets = new AaptAssets();
2593
2594 // Set up the resource gathering in assets if we're going to generate
2595 // dependency files. Every time we encounter a resource while slurping
2596 // the tree, we'll add it to these stores so we have full resource paths
2597 // to write to a dependency file.
2598 if (bundle->getGenDependencies()) {
2599 sp<FilePathStore> resPathStore = new FilePathStore;
2600 assets->setFullResPaths(resPathStore);
2601 sp<FilePathStore> assetPathStore = new FilePathStore;
2602 assets->setFullAssetPaths(assetPathStore);
2603 }
2604
2605 err = assets->slurpFromArgs(bundle);
2606 if (err < 0) {
2607 goto bail;
2608 }
2609
2610 if (bundle->getVerbose()) {
2611 assets->print(String8());
2612 }
2613
Adam Lesinskifab50872014-04-16 14:40:42 -07002614 // Create the ApkBuilder, which will collect the compiled files
2615 // to write to the final APK (or sets of APKs if we are building
2616 // a Split APK.
2617 builder = new ApkBuilder(configFilter);
2618
2619 // If we are generating a Split APK, find out which configurations to split on.
2620 if (bundle->getSplitConfigurations().size() > 0) {
2621 const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2622 const size_t numSplits = splitStrs.size();
2623 for (size_t i = 0; i < numSplits; i++) {
2624 std::set<ConfigDescription> configs;
2625 if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2626 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2627 goto bail;
2628 }
2629
2630 err = builder->createSplitForConfigs(configs);
2631 if (err != NO_ERROR) {
2632 goto bail;
2633 }
2634 }
2635 }
2636
Adam Lesinski282e1812014-01-23 18:17:42 -08002637 // If they asked for any fileAs that need to be compiled, do so.
2638 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002639 err = buildResources(bundle, assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002640 if (err != 0) {
2641 goto bail;
2642 }
2643 }
2644
2645 // At this point we've read everything and processed everything. From here
2646 // on out it's just writing output files.
2647 if (SourcePos::hasErrors()) {
2648 goto bail;
2649 }
2650
2651 // Update symbols with information about which ones are needed as Java symbols.
2652 assets->applyJavaSymbols();
2653 if (SourcePos::hasErrors()) {
2654 goto bail;
2655 }
2656
2657 // If we've been asked to generate a dependency file, do that here
2658 if (bundle->getGenDependencies()) {
2659 // If this is the packaging step, generate the dependency file next to
2660 // the output apk (e.g. bin/resources.ap_.d)
2661 if (outputAPKFile) {
2662 dependencyFile = String8(outputAPKFile);
2663 // Add the .d extension to the dependency file.
2664 dependencyFile.append(".d");
2665 } else {
2666 // Else if this is the R.java dependency generation step,
2667 // generate the dependency file in the R.java package subdirectory
2668 // e.g. gen/com/foo/app/R.java.d
2669 dependencyFile = String8(bundle->getRClassDir());
2670 dependencyFile.appendPath("R.java.d");
2671 }
2672 // Make sure we have a clean dependency file to start with
2673 fp = fopen(dependencyFile, "w");
2674 fclose(fp);
2675 }
2676
2677 // Write out R.java constants
2678 if (!assets->havePrivateSymbols()) {
2679 if (bundle->getCustomPackage() == NULL) {
2680 // Write the R.java file into the appropriate class directory
2681 // e.g. gen/com/foo/app/R.java
Adam Lesinski1e4663852014-08-15 14:47:28 -07002682 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002683 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002684 } else {
2685 const String8 customPkg(bundle->getCustomPackage());
Adam Lesinski1e4663852014-08-15 14:47:28 -07002686 err = writeResourceSymbols(bundle, assets, customPkg, true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002687 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002688 }
2689 if (err < 0) {
2690 goto bail;
2691 }
2692 // If we have library files, we're going to write our R.java file into
2693 // the appropriate class directory for those libraries as well.
2694 // e.g. gen/com/foo/app/lib/R.java
2695 if (bundle->getExtraPackages() != NULL) {
2696 // Split on colon
2697 String8 libs(bundle->getExtraPackages());
2698 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2699 while (packageString != NULL) {
2700 // Write the R.java file out with the correct package name
Marcin Kosiba0f3a5a62014-09-11 13:48:48 +01002701 err = writeResourceSymbols(bundle, assets, String8(packageString), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002702 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002703 if (err < 0) {
2704 goto bail;
2705 }
2706 packageString = strtok(NULL, ":");
2707 }
2708 libs.unlockBuffer();
2709 }
2710 } else {
Adam Lesinski1e4663852014-08-15 14:47:28 -07002711 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002712 if (err < 0) {
2713 goto bail;
2714 }
Adam Lesinski1e4663852014-08-15 14:47:28 -07002715 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002716 if (err < 0) {
2717 goto bail;
2718 }
2719 }
2720
2721 // Write out the ProGuard file
2722 err = writeProguardFile(bundle, assets);
2723 if (err < 0) {
2724 goto bail;
2725 }
2726
Rohit Agrawal86229cb2016-04-21 16:29:58 -07002727 // Write out the Main Dex ProGuard file
2728 err = writeMainDexProguardFile(bundle, assets);
2729 if (err < 0) {
2730 goto bail;
2731 }
2732
Adam Lesinski282e1812014-01-23 18:17:42 -08002733 // Write the apk
2734 if (outputAPKFile) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002735 // Gather all resources and add them to the APK Builder. The builder will then
2736 // figure out which Split they belong in.
2737 err = addResourcesToBuilder(assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002738 if (err != NO_ERROR) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002739 goto bail;
2740 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002741
2742 const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2743 const size_t numSplits = splits.size();
2744 for (size_t i = 0; i < numSplits; i++) {
2745 const sp<ApkSplit>& split = splits[i];
2746 String8 outputPath = buildApkName(String8(outputAPKFile), split);
2747 err = writeAPK(bundle, outputPath, split);
2748 if (err != NO_ERROR) {
2749 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2750 goto bail;
2751 }
2752 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002753 }
2754
2755 // If we've been asked to generate a dependency file, we need to finish up here.
2756 // the writeResourceSymbols and writeAPK functions have already written the target
2757 // half of the dependency file, now we need to write the prerequisites. (files that
2758 // the R.java file or .ap_ file depend on)
2759 if (bundle->getGenDependencies()) {
2760 // Now that writeResourceSymbols or writeAPK has taken care of writing
2761 // the targets to our dependency file, we'll write the prereqs
2762 fp = fopen(dependencyFile, "a+");
2763 fprintf(fp, " : ");
2764 bool includeRaw = (outputAPKFile != NULL);
2765 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2766 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2767 // and therefore was not added to our pathstores during slurping
2768 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2769 fclose(fp);
2770 }
2771
2772 retVal = 0;
2773bail:
2774 if (SourcePos::hasErrors()) {
2775 SourcePos::printErrors(stderr);
2776 }
2777 return retVal;
2778}
2779
2780/*
2781 * Do PNG Crunching
2782 * PRECONDITIONS
2783 * -S flag points to a source directory containing drawable* folders
2784 * -C flag points to destination directory. The folder structure in the
2785 * source directory will be mirrored to the destination (cache) directory
2786 *
2787 * POSTCONDITIONS
2788 * Destination directory will be updated to match the PNG files in
Maurice Chu2675f762013-10-22 17:33:11 -07002789 * the source directory.
Adam Lesinski282e1812014-01-23 18:17:42 -08002790 */
2791int doCrunch(Bundle* bundle)
2792{
2793 fprintf(stdout, "Crunching PNG Files in ");
2794 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2795 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2796
2797 updatePreProcessedCache(bundle);
2798
2799 return NO_ERROR;
2800}
2801
2802/*
2803 * Do PNG Crunching on a single flag
2804 * -i points to a single png file
2805 * -o points to a single png output file
2806 */
2807int doSingleCrunch(Bundle* bundle)
2808{
2809 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2810 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2811
2812 String8 input(bundle->getSingleCrunchInputFile());
2813 String8 output(bundle->getSingleCrunchOutputFile());
2814
2815 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2816 // we can't return the status_t as it gets truncate to the lower 8 bits.
2817 return 42;
2818 }
2819
2820 return NO_ERROR;
2821}
2822
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002823int runInDaemonMode(Bundle* bundle) {
2824 std::cout << "Ready" << std::endl;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002825 for (std::string cmd; std::getline(std::cin, cmd);) {
2826 if (cmd == "quit") {
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002827 return NO_ERROR;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002828 } else if (cmd == "s") {
2829 // Two argument crunch
2830 std::string inputFile, outputFile;
2831 std::getline(std::cin, inputFile);
2832 std::getline(std::cin, outputFile);
2833 bundle->setSingleCrunchInputFile(inputFile.c_str());
2834 bundle->setSingleCrunchOutputFile(outputFile.c_str());
2835 std::cout << "Crunching " << inputFile << std::endl;
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002836 if (doSingleCrunch(bundle) != NO_ERROR) {
2837 std::cout << "Error" << std::endl;
2838 }
2839 std::cout << "Done" << std::endl;
2840 } else {
2841 // in case of invalid command, just bail out.
2842 std::cerr << "Unknown command" << std::endl;
2843 return -1;
2844 }
2845 }
2846 return -1;
2847}
2848
Adam Lesinski282e1812014-01-23 18:17:42 -08002849char CONSOLE_DATA[2925] = {
2850 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2851 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
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, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2854 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2855 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2856 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2857 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2858 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2859 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2860 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2861 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2862 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2863 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2864 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2865 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2866 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2867 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2868 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2869 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2870 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2871 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2872 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2873 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2874 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2875 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2876 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2877 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2878 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2879 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2880 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2881 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2882 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2883 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2884 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2885 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2886 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2887 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2888 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2889 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2890 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2891 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2892 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2893 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2894 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2895 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2896 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2897 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2898 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2899 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2900 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2901 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2902 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2903 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2904 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2905 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2906 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2907 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2908 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2909 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2910 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2911 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2912 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2913 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2914 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2915 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2916 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2917 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2918 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2919 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2920 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2921 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2922 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2923 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2924 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2925 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2926 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2927 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2928 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2929 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2930 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2931 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2932 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2933 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2934 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2935 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2936 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2937 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2938 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2939 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2940 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2941 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2942 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2943 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2944 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2945 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2946 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2947 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2948 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2949 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2950 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2951 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2952 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2953 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2954 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2955 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2956 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2957 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2958 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2959 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2960 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2961 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2962 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2963 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2964 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2965 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2966 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2967 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2968 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2969 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2970 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2971 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2972 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2973 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2974 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2975 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2976 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2977 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2978 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2979 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2980 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2981 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2982 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2983 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2984 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2985 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2986 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2987 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2988 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2989 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2990 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2991 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2992 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2993 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2994 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2995 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2996 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2997 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2998 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2999 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
3000 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
3001 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
3002 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
3003 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
3004 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3005 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3006 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
3007 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
3008 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3009 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3010 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3011 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3012 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
3013 };