blob: 9b62e14d96a2c2472ab9ee5b6df82c7c731bd511 [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,
Adam Lesinski282e1812014-01-23 18:17:42 -0800312};
313
Maurice Chu2675f762013-10-22 17:33:11 -0700314String8 getComponentName(String8 &pkgName, String8 &componentName) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800315 ssize_t idx = componentName.find(".");
316 String8 retStr(pkgName);
317 if (idx == 0) {
318 retStr += componentName;
319 } else if (idx < 0) {
320 retStr += ".";
321 retStr += componentName;
322 } else {
Maurice Chu2675f762013-10-22 17:33:11 -0700323 return componentName;
Adam Lesinski282e1812014-01-23 18:17:42 -0800324 }
Maurice Chu2675f762013-10-22 17:33:11 -0700325 return retStr;
Adam Lesinski282e1812014-01-23 18:17:42 -0800326}
327
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700328static void printCompatibleScreens(ResXMLTree& tree, String8* outError) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800329 size_t len;
330 ResXMLTree::event_code_t code;
331 int depth = 0;
332 bool first = true;
333 printf("compatible-screens:");
334 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
335 if (code == ResXMLTree::END_TAG) {
336 depth--;
337 if (depth < 0) {
338 break;
339 }
340 continue;
341 }
342 if (code != ResXMLTree::START_TAG) {
343 continue;
344 }
345 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700346 const char16_t* ctag16 = tree.getElementName(&len);
347 if (ctag16 == NULL) {
348 *outError = "failed to get XML element name (bad string pool)";
349 return;
350 }
351 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800352 if (tag == "screen") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700353 int32_t screenSize = AaptXml::getIntegerAttribute(tree,
354 SCREEN_SIZE_ATTR);
355 int32_t screenDensity = AaptXml::getIntegerAttribute(tree,
356 SCREEN_DENSITY_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -0800357 if (screenSize > 0 && screenDensity > 0) {
358 if (!first) {
359 printf(",");
360 }
361 first = false;
362 printf("'%d/%d'", screenSize, screenDensity);
363 }
364 }
365 }
366 printf("\n");
367}
368
Adam Lesinski58f1f362013-11-12 12:59:08 -0800369static void printUsesPermission(const String8& name, bool optional=false, int maxSdkVersion=-1) {
370 printf("uses-permission: name='%s'", ResTable::normalizeForOutput(name.string()).string());
371 if (maxSdkVersion != -1) {
372 printf(" maxSdkVersion='%d'", maxSdkVersion);
373 }
374 printf("\n");
375
376 if (optional) {
377 printf("optional-permission: name='%s'",
378 ResTable::normalizeForOutput(name.string()).string());
379 if (maxSdkVersion != -1) {
380 printf(" maxSdkVersion='%d'", maxSdkVersion);
381 }
382 printf("\n");
383 }
384}
385
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800386static void printUsesPermissionSdk23(const String8& name, int maxSdkVersion=-1) {
387 printf("uses-permission-sdk-23: ");
388
389 printf("name='%s'", ResTable::normalizeForOutput(name.string()).string());
390 if (maxSdkVersion != -1) {
391 printf(" maxSdkVersion='%d'", maxSdkVersion);
392 }
393 printf("\n");
394}
395
Adam Lesinski58f1f362013-11-12 12:59:08 -0800396static void printUsesImpliedPermission(const String8& name, const String8& reason) {
397 printf("uses-implied-permission: name='%s' reason='%s'\n",
398 ResTable::normalizeForOutput(name.string()).string(),
399 ResTable::normalizeForOutput(reason.string()).string());
400}
401
Chih-Hung Hsieh9b8528f2016-08-10 14:15:30 -0700402Vector<String8> getNfcAidCategories(AssetManager& assets, const String8& xmlPath, bool offHost,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700403 String8 *outError = NULL)
404{
405 Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER);
406 if (aidAsset == NULL) {
407 if (outError != NULL) *outError = "xml resource does not exist";
408 return Vector<String8>();
409 }
410
411 const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service");
412
413 bool withinApduService = false;
414 Vector<String8> categories;
415
416 String8 error;
417 ResXMLTree tree;
418 tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength());
419
420 size_t len;
421 int depth = 0;
422 ResXMLTree::event_code_t code;
423 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
424 if (code == ResXMLTree::END_TAG) {
425 depth--;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700426 const char16_t* ctag16 = tree.getElementName(&len);
427 if (ctag16 == NULL) {
428 *outError = "failed to get XML element name (bad string pool)";
429 return Vector<String8>();
430 }
431 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700432
433 if (depth == 0 && tag == serviceTagName) {
434 withinApduService = false;
435 }
436
437 } else if (code == ResXMLTree::START_TAG) {
438 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700439 const char16_t* ctag16 = tree.getElementName(&len);
440 if (ctag16 == NULL) {
441 *outError = "failed to get XML element name (bad string pool)";
442 return Vector<String8>();
443 }
444 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700445
446 if (depth == 1) {
447 if (tag == serviceTagName) {
448 withinApduService = true;
449 }
450 } else if (depth == 2 && withinApduService) {
451 if (tag == "aid-group") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700452 String8 category = AaptXml::getAttribute(tree, CATEGORY_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700453 if (error != "") {
454 if (outError != NULL) *outError = error;
455 return Vector<String8>();
456 }
457
458 categories.add(category);
459 }
460 }
461 }
462 }
463 aidAsset->close();
464 return categories;
465}
466
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700467static void printComponentPresence(const char* componentName) {
468 printf("provides-component:'%s'\n", componentName);
469}
470
Adam Lesinski2c72b682014-06-24 09:56:01 -0700471/**
472 * Represents a feature that has been automatically added due to
473 * a pre-requisite or some other reason.
474 */
475struct ImpliedFeature {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800476 ImpliedFeature() : impliedBySdk23(false) {}
477 ImpliedFeature(const String8& n, bool sdk23) : name(n), impliedBySdk23(sdk23) {}
478
Adam Lesinski2c72b682014-06-24 09:56:01 -0700479 /**
480 * Name of the implied feature.
481 */
482 String8 name;
483
484 /**
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800485 * Was this implied by a permission from SDK 23 (<uses-permission-sdk-23 />)?
486 */
487 bool impliedBySdk23;
488
489 /**
Adam Lesinski2c72b682014-06-24 09:56:01 -0700490 * List of human-readable reasons for why this feature was implied.
491 */
492 SortedVector<String8> reasons;
493};
494
Adam Lesinski694d0a72016-04-06 16:12:04 -0700495struct Feature {
496 Feature() : required(false), version(-1) {}
Chih-Hung Hsiehd53e3be2016-05-03 10:02:51 -0700497 explicit Feature(bool required, int32_t version = -1) : required(required), version(version) {}
Adam Lesinski694d0a72016-04-06 16:12:04 -0700498
499 /**
500 * Whether the feature is required.
501 */
502 bool required;
503
504 /**
505 * What version of the feature is requested.
506 */
507 int32_t version;
508};
509
Adam Lesinski2c72b682014-06-24 09:56:01 -0700510/**
511 * Represents a <feature-group> tag in the AndroidManifest.xml
512 */
513struct FeatureGroup {
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700514 FeatureGroup() : openGLESVersion(-1) {}
515
Adam Lesinski2c72b682014-06-24 09:56:01 -0700516 /**
517 * Human readable label
518 */
519 String8 label;
520
521 /**
522 * Explicit features defined in the group
523 */
Adam Lesinski694d0a72016-04-06 16:12:04 -0700524 KeyedVector<String8, Feature> features;
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700525
526 /**
527 * OpenGL ES version required
528 */
529 int openGLESVersion;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700530};
531
Adam Lesinskica955a42016-08-01 16:44:29 -0700532static bool hasFeature(const char* name, const FeatureGroup& grp,
533 const KeyedVector<String8, ImpliedFeature>& implied) {
534 String8 name8(name);
535 ssize_t idx = grp.features.indexOfKey(name8);
536 if (idx < 0) {
537 idx = implied.indexOfKey(name8);
538 }
539 return idx >= 0;
540}
541
Adam Lesinski2c72b682014-06-24 09:56:01 -0700542static void addImpliedFeature(KeyedVector<String8, ImpliedFeature>* impliedFeatures,
Adam Lesinski43158772015-11-11 15:13:55 -0800543 const char* name, const String8& reason, bool sdk23) {
Adam Lesinski2c72b682014-06-24 09:56:01 -0700544 String8 name8(name);
545 ssize_t idx = impliedFeatures->indexOfKey(name8);
546 if (idx < 0) {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800547 idx = impliedFeatures->add(name8, ImpliedFeature(name8, sdk23));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700548 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800549
550 ImpliedFeature* feature = &impliedFeatures->editValueAt(idx);
551
552 // A non-sdk 23 implied feature takes precedence.
553 if (feature->impliedBySdk23 && !sdk23) {
554 feature->impliedBySdk23 = false;
555 }
Adam Lesinski43158772015-11-11 15:13:55 -0800556 feature->reasons.add(reason);
Adam Lesinski2c72b682014-06-24 09:56:01 -0700557}
558
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800559static void printFeatureGroupImpl(const FeatureGroup& grp,
560 const KeyedVector<String8, ImpliedFeature>* impliedFeatures) {
Adam Lesinski2c72b682014-06-24 09:56:01 -0700561 printf("feature-group: label='%s'\n", grp.label.string());
562
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700563 if (grp.openGLESVersion > 0) {
564 printf(" uses-gl-es: '0x%x'\n", grp.openGLESVersion);
565 }
566
Adam Lesinski2c72b682014-06-24 09:56:01 -0700567 const size_t numFeatures = grp.features.size();
568 for (size_t i = 0; i < numFeatures; i++) {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700569 const Feature& feature = grp.features[i];
570 const bool required = feature.required;
571 const int32_t version = feature.version;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700572
573 const String8& featureName = grp.features.keyAt(i);
Adam Lesinski694d0a72016-04-06 16:12:04 -0700574 printf(" uses-feature%s: name='%s'", (required ? "" : "-not-required"),
Adam Lesinski2c72b682014-06-24 09:56:01 -0700575 ResTable::normalizeForOutput(featureName.string()).string());
Adam Lesinski694d0a72016-04-06 16:12:04 -0700576
577 if (version > 0) {
578 printf(" version='%d'", version);
579 }
580 printf("\n");
Adam Lesinski2c72b682014-06-24 09:56:01 -0700581 }
582
583 const size_t numImpliedFeatures =
584 (impliedFeatures != NULL) ? impliedFeatures->size() : 0;
585 for (size_t i = 0; i < numImpliedFeatures; i++) {
586 const ImpliedFeature& impliedFeature = impliedFeatures->valueAt(i);
587 if (grp.features.indexOfKey(impliedFeature.name) >= 0) {
588 // The feature is explicitly set, no need to use implied
589 // definition.
590 continue;
591 }
592
593 String8 printableFeatureName(ResTable::normalizeForOutput(
594 impliedFeature.name.string()));
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800595 const char* sdk23Suffix = impliedFeature.impliedBySdk23 ? "-sdk-23" : "";
596
597 printf(" uses-feature%s: name='%s'\n", sdk23Suffix, printableFeatureName.string());
598 printf(" uses-implied-feature%s: name='%s' reason='", sdk23Suffix,
599 printableFeatureName.string());
Adam Lesinski2c72b682014-06-24 09:56:01 -0700600 const size_t numReasons = impliedFeature.reasons.size();
601 for (size_t j = 0; j < numReasons; j++) {
602 printf("%s", impliedFeature.reasons[j].string());
603 if (j + 2 < numReasons) {
604 printf(", ");
605 } else if (j + 1 < numReasons) {
606 printf(", and ");
607 }
608 }
609 printf("'\n");
610 }
611}
612
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800613static void printFeatureGroup(const FeatureGroup& grp) {
614 printFeatureGroupImpl(grp, NULL);
615}
616
617static void printDefaultFeatureGroup(const FeatureGroup& grp,
618 const KeyedVector<String8, ImpliedFeature>& impliedFeatures) {
619 printFeatureGroupImpl(grp, &impliedFeatures);
620}
621
Adam Lesinski2c72b682014-06-24 09:56:01 -0700622static void addParentFeatures(FeatureGroup* grp, const String8& name) {
623 if (name == "android.hardware.camera.autofocus" ||
624 name == "android.hardware.camera.flash") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700625 grp->features.add(String8("android.hardware.camera"), Feature(true));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700626 } else if (name == "android.hardware.location.gps" ||
627 name == "android.hardware.location.network") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700628 grp->features.add(String8("android.hardware.location"), Feature(true));
Adam Lesinskica955a42016-08-01 16:44:29 -0700629 } else if (name == "android.hardware.faketouch.multitouch") {
630 grp->features.add(String8("android.hardware.faketouch"), Feature(true));
631 } else if (name == "android.hardware.faketouch.multitouch.distinct" ||
632 name == "android.hardware.faketouch.multitouch.jazzhands") {
633 grp->features.add(String8("android.hardware.faketouch.multitouch"), Feature(true));
634 grp->features.add(String8("android.hardware.faketouch"), Feature(true));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700635 } else if (name == "android.hardware.touchscreen.multitouch") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700636 grp->features.add(String8("android.hardware.touchscreen"), Feature(true));
Adam Lesinskica955a42016-08-01 16:44:29 -0700637 } else if (name == "android.hardware.touchscreen.multitouch.distinct" ||
638 name == "android.hardware.touchscreen.multitouch.jazzhands") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700639 grp->features.add(String8("android.hardware.touchscreen.multitouch"), Feature(true));
640 grp->features.add(String8("android.hardware.touchscreen"), Feature(true));
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700641 } else if (name == "android.hardware.opengles.aep") {
642 const int openGLESVersion31 = 0x00030001;
643 if (openGLESVersion31 > grp->openGLESVersion) {
644 grp->openGLESVersion = openGLESVersion31;
645 }
Adam Lesinski2c72b682014-06-24 09:56:01 -0700646 }
647}
648
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800649static void addImpliedFeaturesForPermission(const int targetSdk, const String8& name,
650 KeyedVector<String8, ImpliedFeature>* impliedFeatures,
651 bool impliedBySdk23Permission) {
652 if (name == "android.permission.CAMERA") {
653 addImpliedFeature(impliedFeatures, "android.hardware.camera",
Adam Lesinski43158772015-11-11 15:13:55 -0800654 String8::format("requested %s permission", name.string()),
655 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800656 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
Adam Lesinski43158772015-11-11 15:13:55 -0800657 if (targetSdk < SDK_LOLLIPOP) {
658 addImpliedFeature(impliedFeatures, "android.hardware.location.gps",
659 String8::format("requested %s permission", name.string()),
660 impliedBySdk23Permission);
661 addImpliedFeature(impliedFeatures, "android.hardware.location.gps",
662 String8::format("targetSdkVersion < %d", SDK_LOLLIPOP),
663 impliedBySdk23Permission);
664 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800665 addImpliedFeature(impliedFeatures, "android.hardware.location",
Adam Lesinski43158772015-11-11 15:13:55 -0800666 String8::format("requested %s permission", name.string()),
667 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800668 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
Adam Lesinski43158772015-11-11 15:13:55 -0800669 if (targetSdk < SDK_LOLLIPOP) {
670 addImpliedFeature(impliedFeatures, "android.hardware.location.network",
671 String8::format("requested %s permission", name.string()),
672 impliedBySdk23Permission);
673 addImpliedFeature(impliedFeatures, "android.hardware.location.network",
674 String8::format("targetSdkVersion < %d", SDK_LOLLIPOP),
675 impliedBySdk23Permission);
676 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800677 addImpliedFeature(impliedFeatures, "android.hardware.location",
Adam Lesinski43158772015-11-11 15:13:55 -0800678 String8::format("requested %s permission", name.string()),
679 impliedBySdk23Permission);
680 } else if (name == "android.permission.ACCESS_MOCK_LOCATION" ||
681 name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800682 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
683 addImpliedFeature(impliedFeatures, "android.hardware.location",
Adam Lesinski43158772015-11-11 15:13:55 -0800684 String8::format("requested %s permission", name.string()),
685 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800686 } else if (name == "android.permission.BLUETOOTH" ||
687 name == "android.permission.BLUETOOTH_ADMIN") {
Adam Lesinski43158772015-11-11 15:13:55 -0800688 if (targetSdk > SDK_DONUT) {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800689 addImpliedFeature(impliedFeatures, "android.hardware.bluetooth",
Adam Lesinski43158772015-11-11 15:13:55 -0800690 String8::format("requested %s permission", name.string()),
691 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800692 addImpliedFeature(impliedFeatures, "android.hardware.bluetooth",
Adam Lesinski43158772015-11-11 15:13:55 -0800693 String8::format("targetSdkVersion > %d", SDK_DONUT),
694 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800695 }
696 } else if (name == "android.permission.RECORD_AUDIO") {
697 addImpliedFeature(impliedFeatures, "android.hardware.microphone",
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.ACCESS_WIFI_STATE" ||
701 name == "android.permission.CHANGE_WIFI_STATE" ||
702 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
703 addImpliedFeature(impliedFeatures, "android.hardware.wifi",
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 } else if (name == "android.permission.CALL_PHONE" ||
707 name == "android.permission.CALL_PRIVILEGED" ||
708 name == "android.permission.MODIFY_PHONE_STATE" ||
709 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
710 name == "android.permission.READ_SMS" ||
711 name == "android.permission.RECEIVE_SMS" ||
712 name == "android.permission.RECEIVE_MMS" ||
713 name == "android.permission.RECEIVE_WAP_PUSH" ||
714 name == "android.permission.SEND_SMS" ||
715 name == "android.permission.WRITE_APN_SETTINGS" ||
716 name == "android.permission.WRITE_SMS") {
717 addImpliedFeature(impliedFeatures, "android.hardware.telephony",
Adam Lesinski43158772015-11-11 15:13:55 -0800718 String8("requested a telephony permission"),
719 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800720 }
721}
722
Adam Lesinski282e1812014-01-23 18:17:42 -0800723/*
724 * Handle the "dump" command, to extract select data from an archive.
725 */
726extern char CONSOLE_DATA[2925]; // see EOF
727int doDump(Bundle* bundle)
728{
729 status_t result = UNKNOWN_ERROR;
Adam Lesinski282e1812014-01-23 18:17:42 -0800730
731 if (bundle->getFileSpecCount() < 1) {
732 fprintf(stderr, "ERROR: no dump option specified\n");
733 return 1;
734 }
735
736 if (bundle->getFileSpecCount() < 2) {
737 fprintf(stderr, "ERROR: no dump file specified\n");
738 return 1;
739 }
740
741 const char* option = bundle->getFileSpecEntry(0);
742 const char* filename = bundle->getFileSpecEntry(1);
743
744 AssetManager assets;
Narayan Kamathf85e41f2014-01-24 14:14:30 +0000745 int32_t assetsCookie;
Adam Lesinski282e1812014-01-23 18:17:42 -0800746 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
747 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
748 return 1;
749 }
750
751 // Make a dummy config for retrieving resources... we need to supply
752 // non-default values for some configs so that we can retrieve resources
753 // in the app that don't have a default. The most important of these is
754 // the API version because key resources like icons will have an implicit
755 // version if they are using newer config types like density.
756 ResTable_config config;
Narayan Kamath91447d82014-01-21 15:32:36 +0000757 memset(&config, 0, sizeof(ResTable_config));
Adam Lesinski282e1812014-01-23 18:17:42 -0800758 config.language[0] = 'e';
759 config.language[1] = 'n';
760 config.country[0] = 'U';
761 config.country[1] = 'S';
762 config.orientation = ResTable_config::ORIENTATION_PORT;
763 config.density = ResTable_config::DENSITY_MEDIUM;
764 config.sdkVersion = 10000; // Very high.
765 config.screenWidthDp = 320;
766 config.screenHeightDp = 480;
767 config.smallestScreenWidthDp = 320;
Adam Lesinskic2dea8d2014-08-04 16:40:41 -0700768 config.screenLayout |= ResTable_config::SCREENSIZE_NORMAL;
Adam Lesinski282e1812014-01-23 18:17:42 -0800769 assets.setConfiguration(config);
770
771 const ResTable& res = assets.getResources(false);
Dan Albert68001652014-09-09 09:51:01 -0700772 if (res.getError() != NO_ERROR) {
Adam Lesinski25e9d552014-05-19 15:01:43 -0700773 fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
Adam Lesinski63e646e2014-07-30 11:40:39 -0700774 return 1;
Adam Lesinski282e1812014-01-23 18:17:42 -0800775 }
776
Adam Lesinski694d0a72016-04-06 16:12:04 -0700777 // Source for AndroidManifest.xml
Adam Lesinski10de3af12016-07-13 10:14:03 -0700778 const String8 manifestFile("AndroidManifest.xml");
Adam Lesinski694d0a72016-04-06 16:12:04 -0700779
Adam Lesinski2cb761e2014-08-15 13:59:02 -0700780 // The dynamicRefTable can be null if there are no resources for this asset cookie.
781 // This fine.
Adam Lesinski63e646e2014-07-30 11:40:39 -0700782 const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700783
784 Asset* asset = NULL;
785
Adam Lesinski282e1812014-01-23 18:17:42 -0800786 if (strcmp("resources", option) == 0) {
Elliott Hughesba3fe562015-08-12 14:49:53 -0700787#ifndef __ANDROID__
Adam Lesinski282e1812014-01-23 18:17:42 -0800788 res.print(bundle->getValues());
789#endif
790
791 } else if (strcmp("strings", option) == 0) {
792 const ResStringPool* pool = res.getTableStringBlock(0);
793 printStringPool(pool);
794
795 } else if (strcmp("xmltree", option) == 0) {
796 if (bundle->getFileSpecCount() < 3) {
797 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
798 goto bail;
799 }
800
801 for (int i=2; i<bundle->getFileSpecCount(); i++) {
802 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700803 ResXMLTree tree(dynamicRefTable);
804 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800805 if (asset == NULL) {
806 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
807 goto bail;
808 }
809
810 if (tree.setTo(asset->getBuffer(true),
811 asset->getLength()) != NO_ERROR) {
812 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
813 goto bail;
814 }
815 tree.restart();
816 printXMLBlock(&tree);
817 tree.uninit();
818 delete asset;
819 asset = NULL;
820 }
821
822 } else if (strcmp("xmlstrings", option) == 0) {
823 if (bundle->getFileSpecCount() < 3) {
824 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
825 goto bail;
826 }
827
828 for (int i=2; i<bundle->getFileSpecCount(); i++) {
829 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700830 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800831 if (asset == NULL) {
832 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
833 goto bail;
834 }
835
Adam Lesinski63e646e2014-07-30 11:40:39 -0700836 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800837 if (tree.setTo(asset->getBuffer(true),
838 asset->getLength()) != NO_ERROR) {
839 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
840 goto bail;
841 }
842 printStringPool(&tree.getStrings());
843 delete asset;
844 asset = NULL;
845 }
846
847 } else {
Adam Lesinski63e646e2014-07-30 11:40:39 -0700848 asset = assets.openNonAsset(assetsCookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800849 if (asset == NULL) {
850 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
851 goto bail;
852 }
853
Adam Lesinski63e646e2014-07-30 11:40:39 -0700854 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800855 if (tree.setTo(asset->getBuffer(true),
856 asset->getLength()) != NO_ERROR) {
857 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
858 goto bail;
859 }
860 tree.restart();
861
862 if (strcmp("permissions", option) == 0) {
863 size_t len;
864 ResXMLTree::event_code_t code;
865 int depth = 0;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800866 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
867 code != ResXMLTree::BAD_DOCUMENT) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800868 if (code == ResXMLTree::END_TAG) {
869 depth--;
870 continue;
871 }
872 if (code != ResXMLTree::START_TAG) {
873 continue;
874 }
875 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700876 const char16_t* ctag16 = tree.getElementName(&len);
877 if (ctag16 == NULL) {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700878 SourcePos(manifestFile, tree.getLineNumber()).error(
879 "ERROR: failed to get XML element name (bad string pool)");
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700880 goto bail;
881 }
882 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800883 //printf("Depth %d tag %s\n", depth, tag.string());
884 if (depth == 1) {
885 if (tag != "manifest") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700886 SourcePos(manifestFile, tree.getLineNumber()).error(
887 "ERROR: manifest does not start with <manifest> tag");
Adam Lesinski282e1812014-01-23 18:17:42 -0800888 goto bail;
889 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700890 String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -0700891 printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800892 } else if (depth == 2) {
893 if (tag == "permission") {
894 String8 error;
895 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
896 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700897 SourcePos(manifestFile, tree.getLineNumber()).error(
898 "ERROR getting 'android:name': %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800899 goto bail;
900 }
901
902 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700903 SourcePos(manifestFile, tree.getLineNumber()).error(
904 "ERROR: missing 'android:name' for permission");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800905 goto bail;
906 }
907 printf("permission: %s\n",
908 ResTable::normalizeForOutput(name.string()).string());
909 } else if (tag == "uses-permission") {
910 String8 error;
911 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
912 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700913 SourcePos(manifestFile, tree.getLineNumber()).error(
914 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800915 goto bail;
916 }
917
918 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700919 SourcePos(manifestFile, tree.getLineNumber()).error(
920 "ERROR: missing 'android:name' for uses-permission");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800921 goto bail;
922 }
923 printUsesPermission(name,
924 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
925 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
926 } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
927 String8 error;
928 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
929 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700930 SourcePos(manifestFile, tree.getLineNumber()).error(
931 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800932 goto bail;
933 }
934
935 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700936 SourcePos(manifestFile, tree.getLineNumber()).error(
937 "ERROR: missing 'android:name' for uses-permission-sdk-23");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800938 goto bail;
939 }
940 printUsesPermissionSdk23(
941 name,
942 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
Adam Lesinski282e1812014-01-23 18:17:42 -0800943 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800944 }
945 }
946 } else if (strcmp("badging", option) == 0) {
947 Vector<String8> locales;
948 res.getLocales(&locales);
949
950 Vector<ResTable_config> configs;
951 res.getConfigurations(&configs);
952 SortedVector<int> densities;
953 const size_t NC = configs.size();
954 for (size_t i=0; i<NC; i++) {
955 int dens = configs[i].density;
956 if (dens == 0) {
957 dens = 160;
958 }
959 densities.add(dens);
960 }
961
962 size_t len;
963 ResXMLTree::event_code_t code;
964 int depth = 0;
965 String8 error;
966 bool withinActivity = false;
967 bool isMainActivity = false;
968 bool isLauncherActivity = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800969 bool isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800970 bool isSearchable = false;
971 bool withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700972 bool withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700973 bool withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800974 bool withinReceiver = false;
975 bool withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700976 bool withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800977 bool withinIntentFilter = false;
978 bool hasMainActivity = false;
979 bool hasOtherActivities = false;
980 bool hasOtherReceivers = false;
981 bool hasOtherServices = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700982 bool hasIntentFilter = false;
983
Adam Lesinski282e1812014-01-23 18:17:42 -0800984 bool hasWallpaperService = false;
985 bool hasImeService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700986 bool hasAccessibilityService = false;
987 bool hasPrintService = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800988 bool hasWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700989 bool hasDeviceAdminReceiver = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700990 bool hasPaymentService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700991 bool hasDocumentsProvider = false;
992 bool hasCameraActivity = false;
993 bool hasCameraSecureActivity = false;
994 bool hasLauncher = false;
995 bool hasNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400996 bool hasDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700997
Adam Lesinski282e1812014-01-23 18:17:42 -0800998 bool actMainActivity = false;
999 bool actWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001000 bool actDeviceAdminEnabled = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001001 bool actImeService = false;
1002 bool actWallpaperService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001003 bool actAccessibilityService = false;
1004 bool actPrintService = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001005 bool actHostApduService = false;
1006 bool actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001007 bool actDocumentsProvider = false;
1008 bool actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001009 bool actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001010 bool actCamera = false;
1011 bool actCameraSecure = false;
1012 bool catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001013 bool hasMetaHostPaymentCategory = false;
1014 bool hasMetaOffHostPaymentCategory = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001015
1016 // These permissions are required by services implementing services
1017 // the system binds to (IME, Accessibility, PrintServices, etc.)
1018 bool hasBindDeviceAdminPermission = false;
1019 bool hasBindInputMethodPermission = false;
1020 bool hasBindAccessibilityServicePermission = false;
1021 bool hasBindPrintServicePermission = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001022 bool hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001023 bool hasRequiredSafAttributes = false;
1024 bool hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001025 bool hasBindDreamServicePermission = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001026
1027 // These two implement the implicit permissions that are granted
1028 // to pre-1.6 applications.
1029 bool hasWriteExternalStoragePermission = false;
1030 bool hasReadPhoneStatePermission = false;
1031
1032 // If an app requests write storage, they will also get read storage.
1033 bool hasReadExternalStoragePermission = false;
1034
1035 // Implement transition to read and write call log.
1036 bool hasReadContactsPermission = false;
1037 bool hasWriteContactsPermission = false;
1038 bool hasReadCallLogPermission = false;
1039 bool hasWriteCallLogPermission = false;
1040
Adam Lesinskie47fd122014-08-15 22:25:36 -07001041 // If an app declares itself as multiArch, we report the
1042 // native libraries differently.
1043 bool hasMultiArch = false;
1044
Adam Lesinski282e1812014-01-23 18:17:42 -08001045 // This next group of variables is used to implement a group of
1046 // backward-compatibility heuristics necessitated by the addition of
1047 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
1048 // heuristic is "if an app requests a permission but doesn't explicitly
1049 // request the corresponding <uses-feature>, presume it's there anyway".
Adam Lesinski2c72b682014-06-24 09:56:01 -07001050
Adam Lesinski282e1812014-01-23 18:17:42 -08001051 // 2.2 also added some other features that apps can request, but that
1052 // have no corresponding permission, so we cannot implement any
1053 // back-compatibility heuristic for them. The below are thus unnecessary
1054 // (but are retained here for documentary purposes.)
1055 //bool specCompassFeature = false;
1056 //bool specAccelerometerFeature = false;
1057 //bool specProximityFeature = false;
1058 //bool specAmbientLightFeature = false;
1059 //bool specLiveWallpaperFeature = false;
1060
1061 int targetSdk = 0;
1062 int smallScreen = 1;
1063 int normalScreen = 1;
1064 int largeScreen = 1;
1065 int xlargeScreen = 1;
1066 int anyDensity = 1;
1067 int requiresSmallestWidthDp = 0;
1068 int compatibleWidthLimitDp = 0;
1069 int largestWidthLimitDp = 0;
1070 String8 pkg;
1071 String8 activityName;
1072 String8 activityLabel;
1073 String8 activityIcon;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001074 String8 activityBanner;
Adam Lesinski282e1812014-01-23 18:17:42 -08001075 String8 receiverName;
1076 String8 serviceName;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001077 Vector<String8> supportedInput;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001078
1079 FeatureGroup commonFeatures;
1080 Vector<FeatureGroup> featureGroups;
1081 KeyedVector<String8, ImpliedFeature> impliedFeatures;
1082
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001083 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
1084 code != ResXMLTree::BAD_DOCUMENT) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001085 if (code == ResXMLTree::END_TAG) {
1086 depth--;
1087 if (depth < 2) {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001088 if (withinSupportsInput && !supportedInput.isEmpty()) {
1089 printf("supports-input: '");
1090 const size_t N = supportedInput.size();
1091 for (size_t i=0; i<N; i++) {
Maurice Chu2675f762013-10-22 17:33:11 -07001092 printf("%s", ResTable::normalizeForOutput(
1093 supportedInput[i].string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001094 if (i != N - 1) {
1095 printf("' '");
1096 } else {
1097 printf("'\n");
1098 }
1099 }
1100 supportedInput.clear();
1101 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001102 withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001103 withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001104 withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001105 } else if (depth < 3) {
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001106 if (withinActivity && isMainActivity) {
Maurice Chu2675f762013-10-22 17:33:11 -07001107 String8 aName(getComponentName(pkg, activityName));
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001108 if (isLauncherActivity) {
1109 printf("launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001110 if (aName.length() > 0) {
1111 printf(" name='%s' ",
1112 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001113 }
1114 printf(" label='%s' icon='%s'\n",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001115 ResTable::normalizeForOutput(activityLabel.string())
1116 .string(),
1117 ResTable::normalizeForOutput(activityIcon.string())
1118 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001119 }
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001120 if (isLeanbackLauncherActivity) {
1121 printf("leanback-launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001122 if (aName.length() > 0) {
1123 printf(" name='%s' ",
1124 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001125 }
1126 printf(" label='%s' icon='%s' banner='%s'\n",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001127 ResTable::normalizeForOutput(activityLabel.string())
1128 .string(),
1129 ResTable::normalizeForOutput(activityIcon.string())
1130 .string(),
1131 ResTable::normalizeForOutput(activityBanner.string())
1132 .string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001133 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001134 }
1135 if (!hasIntentFilter) {
1136 hasOtherActivities |= withinActivity;
1137 hasOtherReceivers |= withinReceiver;
1138 hasOtherServices |= withinService;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001139 } else {
1140 if (withinService) {
1141 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
1142 hasBindNfcServicePermission);
1143 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
1144 hasBindNfcServicePermission);
1145 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001146 }
1147 withinActivity = false;
1148 withinService = false;
1149 withinReceiver = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001150 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001151 hasIntentFilter = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001152 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001153 } else if (depth < 4) {
1154 if (withinIntentFilter) {
1155 if (withinActivity) {
1156 hasMainActivity |= actMainActivity;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001157 hasLauncher |= catLauncher;
1158 hasCameraActivity |= actCamera;
1159 hasCameraSecureActivity |= actCameraSecure;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001160 hasOtherActivities |=
1161 !actMainActivity && !actCamera && !actCameraSecure;
Adam Lesinski282e1812014-01-23 18:17:42 -08001162 } else if (withinReceiver) {
1163 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001164 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
1165 hasBindDeviceAdminPermission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001166 hasOtherReceivers |=
1167 (!actWidgetReceivers && !actDeviceAdminEnabled);
Adam Lesinski282e1812014-01-23 18:17:42 -08001168 } else if (withinService) {
1169 hasImeService |= actImeService;
1170 hasWallpaperService |= actWallpaperService;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001171 hasAccessibilityService |= (actAccessibilityService &&
1172 hasBindAccessibilityServicePermission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001173 hasPrintService |=
1174 (actPrintService && hasBindPrintServicePermission);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001175 hasNotificationListenerService |= actNotificationListenerService &&
1176 hasBindNotificationListenerServicePermission;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001177 hasDreamService |= actDreamService && hasBindDreamServicePermission;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001178 hasOtherServices |= (!actImeService && !actWallpaperService &&
Adam Lesinski94fc9122013-09-30 17:16:09 -07001179 !actAccessibilityService && !actPrintService &&
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001180 !actHostApduService && !actOffHostApduService &&
1181 !actNotificationListenerService);
1182 } else if (withinProvider) {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001183 hasDocumentsProvider |=
1184 actDocumentsProvider && hasRequiredSafAttributes;
Adam Lesinski282e1812014-01-23 18:17:42 -08001185 }
1186 }
1187 withinIntentFilter = false;
1188 }
1189 continue;
1190 }
1191 if (code != ResXMLTree::START_TAG) {
1192 continue;
1193 }
1194 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001195
1196 const char16_t* ctag16 = tree.getElementName(&len);
1197 if (ctag16 == NULL) {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001198 SourcePos(manifestFile, tree.getLineNumber()).error(
1199 "ERROR: failed to get XML element name (bad string pool)");
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001200 goto bail;
1201 }
1202 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -08001203 //printf("Depth %d, %s\n", depth, tag.string());
1204 if (depth == 1) {
1205 if (tag != "manifest") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001206 SourcePos(manifestFile, tree.getLineNumber()).error(
1207 "ERROR: manifest does not start with <manifest> tag");
Adam Lesinski282e1812014-01-23 18:17:42 -08001208 goto bail;
1209 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001210 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -07001211 printf("package: name='%s' ",
1212 ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001213 int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR,
1214 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001215 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001216 SourcePos(manifestFile, tree.getLineNumber()).error(
1217 "ERROR getting 'android:versionCode' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001218 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001219 goto bail;
1220 }
1221 if (versionCode > 0) {
1222 printf("versionCode='%d' ", versionCode);
1223 } else {
1224 printf("versionCode='' ");
1225 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001226 String8 versionName = AaptXml::getResolvedAttribute(res, tree,
1227 VERSION_NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001228 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001229 SourcePos(manifestFile, tree.getLineNumber()).error(
1230 "ERROR getting 'android:versionName' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001231 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001232 goto bail;
1233 }
Adam Lesinski25d35a92014-08-11 09:41:56 -07001234 printf("versionName='%s'",
Maurice Chu2675f762013-10-22 17:33:11 -07001235 ResTable::normalizeForOutput(versionName.string()).string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001236
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001237 String8 splitName = AaptXml::getAttribute(tree, NULL, "split");
Adam Lesinski25d35a92014-08-11 09:41:56 -07001238 if (!splitName.isEmpty()) {
1239 printf(" split='%s'", ResTable::normalizeForOutput(
1240 splitName.string()).string());
1241 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001242
Adam Lesinski5283fab2014-08-29 11:23:55 -07001243 String8 platformVersionName = AaptXml::getAttribute(tree, NULL,
1244 "platformBuildVersionName");
1245 printf(" platformBuildVersionName='%s'", platformVersionName.string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001246 printf("\n");
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001247
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001248 int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
1249 INSTALL_LOCATION_ATTR, &error);
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001250 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001251 SourcePos(manifestFile, tree.getLineNumber()).error(
1252 "ERROR getting 'android:installLocation' attribute: %s",
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001253 error.string());
1254 goto bail;
1255 }
1256
1257 if (installLocation >= 0) {
1258 printf("install-location:'");
1259 switch (installLocation) {
1260 case 0:
1261 printf("auto");
1262 break;
1263 case 1:
1264 printf("internalOnly");
1265 break;
1266 case 2:
1267 printf("preferExternal");
1268 break;
1269 default:
1270 fprintf(stderr, "Invalid installLocation %d\n", installLocation);
1271 goto bail;
1272 }
1273 printf("'\n");
1274 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001275 } else if (depth == 2) {
1276 withinApplication = false;
1277 if (tag == "application") {
1278 withinApplication = true;
1279
1280 String8 label;
1281 const size_t NL = locales.size();
1282 for (size_t i=0; i<NL; i++) {
1283 const char* localeStr = locales[i].string();
Adam Lesinskia77685f2016-10-03 16:26:28 -07001284 assets.setConfiguration(config, localeStr != NULL ? localeStr : "");
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001285 String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1286 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001287 if (llabel != "") {
1288 if (localeStr == NULL || strlen(localeStr) == 0) {
1289 label = llabel;
Maurice Chu2675f762013-10-22 17:33:11 -07001290 printf("application-label:'%s'\n",
1291 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001292 } else {
1293 if (label == "") {
1294 label = llabel;
1295 }
1296 printf("application-label-%s:'%s'\n", localeStr,
Maurice Chu2675f762013-10-22 17:33:11 -07001297 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001298 }
1299 }
1300 }
1301
1302 ResTable_config tmpConfig = config;
1303 const size_t ND = densities.size();
1304 for (size_t i=0; i<ND; i++) {
1305 tmpConfig.density = densities[i];
1306 assets.setConfiguration(tmpConfig);
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001307 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1308 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001309 if (icon != "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001310 printf("application-icon-%d:'%s'\n", densities[i],
1311 ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001312 }
1313 }
1314 assets.setConfiguration(config);
1315
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001316 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001317 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001318 SourcePos(manifestFile, tree.getLineNumber()).error(
1319 "ERROR getting 'android:icon' attribute: %s", error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001320 goto bail;
1321 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001322 int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0,
1323 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001324 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001325 SourcePos(manifestFile, tree.getLineNumber()).error(
1326 "ERROR getting 'android:testOnly' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001327 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001328 goto bail;
1329 }
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001330
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001331 String8 banner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1332 &error);
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001333 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001334 SourcePos(manifestFile, tree.getLineNumber()).error(
1335 "ERROR getting 'android:banner' attribute: %s", error.string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001336 goto bail;
1337 }
Maurice Chu2675f762013-10-22 17:33:11 -07001338 printf("application: label='%s' ",
1339 ResTable::normalizeForOutput(label.string()).string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001340 printf("icon='%s'", ResTable::normalizeForOutput(icon.string()).string());
1341 if (banner != "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001342 printf(" banner='%s'",
1343 ResTable::normalizeForOutput(banner.string()).string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001344 }
1345 printf("\n");
Adam Lesinski282e1812014-01-23 18:17:42 -08001346 if (testOnly != 0) {
1347 printf("testOnly='%d'\n", testOnly);
1348 }
1349
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001350 int32_t isGame = AaptXml::getResolvedIntegerAttribute(res, tree,
1351 ISGAME_ATTR, 0, &error);
1352 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001353 SourcePos(manifestFile, tree.getLineNumber()).error(
1354 "ERROR getting 'android:isGame' attribute: %s", error.string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001355 goto bail;
1356 }
1357 if (isGame != 0) {
1358 printf("application-isGame\n");
1359 }
1360
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001361 int32_t debuggable = AaptXml::getResolvedIntegerAttribute(res, tree,
1362 DEBUGGABLE_ATTR, 0, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001363 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001364 SourcePos(manifestFile, tree.getLineNumber()).error(
1365 "ERROR getting 'android:debuggable' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001366 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001367 goto bail;
1368 }
1369 if (debuggable != 0) {
1370 printf("application-debuggable\n");
1371 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07001372
1373 // We must search by name because the multiArch flag hasn't been API
1374 // frozen yet.
1375 int32_t multiArchIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
1376 "multiArch");
1377 if (multiArchIndex >= 0) {
1378 Res_value value;
1379 if (tree.getAttributeValue(multiArchIndex, &value) != NO_ERROR) {
1380 if (value.dataType >= Res_value::TYPE_FIRST_INT &&
1381 value.dataType <= Res_value::TYPE_LAST_INT) {
1382 hasMultiArch = value.data;
1383 }
1384 }
1385 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001386 } else if (tag == "uses-sdk") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001387 int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR,
1388 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001389 if (error != "") {
1390 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001391 String8 name = AaptXml::getResolvedAttribute(res, tree,
1392 MIN_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001393 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001394 SourcePos(manifestFile, tree.getLineNumber()).error(
1395 "ERROR getting 'android:minSdkVersion' attribute: %s",
Adam Lesinski282e1812014-01-23 18:17:42 -08001396 error.string());
1397 goto bail;
1398 }
1399 if (name == "Donut") targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001400 printf("sdkVersion:'%s'\n",
1401 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001402 } else if (code != -1) {
1403 targetSdk = code;
1404 printf("sdkVersion:'%d'\n", code);
1405 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001406 code = AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -08001407 if (code != -1) {
1408 printf("maxSdkVersion:'%d'\n", code);
1409 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001410 code = AaptXml::getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001411 if (error != "") {
1412 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001413 String8 name = AaptXml::getResolvedAttribute(res, tree,
1414 TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001415 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001416 SourcePos(manifestFile, tree.getLineNumber()).error(
1417 "ERROR getting 'android:targetSdkVersion' attribute: %s",
Adam Lesinski282e1812014-01-23 18:17:42 -08001418 error.string());
1419 goto bail;
1420 }
1421 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001422 printf("targetSdkVersion:'%s'\n",
1423 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001424 } else if (code != -1) {
1425 if (targetSdk < code) {
1426 targetSdk = code;
1427 }
1428 printf("targetSdkVersion:'%d'\n", code);
1429 }
1430 } else if (tag == "uses-configuration") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001431 int32_t reqTouchScreen = AaptXml::getIntegerAttribute(tree,
1432 REQ_TOUCH_SCREEN_ATTR, 0);
1433 int32_t reqKeyboardType = AaptXml::getIntegerAttribute(tree,
1434 REQ_KEYBOARD_TYPE_ATTR, 0);
1435 int32_t reqHardKeyboard = AaptXml::getIntegerAttribute(tree,
1436 REQ_HARD_KEYBOARD_ATTR, 0);
1437 int32_t reqNavigation = AaptXml::getIntegerAttribute(tree,
1438 REQ_NAVIGATION_ATTR, 0);
1439 int32_t reqFiveWayNav = AaptXml::getIntegerAttribute(tree,
1440 REQ_FIVE_WAY_NAV_ATTR, 0);
Adam Lesinski282e1812014-01-23 18:17:42 -08001441 printf("uses-configuration:");
1442 if (reqTouchScreen != 0) {
1443 printf(" reqTouchScreen='%d'", reqTouchScreen);
1444 }
1445 if (reqKeyboardType != 0) {
1446 printf(" reqKeyboardType='%d'", reqKeyboardType);
1447 }
1448 if (reqHardKeyboard != 0) {
1449 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1450 }
1451 if (reqNavigation != 0) {
1452 printf(" reqNavigation='%d'", reqNavigation);
1453 }
1454 if (reqFiveWayNav != 0) {
1455 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1456 }
1457 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001458 } else if (tag == "supports-input") {
1459 withinSupportsInput = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001460 } else if (tag == "supports-screens") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001461 smallScreen = AaptXml::getIntegerAttribute(tree,
1462 SMALL_SCREEN_ATTR, 1);
1463 normalScreen = AaptXml::getIntegerAttribute(tree,
1464 NORMAL_SCREEN_ATTR, 1);
1465 largeScreen = AaptXml::getIntegerAttribute(tree,
1466 LARGE_SCREEN_ATTR, 1);
1467 xlargeScreen = AaptXml::getIntegerAttribute(tree,
1468 XLARGE_SCREEN_ATTR, 1);
1469 anyDensity = AaptXml::getIntegerAttribute(tree,
1470 ANY_DENSITY_ATTR, 1);
1471 requiresSmallestWidthDp = AaptXml::getIntegerAttribute(tree,
1472 REQUIRES_SMALLEST_WIDTH_DP_ATTR, 0);
1473 compatibleWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1474 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, 0);
1475 largestWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1476 LARGEST_WIDTH_LIMIT_DP_ATTR, 0);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001477 } else if (tag == "feature-group") {
1478 withinFeatureGroup = true;
1479 FeatureGroup group;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001480 group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001481 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001482 SourcePos(manifestFile, tree.getLineNumber()).error(
1483 "ERROR getting 'android:label' attribute: %s", error.string());
Adam Lesinski2c72b682014-06-24 09:56:01 -07001484 goto bail;
1485 }
1486 featureGroups.add(group);
1487
Adam Lesinski282e1812014-01-23 18:17:42 -08001488 } else if (tag == "uses-feature") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001489 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001490 if (name != "" && error == "") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001491 const char* androidSchema =
1492 "http://schemas.android.com/apk/res/android";
Adam Lesinski282e1812014-01-23 18:17:42 -08001493
Adam Lesinski694d0a72016-04-06 16:12:04 -07001494 int32_t req = AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1,
1495 &error);
1496 if (error != "") {
1497 SourcePos(manifestFile, tree.getLineNumber()).error(
1498 "failed to read attribute 'android:required': %s",
1499 error.string());
1500 goto bail;
1501 }
1502
1503 int32_t version = AaptXml::getIntegerAttribute(tree, androidSchema,
1504 "version", 0, &error);
1505 if (error != "") {
1506 SourcePos(manifestFile, tree.getLineNumber()).error(
1507 "failed to read attribute 'android:version': %s",
1508 error.string());
1509 goto bail;
1510 }
1511
1512 commonFeatures.features.add(name, Feature(req != 0, version));
Adam Lesinski2c72b682014-06-24 09:56:01 -07001513 if (req) {
1514 addParentFeatures(&commonFeatures, name);
Adam Lesinski282e1812014-01-23 18:17:42 -08001515 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001516 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001517 int vers = AaptXml::getIntegerAttribute(tree,
Adam Lesinski282e1812014-01-23 18:17:42 -08001518 GL_ES_VERSION_ATTR, &error);
1519 if (error == "") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001520 if (vers > commonFeatures.openGLESVersion) {
1521 commonFeatures.openGLESVersion = vers;
1522 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001523 }
1524 }
1525 } else if (tag == "uses-permission") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001526 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001527 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001528 SourcePos(manifestFile, tree.getLineNumber()).error(
1529 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001530 goto bail;
1531 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001532
1533 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001534 SourcePos(manifestFile, tree.getLineNumber()).error(
1535 "ERROR: missing 'android:name' for uses-permission");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001536 goto bail;
1537 }
1538
1539 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, false);
1540
1541 if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1542 hasWriteExternalStoragePermission = true;
1543 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1544 hasReadExternalStoragePermission = true;
1545 } else if (name == "android.permission.READ_PHONE_STATE") {
1546 hasReadPhoneStatePermission = true;
1547 } else if (name == "android.permission.READ_CONTACTS") {
1548 hasReadContactsPermission = true;
1549 } else if (name == "android.permission.WRITE_CONTACTS") {
1550 hasWriteContactsPermission = true;
1551 } else if (name == "android.permission.READ_CALL_LOG") {
1552 hasReadCallLogPermission = true;
1553 } else if (name == "android.permission.WRITE_CALL_LOG") {
1554 hasWriteCallLogPermission = true;
1555 }
1556
1557 printUsesPermission(name,
1558 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
1559 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
1560
1561 } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
1562 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1563 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001564 SourcePos(manifestFile, tree.getLineNumber()).error(
1565 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001566 goto bail;
1567 }
1568
1569 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001570 SourcePos(manifestFile, tree.getLineNumber()).error(
1571 "ERROR: missing 'android:name' for uses-permission-sdk-23");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001572 goto bail;
1573 }
1574
1575 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, true);
1576
1577 printUsesPermissionSdk23(
1578 name, AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
1579
Adam Lesinski282e1812014-01-23 18:17:42 -08001580 } else if (tag == "uses-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001581 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001582 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001583 printf("uses-package:'%s'\n",
1584 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001585 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001586 SourcePos(manifestFile, tree.getLineNumber()).error(
1587 "ERROR getting 'android:name' attribute: %s", error.string());
1588 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001589 }
1590 } else if (tag == "original-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001591 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001592 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001593 printf("original-package:'%s'\n",
1594 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001595 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001596 SourcePos(manifestFile, tree.getLineNumber()).error(
1597 "ERROR getting 'android:name' attribute: %s", error.string());
1598 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001599 }
1600 } else if (tag == "supports-gl-texture") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001601 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001602 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001603 printf("supports-gl-texture:'%s'\n",
1604 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001605 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001606 SourcePos(manifestFile, tree.getLineNumber()).error(
1607 "ERROR getting 'android:name' attribute: %s", error.string());
1608 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001609 }
1610 } else if (tag == "compatible-screens") {
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001611 printCompatibleScreens(tree, &error);
1612 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001613 SourcePos(manifestFile, tree.getLineNumber()).error(
1614 "ERROR getting compatible screens: %s", error.string());
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001615 goto bail;
1616 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001617 depth--;
1618 } else if (tag == "package-verifier") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001619 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001620 if (name != "" && error == "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001621 String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR,
1622 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001623 if (publicKey != "" && error == "") {
1624 printf("package-verifier: name='%s' publicKey='%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001625 ResTable::normalizeForOutput(name.string()).string(),
1626 ResTable::normalizeForOutput(publicKey.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001627 }
1628 }
1629 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001630 } else if (depth == 3) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001631 withinActivity = false;
1632 withinReceiver = false;
1633 withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001634 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001635 hasIntentFilter = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001636 hasMetaHostPaymentCategory = false;
1637 hasMetaOffHostPaymentCategory = false;
1638 hasBindDeviceAdminPermission = false;
1639 hasBindInputMethodPermission = false;
1640 hasBindAccessibilityServicePermission = false;
1641 hasBindPrintServicePermission = false;
1642 hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001643 hasRequiredSafAttributes = false;
1644 hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001645 hasBindDreamServicePermission = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001646 if (withinApplication) {
1647 if(tag == "activity") {
1648 withinActivity = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001649 activityName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001650 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001651 SourcePos(manifestFile, tree.getLineNumber()).error(
1652 "ERROR getting 'android:name' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001653 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001654 goto bail;
1655 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001656
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001657 activityLabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1658 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001659 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001660 SourcePos(manifestFile, tree.getLineNumber()).error(
1661 "ERROR getting 'android:label' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001662 error.string());
1663 goto bail;
1664 }
1665
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001666 activityIcon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1667 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001668 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001669 SourcePos(manifestFile, tree.getLineNumber()).error(
1670 "ERROR getting 'android:icon' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001671 error.string());
1672 goto bail;
1673 }
1674
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001675 activityBanner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1676 &error);
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001677 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001678 SourcePos(manifestFile, tree.getLineNumber()).error(
1679 "ERROR getting 'android:banner' attribute: %s",
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001680 error.string());
1681 goto bail;
1682 }
1683
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001684 int32_t orien = AaptXml::getResolvedIntegerAttribute(res, tree,
Michael Wrightec4fdec2013-09-06 16:50:52 -07001685 SCREEN_ORIENTATION_ATTR, &error);
1686 if (error == "") {
1687 if (orien == 0 || orien == 6 || orien == 8) {
1688 // Requests landscape, sensorLandscape, or reverseLandscape.
Adam Lesinski43158772015-11-11 15:13:55 -08001689 addImpliedFeature(
1690 &impliedFeatures, "android.hardware.screen.landscape",
1691 String8("one or more activities have specified a "
1692 "landscape orientation"),
1693 false);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001694 } else if (orien == 1 || orien == 7 || orien == 9) {
1695 // Requests portrait, sensorPortrait, or reversePortrait.
Adam Lesinski43158772015-11-11 15:13:55 -08001696 addImpliedFeature(
1697 &impliedFeatures, "android.hardware.screen.portrait",
1698 String8("one or more activities have specified a "
1699 "portrait orientation"),
1700 false);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001701 }
1702 }
1703 } else if (tag == "uses-library") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001704 String8 libraryName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001705 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001706 SourcePos(manifestFile, tree.getLineNumber()).error(
Michael Wrightec4fdec2013-09-06 16:50:52 -07001707 "ERROR getting 'android:name' attribute for uses-library"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001708 " %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001709 goto bail;
1710 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001711 int req = AaptXml::getIntegerAttribute(tree,
1712 REQUIRED_ATTR, 1);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001713 printf("uses-library%s:'%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001714 req ? "" : "-not-required", ResTable::normalizeForOutput(
1715 libraryName.string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001716 } else if (tag == "receiver") {
1717 withinReceiver = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001718 receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001719
1720 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001721 SourcePos(manifestFile, tree.getLineNumber()).error(
Michael Wrightec4fdec2013-09-06 16:50:52 -07001722 "ERROR getting 'android:name' attribute for receiver:"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001723 " %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001724 goto bail;
1725 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001726
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001727 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1728 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001729 if (error == "") {
1730 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1731 hasBindDeviceAdminPermission = true;
1732 }
1733 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001734 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001735 "ERROR getting 'android:permission' attribute for"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001736 " receiver '%s': %s",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001737 receiverName.string(), error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001738 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001739 } else if (tag == "service") {
1740 withinService = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001741 serviceName = 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(
1745 "ERROR getting 'android:name' attribute for "
1746 "service:%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_INPUT_METHOD") {
1754 hasBindInputMethodPermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001755 } else if (permission ==
1756 "android.permission.BIND_ACCESSIBILITY_SERVICE") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001757 hasBindAccessibilityServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001758 } else if (permission ==
1759 "android.permission.BIND_PRINT_SERVICE") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001760 hasBindPrintServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001761 } else if (permission ==
1762 "android.permission.BIND_NFC_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07001763 hasBindNfcServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001764 } else if (permission ==
1765 "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001766 hasBindNotificationListenerServicePermission = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001767 } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
1768 hasBindDreamServicePermission = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001769 }
1770 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001771 SourcePos(manifestFile, tree.getLineNumber()).error(
1772 "ERROR getting 'android:permission' attribute for "
1773 "service '%s': %s", serviceName.string(), error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001774 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001775 } else if (tag == "provider") {
1776 withinProvider = true;
1777
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001778 bool exported = AaptXml::getResolvedIntegerAttribute(res, tree,
1779 EXPORTED_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001780 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001781 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001782 "ERROR getting 'android:exported' attribute for provider:"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001783 " %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001784 goto bail;
1785 }
1786
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001787 bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute(
1788 res, tree, GRANT_URI_PERMISSIONS_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001789 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001790 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001791 "ERROR getting 'android:grantUriPermissions' attribute for "
Adam Lesinski10de3af12016-07-13 10:14:03 -07001792 "provider: %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001793 goto bail;
1794 }
1795
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001796 String8 permission = AaptXml::getResolvedAttribute(res, tree,
1797 PERMISSION_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001798 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001799 SourcePos(manifestFile, tree.getLineNumber()).error(
1800 "ERROR getting 'android:permission' attribute for "
1801 "provider: %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001802 goto bail;
1803 }
1804
1805 hasRequiredSafAttributes |= exported && grantUriPermissions &&
1806 permission == "android.permission.MANAGE_DOCUMENTS";
1807
Michael Wrightec4fdec2013-09-06 16:50:52 -07001808 } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001809 String8 metaDataName = AaptXml::getResolvedAttribute(res, tree,
1810 NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001811 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001812 SourcePos(manifestFile, tree.getLineNumber()).error(
1813 "ERROR getting 'android:name' attribute for "
1814 "meta-data: %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001815 goto bail;
1816 }
Maurice Chu2675f762013-10-22 17:33:11 -07001817 printf("meta-data: name='%s' ",
1818 ResTable::normalizeForOutput(metaDataName.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001819 printResolvedResourceAttribute(res, tree, VALUE_ATTR, String8("value"),
Maurice Chu76327312013-10-16 18:28:46 -07001820 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001821 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001822 // Try looking for a RESOURCE_ATTR
1823 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001824 printResolvedResourceAttribute(res, tree, RESOURCE_ATTR,
Maurice Chu76327312013-10-16 18:28:46 -07001825 String8("resource"), &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001826 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001827 SourcePos(manifestFile, tree.getLineNumber()).error(
1828 "ERROR getting 'android:value' or "
Maurice Chu76327312013-10-16 18:28:46 -07001829 "'android:resource' attribute for "
Adam Lesinski10de3af12016-07-13 10:14:03 -07001830 "meta-data: %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001831 goto bail;
1832 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001833 }
Maurice Chu76327312013-10-16 18:28:46 -07001834 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001835 } else if (withinSupportsInput && tag == "input-type") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001836 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001837 if (name != "" && error == "") {
1838 supportedInput.add(name);
1839 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001840 SourcePos(manifestFile, tree.getLineNumber()).error(
1841 "ERROR getting 'android:name' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001842 error.string());
1843 goto bail;
1844 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001845 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001846 } else if (withinFeatureGroup && tag == "uses-feature") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001847 const String8 androidSchema("http://schemas.android.com/apk/res/android");
Adam Lesinski2c72b682014-06-24 09:56:01 -07001848 FeatureGroup& top = featureGroups.editTop();
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001849
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001850 String8 name = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001851 if (name != "" && error == "") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001852 Feature feature(true);
1853
1854 int32_t featureVers = AaptXml::getIntegerAttribute(
1855 tree, androidSchema.string(), "version", 0, &error);
1856 if (error == "") {
1857 feature.version = featureVers;
1858 } else {
1859 SourcePos(manifestFile, tree.getLineNumber()).error(
1860 "failed to read attribute 'android:version': %s",
1861 error.string());
1862 goto bail;
1863 }
1864
1865 top.features.add(name, feature);
Adam Lesinskid3edfde2014-08-08 17:32:44 -07001866 addParentFeatures(&top, name);
Adam Lesinski694d0a72016-04-06 16:12:04 -07001867
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001868 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001869 int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR,
1870 &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001871 if (error == "") {
1872 if (vers > top.openGLESVersion) {
1873 top.openGLESVersion = vers;
1874 }
1875 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001876 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001877 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001878 } else if (depth == 4) {
1879 if (tag == "intent-filter") {
1880 hasIntentFilter = true;
1881 withinIntentFilter = true;
1882 actMainActivity = false;
1883 actWidgetReceivers = false;
1884 actImeService = false;
1885 actWallpaperService = false;
1886 actAccessibilityService = false;
1887 actPrintService = false;
1888 actDeviceAdminEnabled = false;
1889 actHostApduService = false;
1890 actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001891 actDocumentsProvider = false;
1892 actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001893 actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001894 actCamera = false;
1895 actCameraSecure = false;
1896 catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001897 } else if (withinService && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001898 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001899 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001900 SourcePos(manifestFile, tree.getLineNumber()).error(
1901 "ERROR getting 'android:name' attribute for "
1902 "meta-data tag in service '%s': %s", serviceName.string(),
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001903 error.string());
Adam Lesinski94fc9122013-09-30 17:16:09 -07001904 goto bail;
1905 }
1906
1907 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1908 name == "android.nfc.cardemulation.off_host_apdu_service") {
1909 bool offHost = true;
1910 if (name == "android.nfc.cardemulation.host_apdu_service") {
1911 offHost = false;
1912 }
1913
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001914 String8 xmlPath = AaptXml::getResolvedAttribute(res, tree,
1915 RESOURCE_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001916 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001917 SourcePos(manifestFile, tree.getLineNumber()).error(
1918 "ERROR getting 'android:resource' attribute for "
1919 "meta-data tag in service '%s': %s",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001920 serviceName.string(), error.string());
Adam Lesinski94fc9122013-09-30 17:16:09 -07001921 goto bail;
1922 }
1923
1924 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1925 offHost, &error);
1926 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001927 SourcePos(manifestFile, tree.getLineNumber()).error(
1928 "ERROR getting AID category for service '%s'",
Adam Lesinski94fc9122013-09-30 17:16:09 -07001929 serviceName.string());
1930 goto bail;
1931 }
1932
1933 const size_t catLen = categories.size();
1934 for (size_t i = 0; i < catLen; i++) {
1935 bool paymentCategory = (categories[i] == "payment");
1936 if (offHost) {
1937 hasMetaOffHostPaymentCategory |= paymentCategory;
1938 } else {
1939 hasMetaHostPaymentCategory |= paymentCategory;
1940 }
1941 }
1942 }
1943 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001944 } else if ((depth == 5) && withinIntentFilter) {
1945 String8 action;
1946 if (tag == "action") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001947 action = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001948 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001949 SourcePos(manifestFile, tree.getLineNumber()).error(
1950 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001951 goto bail;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001952 }
1953
Adam Lesinskia5018c92013-09-30 16:23:15 -07001954 if (withinActivity) {
1955 if (action == "android.intent.action.MAIN") {
1956 isMainActivity = true;
1957 actMainActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001958 } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
1959 action == "android.media.action.VIDEO_CAMERA") {
1960 actCamera = true;
1961 } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
1962 actCameraSecure = true;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001963 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001964 } else if (withinReceiver) {
1965 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1966 actWidgetReceivers = true;
1967 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1968 actDeviceAdminEnabled = true;
1969 }
1970 } else if (withinService) {
1971 if (action == "android.view.InputMethod") {
1972 actImeService = true;
1973 } else if (action == "android.service.wallpaper.WallpaperService") {
1974 actWallpaperService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001975 } else if (action ==
1976 "android.accessibilityservice.AccessibilityService") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001977 actAccessibilityService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001978 } else if (action =="android.printservice.PrintService") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001979 actPrintService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001980 } else if (action ==
1981 "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07001982 actHostApduService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001983 } else if (action ==
1984 "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07001985 actOffHostApduService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001986 } else if (action ==
1987 "android.service.notification.NotificationListenerService") {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001988 actNotificationListenerService = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001989 } else if (action == "android.service.dreams.DreamService") {
1990 actDreamService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001991 }
1992 } else if (withinProvider) {
1993 if (action == "android.content.action.DOCUMENTS_PROVIDER") {
1994 actDocumentsProvider = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001995 }
1996 }
1997 if (action == "android.intent.action.SEARCH") {
1998 isSearchable = true;
1999 }
2000 }
2001
2002 if (tag == "category") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07002003 String8 category = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07002004 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07002005 SourcePos(manifestFile, tree.getLineNumber()).error(
2006 "ERROR getting 'name' attribute: %s", error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07002007 goto bail;
2008 }
2009 if (withinActivity) {
2010 if (category == "android.intent.category.LAUNCHER") {
2011 isLauncherActivity = true;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08002012 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
2013 isLeanbackLauncherActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002014 } else if (category == "android.intent.category.HOME") {
2015 catLauncher = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08002016 }
2017 }
2018 }
2019 }
2020 }
2021
2022 // Pre-1.6 implicitly granted permission compatibility logic
2023 if (targetSdk < 4) {
2024 if (!hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002025 printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
2026 printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
2027 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002028 hasWriteExternalStoragePermission = true;
2029 }
2030 if (!hasReadPhoneStatePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002031 printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
2032 printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
2033 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002034 }
2035 }
2036
2037 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
2038 // force them to always take READ_EXTERNAL_STORAGE as well. We always
2039 // do this (regardless of target API version) because we can't have
2040 // an app with write permission but not read permission.
2041 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002042 printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"));
2043 printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
2044 String8("requested WRITE_EXTERNAL_STORAGE"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002045 }
2046
2047 // Pre-JellyBean call log permission compatibility.
2048 if (targetSdk < 16) {
2049 if (!hasReadCallLogPermission && hasReadContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002050 printUsesPermission(String8("android.permission.READ_CALL_LOG"));
2051 printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
2052 String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002053 }
2054 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002055 printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
2056 printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
2057 String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002058 }
2059 }
2060
Adam Lesinskica955a42016-08-01 16:44:29 -07002061 // If the app hasn't declared the touchscreen as a feature requirement (either
2062 // directly or implied, required or not), then the faketouch feature is implied.
2063 if (!hasFeature("android.hardware.touchscreen", commonFeatures, impliedFeatures)) {
2064 addImpliedFeature(&impliedFeatures, "android.hardware.faketouch",
Adam Lesinski43158772015-11-11 15:13:55 -08002065 String8("default feature for all apps"), false);
Adam Lesinskica955a42016-08-01 16:44:29 -07002066 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07002067
2068 const size_t numFeatureGroups = featureGroups.size();
2069 if (numFeatureGroups == 0) {
2070 // If no <feature-group> tags were defined, apply auto-implied features.
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002071 printDefaultFeatureGroup(commonFeatures, impliedFeatures);
Adam Lesinski2c72b682014-06-24 09:56:01 -07002072
2073 } else {
2074 // <feature-group> tags are defined, so we ignore implied features and
2075 for (size_t i = 0; i < numFeatureGroups; i++) {
2076 FeatureGroup& grp = featureGroups.editItemAt(i);
2077
Adam Lesinskid7a94da2014-07-25 14:38:54 -07002078 if (commonFeatures.openGLESVersion > grp.openGLESVersion) {
2079 grp.openGLESVersion = commonFeatures.openGLESVersion;
2080 }
2081
Adam Lesinski2c72b682014-06-24 09:56:01 -07002082 // Merge the features defined in the top level (not inside a <feature-group>)
2083 // with this feature group.
2084 const size_t numCommonFeatures = commonFeatures.features.size();
2085 for (size_t j = 0; j < numCommonFeatures; j++) {
2086 if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07002087 grp.features.add(commonFeatures.features.keyAt(j),
2088 commonFeatures.features[j]);
Adam Lesinski2c72b682014-06-24 09:56:01 -07002089 }
2090 }
2091
Adam Lesinski73a05112014-12-08 12:53:17 -08002092 if (!grp.features.isEmpty()) {
Adam Lesinski2c72b682014-06-24 09:56:01 -07002093 printFeatureGroup(grp);
Adam Lesinski282e1812014-01-23 18:17:42 -08002094 }
2095 }
2096 }
2097
Adam Lesinski282e1812014-01-23 18:17:42 -08002098
Adam Lesinski282e1812014-01-23 18:17:42 -08002099 if (hasWidgetReceivers) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002100 printComponentPresence("app-widget");
Adam Lesinski282e1812014-01-23 18:17:42 -08002101 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002102 if (hasDeviceAdminReceiver) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002103 printComponentPresence("device-admin");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002104 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002105 if (hasImeService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002106 printComponentPresence("ime");
Adam Lesinski282e1812014-01-23 18:17:42 -08002107 }
2108 if (hasWallpaperService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002109 printComponentPresence("wallpaper");
Adam Lesinski282e1812014-01-23 18:17:42 -08002110 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002111 if (hasAccessibilityService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002112 printComponentPresence("accessibility");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002113 }
2114 if (hasPrintService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002115 printComponentPresence("print-service");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002116 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07002117 if (hasPaymentService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002118 printComponentPresence("payment");
2119 }
2120 if (isSearchable) {
2121 printComponentPresence("search");
2122 }
2123 if (hasDocumentsProvider) {
2124 printComponentPresence("document-provider");
2125 }
2126 if (hasLauncher) {
2127 printComponentPresence("launcher");
2128 }
2129 if (hasNotificationListenerService) {
2130 printComponentPresence("notification-listener");
2131 }
John Spurlockeb8d1be2014-06-25 17:46:15 -04002132 if (hasDreamService) {
2133 printComponentPresence("dream");
2134 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002135 if (hasCameraActivity) {
2136 printComponentPresence("camera");
2137 }
2138 if (hasCameraSecureActivity) {
2139 printComponentPresence("camera-secure");
2140 }
2141
2142 if (hasMainActivity) {
2143 printf("main\n");
Adam Lesinski94fc9122013-09-30 17:16:09 -07002144 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002145 if (hasOtherActivities) {
2146 printf("other-activities\n");
2147 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002148 if (hasOtherReceivers) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002149 printf("other-receivers\n");
2150 }
2151 if (hasOtherServices) {
2152 printf("other-services\n");
2153 }
2154
2155 // For modern apps, if screen size buckets haven't been specified
2156 // but the new width ranges have, then infer the buckets from them.
2157 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
2158 && requiresSmallestWidthDp > 0) {
2159 int compatWidth = compatibleWidthLimitDp;
2160 if (compatWidth <= 0) {
2161 compatWidth = requiresSmallestWidthDp;
2162 }
2163 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
2164 smallScreen = -1;
2165 } else {
2166 smallScreen = 0;
2167 }
2168 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
2169 normalScreen = -1;
2170 } else {
2171 normalScreen = 0;
2172 }
2173 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
2174 largeScreen = -1;
2175 } else {
2176 largeScreen = 0;
2177 }
2178 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
2179 xlargeScreen = -1;
2180 } else {
2181 xlargeScreen = 0;
2182 }
2183 }
2184
2185 // Determine default values for any unspecified screen sizes,
2186 // based on the target SDK of the package. As of 4 (donut)
2187 // the screen size support was introduced, so all default to
2188 // enabled.
2189 if (smallScreen > 0) {
2190 smallScreen = targetSdk >= 4 ? -1 : 0;
2191 }
2192 if (normalScreen > 0) {
2193 normalScreen = -1;
2194 }
2195 if (largeScreen > 0) {
2196 largeScreen = targetSdk >= 4 ? -1 : 0;
2197 }
2198 if (xlargeScreen > 0) {
2199 // Introduced in Gingerbread.
2200 xlargeScreen = targetSdk >= 9 ? -1 : 0;
2201 }
2202 if (anyDensity > 0) {
2203 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
2204 || compatibleWidthLimitDp > 0) ? -1 : 0;
2205 }
2206 printf("supports-screens:");
2207 if (smallScreen != 0) {
2208 printf(" 'small'");
2209 }
2210 if (normalScreen != 0) {
2211 printf(" 'normal'");
2212 }
2213 if (largeScreen != 0) {
2214 printf(" 'large'");
2215 }
2216 if (xlargeScreen != 0) {
2217 printf(" 'xlarge'");
2218 }
2219 printf("\n");
2220 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
2221 if (requiresSmallestWidthDp > 0) {
2222 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
2223 }
2224 if (compatibleWidthLimitDp > 0) {
2225 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
2226 }
2227 if (largestWidthLimitDp > 0) {
2228 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
2229 }
2230
2231 printf("locales:");
2232 const size_t NL = locales.size();
2233 for (size_t i=0; i<NL; i++) {
2234 const char* localeStr = locales[i].string();
2235 if (localeStr == NULL || strlen(localeStr) == 0) {
2236 localeStr = "--_--";
2237 }
2238 printf(" '%s'", localeStr);
2239 }
2240 printf("\n");
2241
2242 printf("densities:");
2243 const size_t ND = densities.size();
2244 for (size_t i=0; i<ND; i++) {
2245 printf(" '%d'", densities[i]);
2246 }
2247 printf("\n");
2248
2249 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
2250 if (dir != NULL) {
2251 if (dir->getFileCount() > 0) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002252 SortedVector<String8> architectures;
Adam Lesinski282e1812014-01-23 18:17:42 -08002253 for (size_t i=0; i<dir->getFileCount(); i++) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002254 architectures.add(ResTable::normalizeForOutput(
2255 dir->getFileName(i).string()));
Adam Lesinski282e1812014-01-23 18:17:42 -08002256 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07002257
2258 bool outputAltNativeCode = false;
2259 // A multiArch package is one that contains 64-bit and
2260 // 32-bit versions of native code and expects 3rd-party
2261 // apps to load these native code libraries. Since most
2262 // 64-bit systems also support 32-bit apps, the apps
2263 // loading this multiArch package's code may be either
2264 // 32-bit or 64-bit.
2265 if (hasMultiArch) {
2266 // If this is a multiArch package, report the 64-bit
2267 // version only. Then as a separate entry, report the
2268 // rest.
2269 //
2270 // If we report the 32-bit architecture, this APK will
2271 // be installed on a 32-bit device, causing a large waste
2272 // of bandwidth and disk space. This assumes that
2273 // the developer of the multiArch package has also
2274 // made a version that is 32-bit only.
2275 String8 intel64("x86_64");
2276 String8 arm64("arm64-v8a");
2277 ssize_t index = architectures.indexOf(intel64);
2278 if (index < 0) {
2279 index = architectures.indexOf(arm64);
2280 }
2281
2282 if (index >= 0) {
2283 printf("native-code: '%s'\n", architectures[index].string());
2284 architectures.removeAt(index);
2285 outputAltNativeCode = true;
2286 }
2287 }
2288
2289 const size_t archCount = architectures.size();
2290 if (archCount > 0) {
2291 if (outputAltNativeCode) {
2292 printf("alt-");
2293 }
2294 printf("native-code:");
2295 for (size_t i = 0; i < archCount; i++) {
2296 printf(" '%s'", architectures[i].string());
2297 }
2298 printf("\n");
2299 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002300 }
2301 delete dir;
2302 }
2303 } else if (strcmp("badger", option) == 0) {
2304 printf("%s", CONSOLE_DATA);
2305 } else if (strcmp("configurations", option) == 0) {
2306 Vector<ResTable_config> configs;
2307 res.getConfigurations(&configs);
2308 const size_t N = configs.size();
2309 for (size_t i=0; i<N; i++) {
2310 printf("%s\n", configs[i].toString().string());
2311 }
2312 } else {
2313 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2314 goto bail;
2315 }
2316 }
2317
2318 result = NO_ERROR;
2319
2320bail:
Adam Lesinski10de3af12016-07-13 10:14:03 -07002321 if (SourcePos::hasErrors()) {
2322 SourcePos::printErrors(stderr);
2323 }
2324
Adam Lesinski282e1812014-01-23 18:17:42 -08002325 if (asset) {
2326 delete asset;
2327 }
2328 return (result != NO_ERROR);
2329}
2330
2331
2332/*
2333 * Handle the "add" command, which wants to add files to a new or
2334 * pre-existing archive.
2335 */
2336int doAdd(Bundle* bundle)
2337{
2338 ZipFile* zip = NULL;
2339 status_t result = UNKNOWN_ERROR;
2340 const char* zipFileName;
2341
2342 if (bundle->getUpdate()) {
2343 /* avoid confusion */
2344 fprintf(stderr, "ERROR: can't use '-u' with add\n");
2345 goto bail;
2346 }
2347
2348 if (bundle->getFileSpecCount() < 1) {
2349 fprintf(stderr, "ERROR: must specify zip file name\n");
2350 goto bail;
2351 }
2352 zipFileName = bundle->getFileSpecEntry(0);
2353
2354 if (bundle->getFileSpecCount() < 2) {
2355 fprintf(stderr, "NOTE: nothing to do\n");
2356 goto bail;
2357 }
2358
2359 zip = openReadWrite(zipFileName, true);
2360 if (zip == NULL) {
2361 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2362 goto bail;
2363 }
2364
2365 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2366 const char* fileName = bundle->getFileSpecEntry(i);
2367
2368 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
2369 printf(" '%s'... (from gzip)\n", fileName);
2370 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
2371 } else {
2372 if (bundle->getJunkPath()) {
2373 String8 storageName = String8(fileName).getPathLeaf();
Maurice Chu2675f762013-10-22 17:33:11 -07002374 printf(" '%s' as '%s'...\n", fileName,
2375 ResTable::normalizeForOutput(storageName.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08002376 result = zip->add(fileName, storageName.string(),
2377 bundle->getCompressionMethod(), NULL);
2378 } else {
2379 printf(" '%s'...\n", fileName);
2380 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2381 }
2382 }
2383 if (result != NO_ERROR) {
2384 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2385 if (result == NAME_NOT_FOUND) {
2386 fprintf(stderr, ": file not found\n");
2387 } else if (result == ALREADY_EXISTS) {
2388 fprintf(stderr, ": already exists in archive\n");
2389 } else {
2390 fprintf(stderr, "\n");
2391 }
2392 goto bail;
2393 }
2394 }
2395
2396 result = NO_ERROR;
2397
2398bail:
2399 delete zip;
2400 return (result != NO_ERROR);
2401}
2402
2403
2404/*
2405 * Delete files from an existing archive.
2406 */
2407int doRemove(Bundle* bundle)
2408{
2409 ZipFile* zip = NULL;
2410 status_t result = UNKNOWN_ERROR;
2411 const char* zipFileName;
2412
2413 if (bundle->getFileSpecCount() < 1) {
2414 fprintf(stderr, "ERROR: must specify zip file name\n");
2415 goto bail;
2416 }
2417 zipFileName = bundle->getFileSpecEntry(0);
2418
2419 if (bundle->getFileSpecCount() < 2) {
2420 fprintf(stderr, "NOTE: nothing to do\n");
2421 goto bail;
2422 }
2423
2424 zip = openReadWrite(zipFileName, false);
2425 if (zip == NULL) {
2426 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2427 zipFileName);
2428 goto bail;
2429 }
2430
2431 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2432 const char* fileName = bundle->getFileSpecEntry(i);
2433 ZipEntry* entry;
2434
2435 entry = zip->getEntryByName(fileName);
2436 if (entry == NULL) {
2437 printf(" '%s' NOT FOUND\n", fileName);
2438 continue;
2439 }
2440
2441 result = zip->remove(entry);
2442
2443 if (result != NO_ERROR) {
2444 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2445 bundle->getFileSpecEntry(i), zipFileName);
2446 goto bail;
2447 }
2448 }
2449
2450 /* update the archive */
2451 zip->flush();
2452
2453bail:
2454 delete zip;
2455 return (result != NO_ERROR);
2456}
2457
Adam Lesinski3921e872014-05-13 10:56:25 -07002458static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002459 const size_t numDirs = dir->getDirs().size();
2460 for (size_t i = 0; i < numDirs; i++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002461 bool ignore = ignoreConfig;
2462 const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2463 const char* dirStr = subDir->getLeaf().string();
2464 if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2465 ignore = true;
2466 }
2467 status_t err = addResourcesToBuilder(subDir, builder, ignore);
Adam Lesinskifab50872014-04-16 14:40:42 -07002468 if (err != NO_ERROR) {
2469 return err;
2470 }
2471 }
2472
2473 const size_t numFiles = dir->getFiles().size();
2474 for (size_t i = 0; i < numFiles; i++) {
2475 sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2476 const size_t numConfigs = gp->getFiles().size();
2477 for (size_t j = 0; j < numConfigs; j++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002478 status_t err = NO_ERROR;
2479 if (ignoreConfig) {
2480 err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2481 } else {
2482 err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2483 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002484 if (err != NO_ERROR) {
2485 fprintf(stderr, "Failed to add %s (%s) to builder.\n",
2486 gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
2487 return err;
2488 }
2489 }
2490 }
2491 return NO_ERROR;
2492}
2493
2494static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2495 if (split->isBase()) {
2496 return original;
2497 }
2498
2499 String8 ext(original.getPathExtension());
2500 if (ext == String8(".apk")) {
2501 return String8::format("%s_%s%s",
2502 original.getBasePath().string(),
2503 split->getDirectorySafeName().string(),
2504 ext.string());
2505 }
2506
2507 return String8::format("%s_%s", original.string(),
2508 split->getDirectorySafeName().string());
2509}
Adam Lesinski282e1812014-01-23 18:17:42 -08002510
2511/*
2512 * Package up an asset directory and associated application files.
2513 */
2514int doPackage(Bundle* bundle)
2515{
2516 const char* outputAPKFile;
2517 int retVal = 1;
2518 status_t err;
2519 sp<AaptAssets> assets;
2520 int N;
2521 FILE* fp;
2522 String8 dependencyFile;
Adam Lesinskifab50872014-04-16 14:40:42 -07002523 sp<ApkBuilder> builder;
Adam Lesinski282e1812014-01-23 18:17:42 -08002524
Anton Krumina2ef5c02014-03-12 14:46:44 -07002525 // -c en_XA or/and ar_XB means do pseudolocalization
Adam Lesinskifab50872014-04-16 14:40:42 -07002526 sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2527 err = configFilter->parse(bundle->getConfigurations());
Adam Lesinski282e1812014-01-23 18:17:42 -08002528 if (err != NO_ERROR) {
2529 goto bail;
2530 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002531 if (configFilter->containsPseudo()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002532 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2533 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002534 if (configFilter->containsPseudoBidi()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002535 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
Adam Lesinski282e1812014-01-23 18:17:42 -08002536 }
2537
2538 N = bundle->getFileSpecCount();
2539 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
Adam Lesinski09384302014-01-22 16:07:42 -08002540 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002541 fprintf(stderr, "ERROR: no input files\n");
2542 goto bail;
2543 }
2544
2545 outputAPKFile = bundle->getOutputAPKFile();
2546
2547 // Make sure the filenames provided exist and are of the appropriate type.
2548 if (outputAPKFile) {
2549 FileType type;
2550 type = getFileType(outputAPKFile);
2551 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2552 fprintf(stderr,
2553 "ERROR: output file '%s' exists but is not regular file\n",
2554 outputAPKFile);
2555 goto bail;
2556 }
2557 }
2558
2559 // Load the assets.
2560 assets = new AaptAssets();
2561
2562 // Set up the resource gathering in assets if we're going to generate
2563 // dependency files. Every time we encounter a resource while slurping
2564 // the tree, we'll add it to these stores so we have full resource paths
2565 // to write to a dependency file.
2566 if (bundle->getGenDependencies()) {
2567 sp<FilePathStore> resPathStore = new FilePathStore;
2568 assets->setFullResPaths(resPathStore);
2569 sp<FilePathStore> assetPathStore = new FilePathStore;
2570 assets->setFullAssetPaths(assetPathStore);
2571 }
2572
2573 err = assets->slurpFromArgs(bundle);
2574 if (err < 0) {
2575 goto bail;
2576 }
2577
2578 if (bundle->getVerbose()) {
2579 assets->print(String8());
2580 }
2581
Adam Lesinskifab50872014-04-16 14:40:42 -07002582 // Create the ApkBuilder, which will collect the compiled files
2583 // to write to the final APK (or sets of APKs if we are building
2584 // a Split APK.
2585 builder = new ApkBuilder(configFilter);
2586
2587 // If we are generating a Split APK, find out which configurations to split on.
2588 if (bundle->getSplitConfigurations().size() > 0) {
2589 const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2590 const size_t numSplits = splitStrs.size();
2591 for (size_t i = 0; i < numSplits; i++) {
2592 std::set<ConfigDescription> configs;
2593 if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2594 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2595 goto bail;
2596 }
2597
2598 err = builder->createSplitForConfigs(configs);
2599 if (err != NO_ERROR) {
2600 goto bail;
2601 }
2602 }
2603 }
2604
Adam Lesinski282e1812014-01-23 18:17:42 -08002605 // If they asked for any fileAs that need to be compiled, do so.
2606 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002607 err = buildResources(bundle, assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002608 if (err != 0) {
2609 goto bail;
2610 }
2611 }
2612
2613 // At this point we've read everything and processed everything. From here
2614 // on out it's just writing output files.
2615 if (SourcePos::hasErrors()) {
2616 goto bail;
2617 }
2618
2619 // Update symbols with information about which ones are needed as Java symbols.
2620 assets->applyJavaSymbols();
2621 if (SourcePos::hasErrors()) {
2622 goto bail;
2623 }
2624
2625 // If we've been asked to generate a dependency file, do that here
2626 if (bundle->getGenDependencies()) {
2627 // If this is the packaging step, generate the dependency file next to
2628 // the output apk (e.g. bin/resources.ap_.d)
2629 if (outputAPKFile) {
2630 dependencyFile = String8(outputAPKFile);
2631 // Add the .d extension to the dependency file.
2632 dependencyFile.append(".d");
2633 } else {
2634 // Else if this is the R.java dependency generation step,
2635 // generate the dependency file in the R.java package subdirectory
2636 // e.g. gen/com/foo/app/R.java.d
2637 dependencyFile = String8(bundle->getRClassDir());
2638 dependencyFile.appendPath("R.java.d");
2639 }
2640 // Make sure we have a clean dependency file to start with
2641 fp = fopen(dependencyFile, "w");
2642 fclose(fp);
2643 }
2644
2645 // Write out R.java constants
2646 if (!assets->havePrivateSymbols()) {
2647 if (bundle->getCustomPackage() == NULL) {
2648 // Write the R.java file into the appropriate class directory
2649 // e.g. gen/com/foo/app/R.java
Adam Lesinski1e4663852014-08-15 14:47:28 -07002650 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002651 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002652 } else {
2653 const String8 customPkg(bundle->getCustomPackage());
Adam Lesinski1e4663852014-08-15 14:47:28 -07002654 err = writeResourceSymbols(bundle, assets, customPkg, true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002655 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002656 }
2657 if (err < 0) {
2658 goto bail;
2659 }
2660 // If we have library files, we're going to write our R.java file into
2661 // the appropriate class directory for those libraries as well.
2662 // e.g. gen/com/foo/app/lib/R.java
2663 if (bundle->getExtraPackages() != NULL) {
2664 // Split on colon
2665 String8 libs(bundle->getExtraPackages());
2666 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2667 while (packageString != NULL) {
2668 // Write the R.java file out with the correct package name
Marcin Kosiba0f3a5a62014-09-11 13:48:48 +01002669 err = writeResourceSymbols(bundle, assets, String8(packageString), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002670 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002671 if (err < 0) {
2672 goto bail;
2673 }
2674 packageString = strtok(NULL, ":");
2675 }
2676 libs.unlockBuffer();
2677 }
2678 } else {
Adam Lesinski1e4663852014-08-15 14:47:28 -07002679 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002680 if (err < 0) {
2681 goto bail;
2682 }
Adam Lesinski1e4663852014-08-15 14:47:28 -07002683 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002684 if (err < 0) {
2685 goto bail;
2686 }
2687 }
2688
2689 // Write out the ProGuard file
2690 err = writeProguardFile(bundle, assets);
2691 if (err < 0) {
2692 goto bail;
2693 }
2694
Rohit Agrawal86229cb2016-04-21 16:29:58 -07002695 // Write out the Main Dex ProGuard file
2696 err = writeMainDexProguardFile(bundle, assets);
2697 if (err < 0) {
2698 goto bail;
2699 }
2700
Adam Lesinski282e1812014-01-23 18:17:42 -08002701 // Write the apk
2702 if (outputAPKFile) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002703 // Gather all resources and add them to the APK Builder. The builder will then
2704 // figure out which Split they belong in.
2705 err = addResourcesToBuilder(assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002706 if (err != NO_ERROR) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002707 goto bail;
2708 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002709
2710 const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2711 const size_t numSplits = splits.size();
2712 for (size_t i = 0; i < numSplits; i++) {
2713 const sp<ApkSplit>& split = splits[i];
2714 String8 outputPath = buildApkName(String8(outputAPKFile), split);
2715 err = writeAPK(bundle, outputPath, split);
2716 if (err != NO_ERROR) {
2717 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2718 goto bail;
2719 }
2720 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002721 }
2722
2723 // If we've been asked to generate a dependency file, we need to finish up here.
2724 // the writeResourceSymbols and writeAPK functions have already written the target
2725 // half of the dependency file, now we need to write the prerequisites. (files that
2726 // the R.java file or .ap_ file depend on)
2727 if (bundle->getGenDependencies()) {
2728 // Now that writeResourceSymbols or writeAPK has taken care of writing
2729 // the targets to our dependency file, we'll write the prereqs
2730 fp = fopen(dependencyFile, "a+");
2731 fprintf(fp, " : ");
2732 bool includeRaw = (outputAPKFile != NULL);
2733 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2734 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2735 // and therefore was not added to our pathstores during slurping
2736 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2737 fclose(fp);
2738 }
2739
2740 retVal = 0;
2741bail:
2742 if (SourcePos::hasErrors()) {
2743 SourcePos::printErrors(stderr);
2744 }
2745 return retVal;
2746}
2747
2748/*
2749 * Do PNG Crunching
2750 * PRECONDITIONS
2751 * -S flag points to a source directory containing drawable* folders
2752 * -C flag points to destination directory. The folder structure in the
2753 * source directory will be mirrored to the destination (cache) directory
2754 *
2755 * POSTCONDITIONS
2756 * Destination directory will be updated to match the PNG files in
Maurice Chu2675f762013-10-22 17:33:11 -07002757 * the source directory.
Adam Lesinski282e1812014-01-23 18:17:42 -08002758 */
2759int doCrunch(Bundle* bundle)
2760{
2761 fprintf(stdout, "Crunching PNG Files in ");
2762 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2763 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2764
2765 updatePreProcessedCache(bundle);
2766
2767 return NO_ERROR;
2768}
2769
2770/*
2771 * Do PNG Crunching on a single flag
2772 * -i points to a single png file
2773 * -o points to a single png output file
2774 */
2775int doSingleCrunch(Bundle* bundle)
2776{
2777 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2778 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2779
2780 String8 input(bundle->getSingleCrunchInputFile());
2781 String8 output(bundle->getSingleCrunchOutputFile());
2782
2783 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2784 // we can't return the status_t as it gets truncate to the lower 8 bits.
2785 return 42;
2786 }
2787
2788 return NO_ERROR;
2789}
2790
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002791int runInDaemonMode(Bundle* bundle) {
2792 std::cout << "Ready" << std::endl;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002793 for (std::string cmd; std::getline(std::cin, cmd);) {
2794 if (cmd == "quit") {
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002795 return NO_ERROR;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002796 } else if (cmd == "s") {
2797 // Two argument crunch
2798 std::string inputFile, outputFile;
2799 std::getline(std::cin, inputFile);
2800 std::getline(std::cin, outputFile);
2801 bundle->setSingleCrunchInputFile(inputFile.c_str());
2802 bundle->setSingleCrunchOutputFile(outputFile.c_str());
2803 std::cout << "Crunching " << inputFile << std::endl;
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002804 if (doSingleCrunch(bundle) != NO_ERROR) {
2805 std::cout << "Error" << std::endl;
2806 }
2807 std::cout << "Done" << std::endl;
2808 } else {
2809 // in case of invalid command, just bail out.
2810 std::cerr << "Unknown command" << std::endl;
2811 return -1;
2812 }
2813 }
2814 return -1;
2815}
2816
Adam Lesinski282e1812014-01-23 18:17:42 -08002817char CONSOLE_DATA[2925] = {
2818 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2819 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2820 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2821 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2822 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2823 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2824 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2825 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2826 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2827 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2828 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2829 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2830 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2831 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2832 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2833 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2834 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2835 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2836 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2837 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2838 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2839 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2840 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2841 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2842 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2843 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2844 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2845 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2846 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2847 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2848 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2849 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2850 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2851 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2852 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2853 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2854 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2855 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2856 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2857 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2858 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2859 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2860 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2861 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2862 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2863 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2864 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2865 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2866 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2867 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2868 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2869 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2870 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2871 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2872 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2873 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2874 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2875 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2876 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2877 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2878 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2879 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2880 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2881 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2882 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2883 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2884 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2885 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2886 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2887 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2888 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2889 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2890 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2891 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2892 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2893 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2894 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2895 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2896 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2897 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2898 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2899 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2900 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2901 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2902 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2903 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2904 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2905 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2906 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2907 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2908 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2909 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2910 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2911 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2912 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2913 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2914 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2915 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2916 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2917 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2918 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2919 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2920 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2921 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2922 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2923 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2924 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2925 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2926 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2927 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2928 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2929 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2930 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2931 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2932 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2933 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2934 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2935 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2936 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2937 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2938 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2939 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2940 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2941 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2942 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2943 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2944 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2945 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2946 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2947 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2948 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2949 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2950 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2951 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2952 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2953 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2954 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2955 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2956 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2957 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2958 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2959 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2960 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2961 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2962 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2963 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2964 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2965 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2966 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2967 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2968 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2969 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2970 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2971 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2972 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2973 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2974 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2975 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2976 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2977 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2978 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2979 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2980 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2981 };