blob: d80aaba8f64bfe4d8ed9ad0de3ce52b3299f7b44 [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 Lesinski5f3b2ec2015-12-02 15:40:19 -0800543 const char* name, const char* 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 }
556 feature->reasons.add(String8(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",
654 String8::format("requested %s permission", name.string())
655 .string(), impliedBySdk23Permission);
656 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
657 addImpliedFeature(impliedFeatures, "android.hardware.location.gps",
658 String8::format("requested %s permission", name.string())
659 .string(), impliedBySdk23Permission);
660 addImpliedFeature(impliedFeatures, "android.hardware.location",
661 String8::format("requested %s permission", name.string())
662 .string(), impliedBySdk23Permission);
663 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
664 addImpliedFeature(impliedFeatures, "android.hardware.location",
665 String8::format("requested %s permission", name.string())
666 .string(), impliedBySdk23Permission);
667 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
668 addImpliedFeature(impliedFeatures, "android.hardware.location.network",
669 String8::format("requested %s permission", name.string())
670 .string(), impliedBySdk23Permission);
671 addImpliedFeature(impliedFeatures, "android.hardware.location",
672 String8::format("requested %s permission", name.string())
673 .string(), impliedBySdk23Permission);
674 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
675 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
676 addImpliedFeature(impliedFeatures, "android.hardware.location",
677 String8::format("requested %s permission", name.string())
678 .string(), impliedBySdk23Permission);
679 } else if (name == "android.permission.BLUETOOTH" ||
680 name == "android.permission.BLUETOOTH_ADMIN") {
681 if (targetSdk > 4) {
682 addImpliedFeature(impliedFeatures, "android.hardware.bluetooth",
683 String8::format("requested %s permission", name.string())
684 .string(), impliedBySdk23Permission);
685 addImpliedFeature(impliedFeatures, "android.hardware.bluetooth",
686 "targetSdkVersion > 4", impliedBySdk23Permission);
687 }
688 } else if (name == "android.permission.RECORD_AUDIO") {
689 addImpliedFeature(impliedFeatures, "android.hardware.microphone",
690 String8::format("requested %s permission", name.string())
691 .string(), impliedBySdk23Permission);
692 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
693 name == "android.permission.CHANGE_WIFI_STATE" ||
694 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
695 addImpliedFeature(impliedFeatures, "android.hardware.wifi",
696 String8::format("requested %s permission", name.string())
697 .string(), impliedBySdk23Permission);
698 } else if (name == "android.permission.CALL_PHONE" ||
699 name == "android.permission.CALL_PRIVILEGED" ||
700 name == "android.permission.MODIFY_PHONE_STATE" ||
701 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
702 name == "android.permission.READ_SMS" ||
703 name == "android.permission.RECEIVE_SMS" ||
704 name == "android.permission.RECEIVE_MMS" ||
705 name == "android.permission.RECEIVE_WAP_PUSH" ||
706 name == "android.permission.SEND_SMS" ||
707 name == "android.permission.WRITE_APN_SETTINGS" ||
708 name == "android.permission.WRITE_SMS") {
709 addImpliedFeature(impliedFeatures, "android.hardware.telephony",
710 String8("requested a telephony permission").string(),
711 impliedBySdk23Permission);
712 }
713}
714
Adam Lesinski282e1812014-01-23 18:17:42 -0800715/*
716 * Handle the "dump" command, to extract select data from an archive.
717 */
718extern char CONSOLE_DATA[2925]; // see EOF
719int doDump(Bundle* bundle)
720{
721 status_t result = UNKNOWN_ERROR;
Adam Lesinski282e1812014-01-23 18:17:42 -0800722
723 if (bundle->getFileSpecCount() < 1) {
724 fprintf(stderr, "ERROR: no dump option specified\n");
725 return 1;
726 }
727
728 if (bundle->getFileSpecCount() < 2) {
729 fprintf(stderr, "ERROR: no dump file specified\n");
730 return 1;
731 }
732
733 const char* option = bundle->getFileSpecEntry(0);
734 const char* filename = bundle->getFileSpecEntry(1);
735
736 AssetManager assets;
Narayan Kamathf85e41f2014-01-24 14:14:30 +0000737 int32_t assetsCookie;
Adam Lesinski282e1812014-01-23 18:17:42 -0800738 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
739 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
740 return 1;
741 }
742
743 // Make a dummy config for retrieving resources... we need to supply
744 // non-default values for some configs so that we can retrieve resources
745 // in the app that don't have a default. The most important of these is
746 // the API version because key resources like icons will have an implicit
747 // version if they are using newer config types like density.
748 ResTable_config config;
Narayan Kamath91447d82014-01-21 15:32:36 +0000749 memset(&config, 0, sizeof(ResTable_config));
Adam Lesinski282e1812014-01-23 18:17:42 -0800750 config.language[0] = 'e';
751 config.language[1] = 'n';
752 config.country[0] = 'U';
753 config.country[1] = 'S';
754 config.orientation = ResTable_config::ORIENTATION_PORT;
755 config.density = ResTable_config::DENSITY_MEDIUM;
756 config.sdkVersion = 10000; // Very high.
757 config.screenWidthDp = 320;
758 config.screenHeightDp = 480;
759 config.smallestScreenWidthDp = 320;
Adam Lesinskic2dea8d2014-08-04 16:40:41 -0700760 config.screenLayout |= ResTable_config::SCREENSIZE_NORMAL;
Adam Lesinski282e1812014-01-23 18:17:42 -0800761 assets.setConfiguration(config);
762
763 const ResTable& res = assets.getResources(false);
Dan Albert68001652014-09-09 09:51:01 -0700764 if (res.getError() != NO_ERROR) {
Adam Lesinski25e9d552014-05-19 15:01:43 -0700765 fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
Adam Lesinski63e646e2014-07-30 11:40:39 -0700766 return 1;
Adam Lesinski282e1812014-01-23 18:17:42 -0800767 }
768
Adam Lesinski694d0a72016-04-06 16:12:04 -0700769 // Source for AndroidManifest.xml
Adam Lesinski10de3af12016-07-13 10:14:03 -0700770 const String8 manifestFile("AndroidManifest.xml");
Adam Lesinski694d0a72016-04-06 16:12:04 -0700771
Adam Lesinski2cb761e2014-08-15 13:59:02 -0700772 // The dynamicRefTable can be null if there are no resources for this asset cookie.
773 // This fine.
Adam Lesinski63e646e2014-07-30 11:40:39 -0700774 const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700775
776 Asset* asset = NULL;
777
Adam Lesinski282e1812014-01-23 18:17:42 -0800778 if (strcmp("resources", option) == 0) {
Elliott Hughesba3fe562015-08-12 14:49:53 -0700779#ifndef __ANDROID__
Adam Lesinski282e1812014-01-23 18:17:42 -0800780 res.print(bundle->getValues());
781#endif
782
783 } else if (strcmp("strings", option) == 0) {
784 const ResStringPool* pool = res.getTableStringBlock(0);
785 printStringPool(pool);
786
787 } else if (strcmp("xmltree", option) == 0) {
788 if (bundle->getFileSpecCount() < 3) {
789 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
790 goto bail;
791 }
792
793 for (int i=2; i<bundle->getFileSpecCount(); i++) {
794 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700795 ResXMLTree tree(dynamicRefTable);
796 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800797 if (asset == NULL) {
798 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
799 goto bail;
800 }
801
802 if (tree.setTo(asset->getBuffer(true),
803 asset->getLength()) != NO_ERROR) {
804 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
805 goto bail;
806 }
807 tree.restart();
808 printXMLBlock(&tree);
809 tree.uninit();
810 delete asset;
811 asset = NULL;
812 }
813
814 } else if (strcmp("xmlstrings", option) == 0) {
815 if (bundle->getFileSpecCount() < 3) {
816 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
817 goto bail;
818 }
819
820 for (int i=2; i<bundle->getFileSpecCount(); i++) {
821 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700822 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800823 if (asset == NULL) {
824 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
825 goto bail;
826 }
827
Adam Lesinski63e646e2014-07-30 11:40:39 -0700828 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800829 if (tree.setTo(asset->getBuffer(true),
830 asset->getLength()) != NO_ERROR) {
831 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
832 goto bail;
833 }
834 printStringPool(&tree.getStrings());
835 delete asset;
836 asset = NULL;
837 }
838
839 } else {
Adam Lesinski63e646e2014-07-30 11:40:39 -0700840 asset = assets.openNonAsset(assetsCookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800841 if (asset == NULL) {
842 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
843 goto bail;
844 }
845
Adam Lesinski63e646e2014-07-30 11:40:39 -0700846 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800847 if (tree.setTo(asset->getBuffer(true),
848 asset->getLength()) != NO_ERROR) {
849 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
850 goto bail;
851 }
852 tree.restart();
853
854 if (strcmp("permissions", option) == 0) {
855 size_t len;
856 ResXMLTree::event_code_t code;
857 int depth = 0;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800858 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
859 code != ResXMLTree::BAD_DOCUMENT) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800860 if (code == ResXMLTree::END_TAG) {
861 depth--;
862 continue;
863 }
864 if (code != ResXMLTree::START_TAG) {
865 continue;
866 }
867 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700868 const char16_t* ctag16 = tree.getElementName(&len);
869 if (ctag16 == NULL) {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700870 SourcePos(manifestFile, tree.getLineNumber()).error(
871 "ERROR: failed to get XML element name (bad string pool)");
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700872 goto bail;
873 }
874 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800875 //printf("Depth %d tag %s\n", depth, tag.string());
876 if (depth == 1) {
877 if (tag != "manifest") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700878 SourcePos(manifestFile, tree.getLineNumber()).error(
879 "ERROR: manifest does not start with <manifest> tag");
Adam Lesinski282e1812014-01-23 18:17:42 -0800880 goto bail;
881 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700882 String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -0700883 printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800884 } else if (depth == 2) {
885 if (tag == "permission") {
886 String8 error;
887 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
888 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700889 SourcePos(manifestFile, tree.getLineNumber()).error(
890 "ERROR getting 'android:name': %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800891 goto bail;
892 }
893
894 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700895 SourcePos(manifestFile, tree.getLineNumber()).error(
896 "ERROR: missing 'android:name' for permission");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800897 goto bail;
898 }
899 printf("permission: %s\n",
900 ResTable::normalizeForOutput(name.string()).string());
901 } else if (tag == "uses-permission") {
902 String8 error;
903 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
904 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700905 SourcePos(manifestFile, tree.getLineNumber()).error(
906 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800907 goto bail;
908 }
909
910 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700911 SourcePos(manifestFile, tree.getLineNumber()).error(
912 "ERROR: missing 'android:name' for uses-permission");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800913 goto bail;
914 }
915 printUsesPermission(name,
916 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
917 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
918 } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
919 String8 error;
920 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
921 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700922 SourcePos(manifestFile, tree.getLineNumber()).error(
923 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800924 goto bail;
925 }
926
927 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700928 SourcePos(manifestFile, tree.getLineNumber()).error(
929 "ERROR: missing 'android:name' for uses-permission-sdk-23");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800930 goto bail;
931 }
932 printUsesPermissionSdk23(
933 name,
934 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
Adam Lesinski282e1812014-01-23 18:17:42 -0800935 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800936 }
937 }
938 } else if (strcmp("badging", option) == 0) {
939 Vector<String8> locales;
940 res.getLocales(&locales);
941
942 Vector<ResTable_config> configs;
943 res.getConfigurations(&configs);
944 SortedVector<int> densities;
945 const size_t NC = configs.size();
946 for (size_t i=0; i<NC; i++) {
947 int dens = configs[i].density;
948 if (dens == 0) {
949 dens = 160;
950 }
951 densities.add(dens);
952 }
953
954 size_t len;
955 ResXMLTree::event_code_t code;
956 int depth = 0;
957 String8 error;
958 bool withinActivity = false;
959 bool isMainActivity = false;
960 bool isLauncherActivity = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800961 bool isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800962 bool isSearchable = false;
963 bool withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700964 bool withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700965 bool withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800966 bool withinReceiver = false;
967 bool withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700968 bool withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800969 bool withinIntentFilter = false;
970 bool hasMainActivity = false;
971 bool hasOtherActivities = false;
972 bool hasOtherReceivers = false;
973 bool hasOtherServices = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700974 bool hasIntentFilter = false;
975
Adam Lesinski282e1812014-01-23 18:17:42 -0800976 bool hasWallpaperService = false;
977 bool hasImeService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700978 bool hasAccessibilityService = false;
979 bool hasPrintService = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800980 bool hasWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700981 bool hasDeviceAdminReceiver = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700982 bool hasPaymentService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700983 bool hasDocumentsProvider = false;
984 bool hasCameraActivity = false;
985 bool hasCameraSecureActivity = false;
986 bool hasLauncher = false;
987 bool hasNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400988 bool hasDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700989
Adam Lesinski282e1812014-01-23 18:17:42 -0800990 bool actMainActivity = false;
991 bool actWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700992 bool actDeviceAdminEnabled = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800993 bool actImeService = false;
994 bool actWallpaperService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700995 bool actAccessibilityService = false;
996 bool actPrintService = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700997 bool actHostApduService = false;
998 bool actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700999 bool actDocumentsProvider = false;
1000 bool actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001001 bool actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001002 bool actCamera = false;
1003 bool actCameraSecure = false;
1004 bool catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001005 bool hasMetaHostPaymentCategory = false;
1006 bool hasMetaOffHostPaymentCategory = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001007
1008 // These permissions are required by services implementing services
1009 // the system binds to (IME, Accessibility, PrintServices, etc.)
1010 bool hasBindDeviceAdminPermission = false;
1011 bool hasBindInputMethodPermission = false;
1012 bool hasBindAccessibilityServicePermission = false;
1013 bool hasBindPrintServicePermission = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001014 bool hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001015 bool hasRequiredSafAttributes = false;
1016 bool hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001017 bool hasBindDreamServicePermission = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001018
1019 // These two implement the implicit permissions that are granted
1020 // to pre-1.6 applications.
1021 bool hasWriteExternalStoragePermission = false;
1022 bool hasReadPhoneStatePermission = false;
1023
1024 // If an app requests write storage, they will also get read storage.
1025 bool hasReadExternalStoragePermission = false;
1026
1027 // Implement transition to read and write call log.
1028 bool hasReadContactsPermission = false;
1029 bool hasWriteContactsPermission = false;
1030 bool hasReadCallLogPermission = false;
1031 bool hasWriteCallLogPermission = false;
1032
Adam Lesinskie47fd122014-08-15 22:25:36 -07001033 // If an app declares itself as multiArch, we report the
1034 // native libraries differently.
1035 bool hasMultiArch = false;
1036
Adam Lesinski282e1812014-01-23 18:17:42 -08001037 // This next group of variables is used to implement a group of
1038 // backward-compatibility heuristics necessitated by the addition of
1039 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
1040 // heuristic is "if an app requests a permission but doesn't explicitly
1041 // request the corresponding <uses-feature>, presume it's there anyway".
Adam Lesinski2c72b682014-06-24 09:56:01 -07001042
Adam Lesinski282e1812014-01-23 18:17:42 -08001043 // 2.2 also added some other features that apps can request, but that
1044 // have no corresponding permission, so we cannot implement any
1045 // back-compatibility heuristic for them. The below are thus unnecessary
1046 // (but are retained here for documentary purposes.)
1047 //bool specCompassFeature = false;
1048 //bool specAccelerometerFeature = false;
1049 //bool specProximityFeature = false;
1050 //bool specAmbientLightFeature = false;
1051 //bool specLiveWallpaperFeature = false;
1052
1053 int targetSdk = 0;
1054 int smallScreen = 1;
1055 int normalScreen = 1;
1056 int largeScreen = 1;
1057 int xlargeScreen = 1;
1058 int anyDensity = 1;
1059 int requiresSmallestWidthDp = 0;
1060 int compatibleWidthLimitDp = 0;
1061 int largestWidthLimitDp = 0;
1062 String8 pkg;
1063 String8 activityName;
1064 String8 activityLabel;
1065 String8 activityIcon;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001066 String8 activityBanner;
Adam Lesinski282e1812014-01-23 18:17:42 -08001067 String8 receiverName;
1068 String8 serviceName;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001069 Vector<String8> supportedInput;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001070
1071 FeatureGroup commonFeatures;
1072 Vector<FeatureGroup> featureGroups;
1073 KeyedVector<String8, ImpliedFeature> impliedFeatures;
1074
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001075 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
1076 code != ResXMLTree::BAD_DOCUMENT) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001077 if (code == ResXMLTree::END_TAG) {
1078 depth--;
1079 if (depth < 2) {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001080 if (withinSupportsInput && !supportedInput.isEmpty()) {
1081 printf("supports-input: '");
1082 const size_t N = supportedInput.size();
1083 for (size_t i=0; i<N; i++) {
Maurice Chu2675f762013-10-22 17:33:11 -07001084 printf("%s", ResTable::normalizeForOutput(
1085 supportedInput[i].string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001086 if (i != N - 1) {
1087 printf("' '");
1088 } else {
1089 printf("'\n");
1090 }
1091 }
1092 supportedInput.clear();
1093 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001094 withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001095 withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001096 withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001097 } else if (depth < 3) {
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001098 if (withinActivity && isMainActivity) {
Maurice Chu2675f762013-10-22 17:33:11 -07001099 String8 aName(getComponentName(pkg, activityName));
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001100 if (isLauncherActivity) {
1101 printf("launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001102 if (aName.length() > 0) {
1103 printf(" name='%s' ",
1104 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001105 }
1106 printf(" label='%s' icon='%s'\n",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001107 ResTable::normalizeForOutput(activityLabel.string())
1108 .string(),
1109 ResTable::normalizeForOutput(activityIcon.string())
1110 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001111 }
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001112 if (isLeanbackLauncherActivity) {
1113 printf("leanback-launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001114 if (aName.length() > 0) {
1115 printf(" name='%s' ",
1116 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001117 }
1118 printf(" label='%s' icon='%s' banner='%s'\n",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001119 ResTable::normalizeForOutput(activityLabel.string())
1120 .string(),
1121 ResTable::normalizeForOutput(activityIcon.string())
1122 .string(),
1123 ResTable::normalizeForOutput(activityBanner.string())
1124 .string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001125 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001126 }
1127 if (!hasIntentFilter) {
1128 hasOtherActivities |= withinActivity;
1129 hasOtherReceivers |= withinReceiver;
1130 hasOtherServices |= withinService;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001131 } else {
1132 if (withinService) {
1133 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
1134 hasBindNfcServicePermission);
1135 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
1136 hasBindNfcServicePermission);
1137 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001138 }
1139 withinActivity = false;
1140 withinService = false;
1141 withinReceiver = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001142 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001143 hasIntentFilter = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001144 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001145 } else if (depth < 4) {
1146 if (withinIntentFilter) {
1147 if (withinActivity) {
1148 hasMainActivity |= actMainActivity;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001149 hasLauncher |= catLauncher;
1150 hasCameraActivity |= actCamera;
1151 hasCameraSecureActivity |= actCameraSecure;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001152 hasOtherActivities |=
1153 !actMainActivity && !actCamera && !actCameraSecure;
Adam Lesinski282e1812014-01-23 18:17:42 -08001154 } else if (withinReceiver) {
1155 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001156 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
1157 hasBindDeviceAdminPermission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001158 hasOtherReceivers |=
1159 (!actWidgetReceivers && !actDeviceAdminEnabled);
Adam Lesinski282e1812014-01-23 18:17:42 -08001160 } else if (withinService) {
1161 hasImeService |= actImeService;
1162 hasWallpaperService |= actWallpaperService;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001163 hasAccessibilityService |= (actAccessibilityService &&
1164 hasBindAccessibilityServicePermission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001165 hasPrintService |=
1166 (actPrintService && hasBindPrintServicePermission);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001167 hasNotificationListenerService |= actNotificationListenerService &&
1168 hasBindNotificationListenerServicePermission;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001169 hasDreamService |= actDreamService && hasBindDreamServicePermission;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001170 hasOtherServices |= (!actImeService && !actWallpaperService &&
Adam Lesinski94fc9122013-09-30 17:16:09 -07001171 !actAccessibilityService && !actPrintService &&
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001172 !actHostApduService && !actOffHostApduService &&
1173 !actNotificationListenerService);
1174 } else if (withinProvider) {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001175 hasDocumentsProvider |=
1176 actDocumentsProvider && hasRequiredSafAttributes;
Adam Lesinski282e1812014-01-23 18:17:42 -08001177 }
1178 }
1179 withinIntentFilter = false;
1180 }
1181 continue;
1182 }
1183 if (code != ResXMLTree::START_TAG) {
1184 continue;
1185 }
1186 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001187
1188 const char16_t* ctag16 = tree.getElementName(&len);
1189 if (ctag16 == NULL) {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001190 SourcePos(manifestFile, tree.getLineNumber()).error(
1191 "ERROR: failed to get XML element name (bad string pool)");
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001192 goto bail;
1193 }
1194 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -08001195 //printf("Depth %d, %s\n", depth, tag.string());
1196 if (depth == 1) {
1197 if (tag != "manifest") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001198 SourcePos(manifestFile, tree.getLineNumber()).error(
1199 "ERROR: manifest does not start with <manifest> tag");
Adam Lesinski282e1812014-01-23 18:17:42 -08001200 goto bail;
1201 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001202 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -07001203 printf("package: name='%s' ",
1204 ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001205 int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR,
1206 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001207 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001208 SourcePos(manifestFile, tree.getLineNumber()).error(
1209 "ERROR getting 'android:versionCode' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001210 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001211 goto bail;
1212 }
1213 if (versionCode > 0) {
1214 printf("versionCode='%d' ", versionCode);
1215 } else {
1216 printf("versionCode='' ");
1217 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001218 String8 versionName = AaptXml::getResolvedAttribute(res, tree,
1219 VERSION_NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001220 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001221 SourcePos(manifestFile, tree.getLineNumber()).error(
1222 "ERROR getting 'android:versionName' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001223 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001224 goto bail;
1225 }
Adam Lesinski25d35a92014-08-11 09:41:56 -07001226 printf("versionName='%s'",
Maurice Chu2675f762013-10-22 17:33:11 -07001227 ResTable::normalizeForOutput(versionName.string()).string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001228
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001229 String8 splitName = AaptXml::getAttribute(tree, NULL, "split");
Adam Lesinski25d35a92014-08-11 09:41:56 -07001230 if (!splitName.isEmpty()) {
1231 printf(" split='%s'", ResTable::normalizeForOutput(
1232 splitName.string()).string());
1233 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001234
Adam Lesinski5283fab2014-08-29 11:23:55 -07001235 String8 platformVersionName = AaptXml::getAttribute(tree, NULL,
1236 "platformBuildVersionName");
1237 printf(" platformBuildVersionName='%s'", platformVersionName.string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001238 printf("\n");
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001239
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001240 int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
1241 INSTALL_LOCATION_ATTR, &error);
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001242 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001243 SourcePos(manifestFile, tree.getLineNumber()).error(
1244 "ERROR getting 'android:installLocation' attribute: %s",
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001245 error.string());
1246 goto bail;
1247 }
1248
1249 if (installLocation >= 0) {
1250 printf("install-location:'");
1251 switch (installLocation) {
1252 case 0:
1253 printf("auto");
1254 break;
1255 case 1:
1256 printf("internalOnly");
1257 break;
1258 case 2:
1259 printf("preferExternal");
1260 break;
1261 default:
1262 fprintf(stderr, "Invalid installLocation %d\n", installLocation);
1263 goto bail;
1264 }
1265 printf("'\n");
1266 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001267 } else if (depth == 2) {
1268 withinApplication = false;
1269 if (tag == "application") {
1270 withinApplication = true;
1271
1272 String8 label;
1273 const size_t NL = locales.size();
1274 for (size_t i=0; i<NL; i++) {
1275 const char* localeStr = locales[i].string();
1276 assets.setLocale(localeStr != NULL ? localeStr : "");
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001277 String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1278 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001279 if (llabel != "") {
1280 if (localeStr == NULL || strlen(localeStr) == 0) {
1281 label = llabel;
Maurice Chu2675f762013-10-22 17:33:11 -07001282 printf("application-label:'%s'\n",
1283 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001284 } else {
1285 if (label == "") {
1286 label = llabel;
1287 }
1288 printf("application-label-%s:'%s'\n", localeStr,
Maurice Chu2675f762013-10-22 17:33:11 -07001289 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001290 }
1291 }
1292 }
1293
1294 ResTable_config tmpConfig = config;
1295 const size_t ND = densities.size();
1296 for (size_t i=0; i<ND; i++) {
1297 tmpConfig.density = densities[i];
1298 assets.setConfiguration(tmpConfig);
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001299 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1300 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001301 if (icon != "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001302 printf("application-icon-%d:'%s'\n", densities[i],
1303 ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001304 }
1305 }
1306 assets.setConfiguration(config);
1307
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001308 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001309 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001310 SourcePos(manifestFile, tree.getLineNumber()).error(
1311 "ERROR getting 'android:icon' attribute: %s", error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001312 goto bail;
1313 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001314 int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0,
1315 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001316 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001317 SourcePos(manifestFile, tree.getLineNumber()).error(
1318 "ERROR getting 'android:testOnly' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001319 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001320 goto bail;
1321 }
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001322
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001323 String8 banner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1324 &error);
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001325 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001326 SourcePos(manifestFile, tree.getLineNumber()).error(
1327 "ERROR getting 'android:banner' attribute: %s", error.string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001328 goto bail;
1329 }
Maurice Chu2675f762013-10-22 17:33:11 -07001330 printf("application: label='%s' ",
1331 ResTable::normalizeForOutput(label.string()).string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001332 printf("icon='%s'", ResTable::normalizeForOutput(icon.string()).string());
1333 if (banner != "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001334 printf(" banner='%s'",
1335 ResTable::normalizeForOutput(banner.string()).string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001336 }
1337 printf("\n");
Adam Lesinski282e1812014-01-23 18:17:42 -08001338 if (testOnly != 0) {
1339 printf("testOnly='%d'\n", testOnly);
1340 }
1341
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001342 int32_t isGame = AaptXml::getResolvedIntegerAttribute(res, tree,
1343 ISGAME_ATTR, 0, &error);
1344 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001345 SourcePos(manifestFile, tree.getLineNumber()).error(
1346 "ERROR getting 'android:isGame' attribute: %s", error.string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001347 goto bail;
1348 }
1349 if (isGame != 0) {
1350 printf("application-isGame\n");
1351 }
1352
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001353 int32_t debuggable = AaptXml::getResolvedIntegerAttribute(res, tree,
1354 DEBUGGABLE_ATTR, 0, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001355 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001356 SourcePos(manifestFile, tree.getLineNumber()).error(
1357 "ERROR getting 'android:debuggable' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001358 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001359 goto bail;
1360 }
1361 if (debuggable != 0) {
1362 printf("application-debuggable\n");
1363 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07001364
1365 // We must search by name because the multiArch flag hasn't been API
1366 // frozen yet.
1367 int32_t multiArchIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
1368 "multiArch");
1369 if (multiArchIndex >= 0) {
1370 Res_value value;
1371 if (tree.getAttributeValue(multiArchIndex, &value) != NO_ERROR) {
1372 if (value.dataType >= Res_value::TYPE_FIRST_INT &&
1373 value.dataType <= Res_value::TYPE_LAST_INT) {
1374 hasMultiArch = value.data;
1375 }
1376 }
1377 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001378 } else if (tag == "uses-sdk") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001379 int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR,
1380 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001381 if (error != "") {
1382 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001383 String8 name = AaptXml::getResolvedAttribute(res, tree,
1384 MIN_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001385 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001386 SourcePos(manifestFile, tree.getLineNumber()).error(
1387 "ERROR getting 'android:minSdkVersion' attribute: %s",
Adam Lesinski282e1812014-01-23 18:17:42 -08001388 error.string());
1389 goto bail;
1390 }
1391 if (name == "Donut") targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001392 printf("sdkVersion:'%s'\n",
1393 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001394 } else if (code != -1) {
1395 targetSdk = code;
1396 printf("sdkVersion:'%d'\n", code);
1397 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001398 code = AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -08001399 if (code != -1) {
1400 printf("maxSdkVersion:'%d'\n", code);
1401 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001402 code = AaptXml::getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001403 if (error != "") {
1404 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001405 String8 name = AaptXml::getResolvedAttribute(res, tree,
1406 TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001407 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001408 SourcePos(manifestFile, tree.getLineNumber()).error(
1409 "ERROR getting 'android:targetSdkVersion' attribute: %s",
Adam Lesinski282e1812014-01-23 18:17:42 -08001410 error.string());
1411 goto bail;
1412 }
1413 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001414 printf("targetSdkVersion:'%s'\n",
1415 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001416 } else if (code != -1) {
1417 if (targetSdk < code) {
1418 targetSdk = code;
1419 }
1420 printf("targetSdkVersion:'%d'\n", code);
1421 }
1422 } else if (tag == "uses-configuration") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001423 int32_t reqTouchScreen = AaptXml::getIntegerAttribute(tree,
1424 REQ_TOUCH_SCREEN_ATTR, 0);
1425 int32_t reqKeyboardType = AaptXml::getIntegerAttribute(tree,
1426 REQ_KEYBOARD_TYPE_ATTR, 0);
1427 int32_t reqHardKeyboard = AaptXml::getIntegerAttribute(tree,
1428 REQ_HARD_KEYBOARD_ATTR, 0);
1429 int32_t reqNavigation = AaptXml::getIntegerAttribute(tree,
1430 REQ_NAVIGATION_ATTR, 0);
1431 int32_t reqFiveWayNav = AaptXml::getIntegerAttribute(tree,
1432 REQ_FIVE_WAY_NAV_ATTR, 0);
Adam Lesinski282e1812014-01-23 18:17:42 -08001433 printf("uses-configuration:");
1434 if (reqTouchScreen != 0) {
1435 printf(" reqTouchScreen='%d'", reqTouchScreen);
1436 }
1437 if (reqKeyboardType != 0) {
1438 printf(" reqKeyboardType='%d'", reqKeyboardType);
1439 }
1440 if (reqHardKeyboard != 0) {
1441 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1442 }
1443 if (reqNavigation != 0) {
1444 printf(" reqNavigation='%d'", reqNavigation);
1445 }
1446 if (reqFiveWayNav != 0) {
1447 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1448 }
1449 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001450 } else if (tag == "supports-input") {
1451 withinSupportsInput = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001452 } else if (tag == "supports-screens") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001453 smallScreen = AaptXml::getIntegerAttribute(tree,
1454 SMALL_SCREEN_ATTR, 1);
1455 normalScreen = AaptXml::getIntegerAttribute(tree,
1456 NORMAL_SCREEN_ATTR, 1);
1457 largeScreen = AaptXml::getIntegerAttribute(tree,
1458 LARGE_SCREEN_ATTR, 1);
1459 xlargeScreen = AaptXml::getIntegerAttribute(tree,
1460 XLARGE_SCREEN_ATTR, 1);
1461 anyDensity = AaptXml::getIntegerAttribute(tree,
1462 ANY_DENSITY_ATTR, 1);
1463 requiresSmallestWidthDp = AaptXml::getIntegerAttribute(tree,
1464 REQUIRES_SMALLEST_WIDTH_DP_ATTR, 0);
1465 compatibleWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1466 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, 0);
1467 largestWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1468 LARGEST_WIDTH_LIMIT_DP_ATTR, 0);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001469 } else if (tag == "feature-group") {
1470 withinFeatureGroup = true;
1471 FeatureGroup group;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001472 group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001473 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001474 SourcePos(manifestFile, tree.getLineNumber()).error(
1475 "ERROR getting 'android:label' attribute: %s", error.string());
Adam Lesinski2c72b682014-06-24 09:56:01 -07001476 goto bail;
1477 }
1478 featureGroups.add(group);
1479
Adam Lesinski282e1812014-01-23 18:17:42 -08001480 } else if (tag == "uses-feature") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001481 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001482 if (name != "" && error == "") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001483 const char* androidSchema =
1484 "http://schemas.android.com/apk/res/android";
Adam Lesinski282e1812014-01-23 18:17:42 -08001485
Adam Lesinski694d0a72016-04-06 16:12:04 -07001486 int32_t req = AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1,
1487 &error);
1488 if (error != "") {
1489 SourcePos(manifestFile, tree.getLineNumber()).error(
1490 "failed to read attribute 'android:required': %s",
1491 error.string());
1492 goto bail;
1493 }
1494
1495 int32_t version = AaptXml::getIntegerAttribute(tree, androidSchema,
1496 "version", 0, &error);
1497 if (error != "") {
1498 SourcePos(manifestFile, tree.getLineNumber()).error(
1499 "failed to read attribute 'android:version': %s",
1500 error.string());
1501 goto bail;
1502 }
1503
1504 commonFeatures.features.add(name, Feature(req != 0, version));
Adam Lesinski2c72b682014-06-24 09:56:01 -07001505 if (req) {
1506 addParentFeatures(&commonFeatures, name);
Adam Lesinski282e1812014-01-23 18:17:42 -08001507 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001508 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001509 int vers = AaptXml::getIntegerAttribute(tree,
Adam Lesinski282e1812014-01-23 18:17:42 -08001510 GL_ES_VERSION_ATTR, &error);
1511 if (error == "") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001512 if (vers > commonFeatures.openGLESVersion) {
1513 commonFeatures.openGLESVersion = vers;
1514 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001515 }
1516 }
1517 } else if (tag == "uses-permission") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001518 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001519 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001520 SourcePos(manifestFile, tree.getLineNumber()).error(
1521 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001522 goto bail;
1523 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001524
1525 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001526 SourcePos(manifestFile, tree.getLineNumber()).error(
1527 "ERROR: missing 'android:name' for uses-permission");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001528 goto bail;
1529 }
1530
1531 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, false);
1532
1533 if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1534 hasWriteExternalStoragePermission = true;
1535 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1536 hasReadExternalStoragePermission = true;
1537 } else if (name == "android.permission.READ_PHONE_STATE") {
1538 hasReadPhoneStatePermission = true;
1539 } else if (name == "android.permission.READ_CONTACTS") {
1540 hasReadContactsPermission = true;
1541 } else if (name == "android.permission.WRITE_CONTACTS") {
1542 hasWriteContactsPermission = true;
1543 } else if (name == "android.permission.READ_CALL_LOG") {
1544 hasReadCallLogPermission = true;
1545 } else if (name == "android.permission.WRITE_CALL_LOG") {
1546 hasWriteCallLogPermission = true;
1547 }
1548
1549 printUsesPermission(name,
1550 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
1551 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
1552
1553 } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
1554 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1555 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001556 SourcePos(manifestFile, tree.getLineNumber()).error(
1557 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001558 goto bail;
1559 }
1560
1561 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001562 SourcePos(manifestFile, tree.getLineNumber()).error(
1563 "ERROR: missing 'android:name' for uses-permission-sdk-23");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001564 goto bail;
1565 }
1566
1567 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, true);
1568
1569 printUsesPermissionSdk23(
1570 name, AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
1571
Adam Lesinski282e1812014-01-23 18:17:42 -08001572 } else if (tag == "uses-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001573 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001574 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001575 printf("uses-package:'%s'\n",
1576 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001577 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001578 SourcePos(manifestFile, tree.getLineNumber()).error(
1579 "ERROR getting 'android:name' attribute: %s", error.string());
1580 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001581 }
1582 } else if (tag == "original-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001583 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001584 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001585 printf("original-package:'%s'\n",
1586 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001587 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001588 SourcePos(manifestFile, tree.getLineNumber()).error(
1589 "ERROR getting 'android:name' attribute: %s", error.string());
1590 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001591 }
1592 } else if (tag == "supports-gl-texture") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001593 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001594 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001595 printf("supports-gl-texture:'%s'\n",
1596 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001597 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001598 SourcePos(manifestFile, tree.getLineNumber()).error(
1599 "ERROR getting 'android:name' attribute: %s", error.string());
1600 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001601 }
1602 } else if (tag == "compatible-screens") {
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001603 printCompatibleScreens(tree, &error);
1604 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001605 SourcePos(manifestFile, tree.getLineNumber()).error(
1606 "ERROR getting compatible screens: %s", error.string());
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001607 goto bail;
1608 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001609 depth--;
1610 } else if (tag == "package-verifier") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001611 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001612 if (name != "" && error == "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001613 String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR,
1614 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001615 if (publicKey != "" && error == "") {
1616 printf("package-verifier: name='%s' publicKey='%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001617 ResTable::normalizeForOutput(name.string()).string(),
1618 ResTable::normalizeForOutput(publicKey.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001619 }
1620 }
1621 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001622 } else if (depth == 3) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001623 withinActivity = false;
1624 withinReceiver = false;
1625 withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001626 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001627 hasIntentFilter = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001628 hasMetaHostPaymentCategory = false;
1629 hasMetaOffHostPaymentCategory = false;
1630 hasBindDeviceAdminPermission = false;
1631 hasBindInputMethodPermission = false;
1632 hasBindAccessibilityServicePermission = false;
1633 hasBindPrintServicePermission = false;
1634 hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001635 hasRequiredSafAttributes = false;
1636 hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001637 hasBindDreamServicePermission = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001638 if (withinApplication) {
1639 if(tag == "activity") {
1640 withinActivity = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001641 activityName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001642 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001643 SourcePos(manifestFile, tree.getLineNumber()).error(
1644 "ERROR getting 'android:name' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001645 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001646 goto bail;
1647 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001648
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001649 activityLabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1650 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001651 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001652 SourcePos(manifestFile, tree.getLineNumber()).error(
1653 "ERROR getting 'android:label' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001654 error.string());
1655 goto bail;
1656 }
1657
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001658 activityIcon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1659 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001660 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001661 SourcePos(manifestFile, tree.getLineNumber()).error(
1662 "ERROR getting 'android:icon' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001663 error.string());
1664 goto bail;
1665 }
1666
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001667 activityBanner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1668 &error);
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001669 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001670 SourcePos(manifestFile, tree.getLineNumber()).error(
1671 "ERROR getting 'android:banner' attribute: %s",
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001672 error.string());
1673 goto bail;
1674 }
1675
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001676 int32_t orien = AaptXml::getResolvedIntegerAttribute(res, tree,
Michael Wrightec4fdec2013-09-06 16:50:52 -07001677 SCREEN_ORIENTATION_ATTR, &error);
1678 if (error == "") {
1679 if (orien == 0 || orien == 6 || orien == 8) {
1680 // Requests landscape, sensorLandscape, or reverseLandscape.
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001681 addImpliedFeature(&impliedFeatures,
1682 "android.hardware.screen.landscape",
1683 "one or more activities have specified a "
1684 "landscape orientation",
1685 false);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001686 } else if (orien == 1 || orien == 7 || orien == 9) {
1687 // Requests portrait, sensorPortrait, or reversePortrait.
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001688 addImpliedFeature(&impliedFeatures,
1689 "android.hardware.screen.portrait",
1690 "one or more activities have specified a "
1691 "portrait orientation",
1692 false);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001693 }
1694 }
1695 } else if (tag == "uses-library") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001696 String8 libraryName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001697 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001698 SourcePos(manifestFile, tree.getLineNumber()).error(
Michael Wrightec4fdec2013-09-06 16:50:52 -07001699 "ERROR getting 'android:name' attribute for uses-library"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001700 " %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001701 goto bail;
1702 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001703 int req = AaptXml::getIntegerAttribute(tree,
1704 REQUIRED_ATTR, 1);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001705 printf("uses-library%s:'%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001706 req ? "" : "-not-required", ResTable::normalizeForOutput(
1707 libraryName.string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001708 } else if (tag == "receiver") {
1709 withinReceiver = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001710 receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001711
1712 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001713 SourcePos(manifestFile, tree.getLineNumber()).error(
Michael Wrightec4fdec2013-09-06 16:50:52 -07001714 "ERROR getting 'android:name' attribute for receiver:"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001715 " %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001716 goto bail;
1717 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001718
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001719 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1720 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001721 if (error == "") {
1722 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1723 hasBindDeviceAdminPermission = true;
1724 }
1725 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001726 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001727 "ERROR getting 'android:permission' attribute for"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001728 " receiver '%s': %s",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001729 receiverName.string(), error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001730 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001731 } else if (tag == "service") {
1732 withinService = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001733 serviceName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001734
1735 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001736 SourcePos(manifestFile, tree.getLineNumber()).error(
1737 "ERROR getting 'android:name' attribute for "
1738 "service:%s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001739 goto bail;
1740 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001741
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001742 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1743 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001744 if (error == "") {
1745 if (permission == "android.permission.BIND_INPUT_METHOD") {
1746 hasBindInputMethodPermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001747 } else if (permission ==
1748 "android.permission.BIND_ACCESSIBILITY_SERVICE") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001749 hasBindAccessibilityServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001750 } else if (permission ==
1751 "android.permission.BIND_PRINT_SERVICE") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001752 hasBindPrintServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001753 } else if (permission ==
1754 "android.permission.BIND_NFC_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07001755 hasBindNfcServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001756 } else if (permission ==
1757 "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001758 hasBindNotificationListenerServicePermission = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001759 } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
1760 hasBindDreamServicePermission = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001761 }
1762 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001763 SourcePos(manifestFile, tree.getLineNumber()).error(
1764 "ERROR getting 'android:permission' attribute for "
1765 "service '%s': %s", serviceName.string(), error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001766 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001767 } else if (tag == "provider") {
1768 withinProvider = true;
1769
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001770 bool exported = AaptXml::getResolvedIntegerAttribute(res, tree,
1771 EXPORTED_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001772 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001773 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001774 "ERROR getting 'android:exported' attribute for provider:"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001775 " %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001776 goto bail;
1777 }
1778
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001779 bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute(
1780 res, tree, GRANT_URI_PERMISSIONS_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001781 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001782 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001783 "ERROR getting 'android:grantUriPermissions' attribute for "
Adam Lesinski10de3af12016-07-13 10:14:03 -07001784 "provider: %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001785 goto bail;
1786 }
1787
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001788 String8 permission = AaptXml::getResolvedAttribute(res, tree,
1789 PERMISSION_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001790 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001791 SourcePos(manifestFile, tree.getLineNumber()).error(
1792 "ERROR getting 'android:permission' attribute for "
1793 "provider: %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001794 goto bail;
1795 }
1796
1797 hasRequiredSafAttributes |= exported && grantUriPermissions &&
1798 permission == "android.permission.MANAGE_DOCUMENTS";
1799
Michael Wrightec4fdec2013-09-06 16:50:52 -07001800 } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001801 String8 metaDataName = AaptXml::getResolvedAttribute(res, tree,
1802 NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001803 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001804 SourcePos(manifestFile, tree.getLineNumber()).error(
1805 "ERROR getting 'android:name' attribute for "
1806 "meta-data: %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001807 goto bail;
1808 }
Maurice Chu2675f762013-10-22 17:33:11 -07001809 printf("meta-data: name='%s' ",
1810 ResTable::normalizeForOutput(metaDataName.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001811 printResolvedResourceAttribute(res, tree, VALUE_ATTR, String8("value"),
Maurice Chu76327312013-10-16 18:28:46 -07001812 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001813 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001814 // Try looking for a RESOURCE_ATTR
1815 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001816 printResolvedResourceAttribute(res, tree, RESOURCE_ATTR,
Maurice Chu76327312013-10-16 18:28:46 -07001817 String8("resource"), &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001818 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001819 SourcePos(manifestFile, tree.getLineNumber()).error(
1820 "ERROR getting 'android:value' or "
Maurice Chu76327312013-10-16 18:28:46 -07001821 "'android:resource' attribute for "
Adam Lesinski10de3af12016-07-13 10:14:03 -07001822 "meta-data: %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001823 goto bail;
1824 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001825 }
Maurice Chu76327312013-10-16 18:28:46 -07001826 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001827 } else if (withinSupportsInput && tag == "input-type") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001828 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001829 if (name != "" && error == "") {
1830 supportedInput.add(name);
1831 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001832 SourcePos(manifestFile, tree.getLineNumber()).error(
1833 "ERROR getting 'android:name' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001834 error.string());
1835 goto bail;
1836 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001837 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001838 } else if (withinFeatureGroup && tag == "uses-feature") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001839 const String8 androidSchema("http://schemas.android.com/apk/res/android");
Adam Lesinski2c72b682014-06-24 09:56:01 -07001840 FeatureGroup& top = featureGroups.editTop();
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001841
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001842 String8 name = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001843 if (name != "" && error == "") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001844 Feature feature(true);
1845
1846 int32_t featureVers = AaptXml::getIntegerAttribute(
1847 tree, androidSchema.string(), "version", 0, &error);
1848 if (error == "") {
1849 feature.version = featureVers;
1850 } else {
1851 SourcePos(manifestFile, tree.getLineNumber()).error(
1852 "failed to read attribute 'android:version': %s",
1853 error.string());
1854 goto bail;
1855 }
1856
1857 top.features.add(name, feature);
Adam Lesinskid3edfde2014-08-08 17:32:44 -07001858 addParentFeatures(&top, name);
Adam Lesinski694d0a72016-04-06 16:12:04 -07001859
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001860 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001861 int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR,
1862 &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001863 if (error == "") {
1864 if (vers > top.openGLESVersion) {
1865 top.openGLESVersion = vers;
1866 }
1867 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001868 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001869 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001870 } else if (depth == 4) {
1871 if (tag == "intent-filter") {
1872 hasIntentFilter = true;
1873 withinIntentFilter = true;
1874 actMainActivity = false;
1875 actWidgetReceivers = false;
1876 actImeService = false;
1877 actWallpaperService = false;
1878 actAccessibilityService = false;
1879 actPrintService = false;
1880 actDeviceAdminEnabled = false;
1881 actHostApduService = false;
1882 actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001883 actDocumentsProvider = false;
1884 actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001885 actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001886 actCamera = false;
1887 actCameraSecure = false;
1888 catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001889 } else if (withinService && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001890 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001891 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001892 SourcePos(manifestFile, tree.getLineNumber()).error(
1893 "ERROR getting 'android:name' attribute for "
1894 "meta-data tag in service '%s': %s", serviceName.string(),
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001895 error.string());
Adam Lesinski94fc9122013-09-30 17:16:09 -07001896 goto bail;
1897 }
1898
1899 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1900 name == "android.nfc.cardemulation.off_host_apdu_service") {
1901 bool offHost = true;
1902 if (name == "android.nfc.cardemulation.host_apdu_service") {
1903 offHost = false;
1904 }
1905
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001906 String8 xmlPath = AaptXml::getResolvedAttribute(res, tree,
1907 RESOURCE_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001908 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001909 SourcePos(manifestFile, tree.getLineNumber()).error(
1910 "ERROR getting 'android:resource' attribute for "
1911 "meta-data tag in service '%s': %s",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001912 serviceName.string(), error.string());
Adam Lesinski94fc9122013-09-30 17:16:09 -07001913 goto bail;
1914 }
1915
1916 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1917 offHost, &error);
1918 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001919 SourcePos(manifestFile, tree.getLineNumber()).error(
1920 "ERROR getting AID category for service '%s'",
Adam Lesinski94fc9122013-09-30 17:16:09 -07001921 serviceName.string());
1922 goto bail;
1923 }
1924
1925 const size_t catLen = categories.size();
1926 for (size_t i = 0; i < catLen; i++) {
1927 bool paymentCategory = (categories[i] == "payment");
1928 if (offHost) {
1929 hasMetaOffHostPaymentCategory |= paymentCategory;
1930 } else {
1931 hasMetaHostPaymentCategory |= paymentCategory;
1932 }
1933 }
1934 }
1935 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001936 } else if ((depth == 5) && withinIntentFilter) {
1937 String8 action;
1938 if (tag == "action") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001939 action = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001940 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001941 SourcePos(manifestFile, tree.getLineNumber()).error(
1942 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001943 goto bail;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001944 }
1945
Adam Lesinskia5018c92013-09-30 16:23:15 -07001946 if (withinActivity) {
1947 if (action == "android.intent.action.MAIN") {
1948 isMainActivity = true;
1949 actMainActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001950 } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
1951 action == "android.media.action.VIDEO_CAMERA") {
1952 actCamera = true;
1953 } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
1954 actCameraSecure = true;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001955 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001956 } else if (withinReceiver) {
1957 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1958 actWidgetReceivers = true;
1959 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1960 actDeviceAdminEnabled = true;
1961 }
1962 } else if (withinService) {
1963 if (action == "android.view.InputMethod") {
1964 actImeService = true;
1965 } else if (action == "android.service.wallpaper.WallpaperService") {
1966 actWallpaperService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001967 } else if (action ==
1968 "android.accessibilityservice.AccessibilityService") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001969 actAccessibilityService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001970 } else if (action =="android.printservice.PrintService") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001971 actPrintService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001972 } else if (action ==
1973 "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07001974 actHostApduService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001975 } else if (action ==
1976 "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07001977 actOffHostApduService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001978 } else if (action ==
1979 "android.service.notification.NotificationListenerService") {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001980 actNotificationListenerService = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001981 } else if (action == "android.service.dreams.DreamService") {
1982 actDreamService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001983 }
1984 } else if (withinProvider) {
1985 if (action == "android.content.action.DOCUMENTS_PROVIDER") {
1986 actDocumentsProvider = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001987 }
1988 }
1989 if (action == "android.intent.action.SEARCH") {
1990 isSearchable = true;
1991 }
1992 }
1993
1994 if (tag == "category") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001995 String8 category = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001996 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001997 SourcePos(manifestFile, tree.getLineNumber()).error(
1998 "ERROR getting 'name' attribute: %s", error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001999 goto bail;
2000 }
2001 if (withinActivity) {
2002 if (category == "android.intent.category.LAUNCHER") {
2003 isLauncherActivity = true;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08002004 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
2005 isLeanbackLauncherActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002006 } else if (category == "android.intent.category.HOME") {
2007 catLauncher = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08002008 }
2009 }
2010 }
2011 }
2012 }
2013
2014 // Pre-1.6 implicitly granted permission compatibility logic
2015 if (targetSdk < 4) {
2016 if (!hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002017 printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
2018 printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
2019 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002020 hasWriteExternalStoragePermission = true;
2021 }
2022 if (!hasReadPhoneStatePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002023 printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
2024 printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
2025 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002026 }
2027 }
2028
2029 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
2030 // force them to always take READ_EXTERNAL_STORAGE as well. We always
2031 // do this (regardless of target API version) because we can't have
2032 // an app with write permission but not read permission.
2033 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002034 printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"));
2035 printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
2036 String8("requested WRITE_EXTERNAL_STORAGE"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002037 }
2038
2039 // Pre-JellyBean call log permission compatibility.
2040 if (targetSdk < 16) {
2041 if (!hasReadCallLogPermission && hasReadContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002042 printUsesPermission(String8("android.permission.READ_CALL_LOG"));
2043 printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
2044 String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002045 }
2046 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002047 printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
2048 printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
2049 String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002050 }
2051 }
2052
Adam Lesinskica955a42016-08-01 16:44:29 -07002053 // If the app hasn't declared the touchscreen as a feature requirement (either
2054 // directly or implied, required or not), then the faketouch feature is implied.
2055 if (!hasFeature("android.hardware.touchscreen", commonFeatures, impliedFeatures)) {
2056 addImpliedFeature(&impliedFeatures, "android.hardware.faketouch",
2057 "default feature for all apps", false);
2058 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07002059
2060 const size_t numFeatureGroups = featureGroups.size();
2061 if (numFeatureGroups == 0) {
2062 // If no <feature-group> tags were defined, apply auto-implied features.
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002063 printDefaultFeatureGroup(commonFeatures, impliedFeatures);
Adam Lesinski2c72b682014-06-24 09:56:01 -07002064
2065 } else {
2066 // <feature-group> tags are defined, so we ignore implied features and
2067 for (size_t i = 0; i < numFeatureGroups; i++) {
2068 FeatureGroup& grp = featureGroups.editItemAt(i);
2069
Adam Lesinskid7a94da2014-07-25 14:38:54 -07002070 if (commonFeatures.openGLESVersion > grp.openGLESVersion) {
2071 grp.openGLESVersion = commonFeatures.openGLESVersion;
2072 }
2073
Adam Lesinski2c72b682014-06-24 09:56:01 -07002074 // Merge the features defined in the top level (not inside a <feature-group>)
2075 // with this feature group.
2076 const size_t numCommonFeatures = commonFeatures.features.size();
2077 for (size_t j = 0; j < numCommonFeatures; j++) {
2078 if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07002079 grp.features.add(commonFeatures.features.keyAt(j),
2080 commonFeatures.features[j]);
Adam Lesinski2c72b682014-06-24 09:56:01 -07002081 }
2082 }
2083
Adam Lesinski73a05112014-12-08 12:53:17 -08002084 if (!grp.features.isEmpty()) {
Adam Lesinski2c72b682014-06-24 09:56:01 -07002085 printFeatureGroup(grp);
Adam Lesinski282e1812014-01-23 18:17:42 -08002086 }
2087 }
2088 }
2089
Adam Lesinski282e1812014-01-23 18:17:42 -08002090
Adam Lesinski282e1812014-01-23 18:17:42 -08002091 if (hasWidgetReceivers) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002092 printComponentPresence("app-widget");
Adam Lesinski282e1812014-01-23 18:17:42 -08002093 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002094 if (hasDeviceAdminReceiver) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002095 printComponentPresence("device-admin");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002096 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002097 if (hasImeService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002098 printComponentPresence("ime");
Adam Lesinski282e1812014-01-23 18:17:42 -08002099 }
2100 if (hasWallpaperService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002101 printComponentPresence("wallpaper");
Adam Lesinski282e1812014-01-23 18:17:42 -08002102 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002103 if (hasAccessibilityService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002104 printComponentPresence("accessibility");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002105 }
2106 if (hasPrintService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002107 printComponentPresence("print-service");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002108 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07002109 if (hasPaymentService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002110 printComponentPresence("payment");
2111 }
2112 if (isSearchable) {
2113 printComponentPresence("search");
2114 }
2115 if (hasDocumentsProvider) {
2116 printComponentPresence("document-provider");
2117 }
2118 if (hasLauncher) {
2119 printComponentPresence("launcher");
2120 }
2121 if (hasNotificationListenerService) {
2122 printComponentPresence("notification-listener");
2123 }
John Spurlockeb8d1be2014-06-25 17:46:15 -04002124 if (hasDreamService) {
2125 printComponentPresence("dream");
2126 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002127 if (hasCameraActivity) {
2128 printComponentPresence("camera");
2129 }
2130 if (hasCameraSecureActivity) {
2131 printComponentPresence("camera-secure");
2132 }
2133
2134 if (hasMainActivity) {
2135 printf("main\n");
Adam Lesinski94fc9122013-09-30 17:16:09 -07002136 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002137 if (hasOtherActivities) {
2138 printf("other-activities\n");
2139 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002140 if (hasOtherReceivers) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002141 printf("other-receivers\n");
2142 }
2143 if (hasOtherServices) {
2144 printf("other-services\n");
2145 }
2146
2147 // For modern apps, if screen size buckets haven't been specified
2148 // but the new width ranges have, then infer the buckets from them.
2149 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
2150 && requiresSmallestWidthDp > 0) {
2151 int compatWidth = compatibleWidthLimitDp;
2152 if (compatWidth <= 0) {
2153 compatWidth = requiresSmallestWidthDp;
2154 }
2155 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
2156 smallScreen = -1;
2157 } else {
2158 smallScreen = 0;
2159 }
2160 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
2161 normalScreen = -1;
2162 } else {
2163 normalScreen = 0;
2164 }
2165 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
2166 largeScreen = -1;
2167 } else {
2168 largeScreen = 0;
2169 }
2170 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
2171 xlargeScreen = -1;
2172 } else {
2173 xlargeScreen = 0;
2174 }
2175 }
2176
2177 // Determine default values for any unspecified screen sizes,
2178 // based on the target SDK of the package. As of 4 (donut)
2179 // the screen size support was introduced, so all default to
2180 // enabled.
2181 if (smallScreen > 0) {
2182 smallScreen = targetSdk >= 4 ? -1 : 0;
2183 }
2184 if (normalScreen > 0) {
2185 normalScreen = -1;
2186 }
2187 if (largeScreen > 0) {
2188 largeScreen = targetSdk >= 4 ? -1 : 0;
2189 }
2190 if (xlargeScreen > 0) {
2191 // Introduced in Gingerbread.
2192 xlargeScreen = targetSdk >= 9 ? -1 : 0;
2193 }
2194 if (anyDensity > 0) {
2195 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
2196 || compatibleWidthLimitDp > 0) ? -1 : 0;
2197 }
2198 printf("supports-screens:");
2199 if (smallScreen != 0) {
2200 printf(" 'small'");
2201 }
2202 if (normalScreen != 0) {
2203 printf(" 'normal'");
2204 }
2205 if (largeScreen != 0) {
2206 printf(" 'large'");
2207 }
2208 if (xlargeScreen != 0) {
2209 printf(" 'xlarge'");
2210 }
2211 printf("\n");
2212 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
2213 if (requiresSmallestWidthDp > 0) {
2214 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
2215 }
2216 if (compatibleWidthLimitDp > 0) {
2217 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
2218 }
2219 if (largestWidthLimitDp > 0) {
2220 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
2221 }
2222
2223 printf("locales:");
2224 const size_t NL = locales.size();
2225 for (size_t i=0; i<NL; i++) {
2226 const char* localeStr = locales[i].string();
2227 if (localeStr == NULL || strlen(localeStr) == 0) {
2228 localeStr = "--_--";
2229 }
2230 printf(" '%s'", localeStr);
2231 }
2232 printf("\n");
2233
2234 printf("densities:");
2235 const size_t ND = densities.size();
2236 for (size_t i=0; i<ND; i++) {
2237 printf(" '%d'", densities[i]);
2238 }
2239 printf("\n");
2240
2241 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
2242 if (dir != NULL) {
2243 if (dir->getFileCount() > 0) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002244 SortedVector<String8> architectures;
Adam Lesinski282e1812014-01-23 18:17:42 -08002245 for (size_t i=0; i<dir->getFileCount(); i++) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002246 architectures.add(ResTable::normalizeForOutput(
2247 dir->getFileName(i).string()));
Adam Lesinski282e1812014-01-23 18:17:42 -08002248 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07002249
2250 bool outputAltNativeCode = false;
2251 // A multiArch package is one that contains 64-bit and
2252 // 32-bit versions of native code and expects 3rd-party
2253 // apps to load these native code libraries. Since most
2254 // 64-bit systems also support 32-bit apps, the apps
2255 // loading this multiArch package's code may be either
2256 // 32-bit or 64-bit.
2257 if (hasMultiArch) {
2258 // If this is a multiArch package, report the 64-bit
2259 // version only. Then as a separate entry, report the
2260 // rest.
2261 //
2262 // If we report the 32-bit architecture, this APK will
2263 // be installed on a 32-bit device, causing a large waste
2264 // of bandwidth and disk space. This assumes that
2265 // the developer of the multiArch package has also
2266 // made a version that is 32-bit only.
2267 String8 intel64("x86_64");
2268 String8 arm64("arm64-v8a");
2269 ssize_t index = architectures.indexOf(intel64);
2270 if (index < 0) {
2271 index = architectures.indexOf(arm64);
2272 }
2273
2274 if (index >= 0) {
2275 printf("native-code: '%s'\n", architectures[index].string());
2276 architectures.removeAt(index);
2277 outputAltNativeCode = true;
2278 }
2279 }
2280
2281 const size_t archCount = architectures.size();
2282 if (archCount > 0) {
2283 if (outputAltNativeCode) {
2284 printf("alt-");
2285 }
2286 printf("native-code:");
2287 for (size_t i = 0; i < archCount; i++) {
2288 printf(" '%s'", architectures[i].string());
2289 }
2290 printf("\n");
2291 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002292 }
2293 delete dir;
2294 }
2295 } else if (strcmp("badger", option) == 0) {
2296 printf("%s", CONSOLE_DATA);
2297 } else if (strcmp("configurations", option) == 0) {
2298 Vector<ResTable_config> configs;
2299 res.getConfigurations(&configs);
2300 const size_t N = configs.size();
2301 for (size_t i=0; i<N; i++) {
2302 printf("%s\n", configs[i].toString().string());
2303 }
2304 } else {
2305 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2306 goto bail;
2307 }
2308 }
2309
2310 result = NO_ERROR;
2311
2312bail:
Adam Lesinski10de3af12016-07-13 10:14:03 -07002313 if (SourcePos::hasErrors()) {
2314 SourcePos::printErrors(stderr);
2315 }
2316
Adam Lesinski282e1812014-01-23 18:17:42 -08002317 if (asset) {
2318 delete asset;
2319 }
2320 return (result != NO_ERROR);
2321}
2322
2323
2324/*
2325 * Handle the "add" command, which wants to add files to a new or
2326 * pre-existing archive.
2327 */
2328int doAdd(Bundle* bundle)
2329{
2330 ZipFile* zip = NULL;
2331 status_t result = UNKNOWN_ERROR;
2332 const char* zipFileName;
2333
2334 if (bundle->getUpdate()) {
2335 /* avoid confusion */
2336 fprintf(stderr, "ERROR: can't use '-u' with add\n");
2337 goto bail;
2338 }
2339
2340 if (bundle->getFileSpecCount() < 1) {
2341 fprintf(stderr, "ERROR: must specify zip file name\n");
2342 goto bail;
2343 }
2344 zipFileName = bundle->getFileSpecEntry(0);
2345
2346 if (bundle->getFileSpecCount() < 2) {
2347 fprintf(stderr, "NOTE: nothing to do\n");
2348 goto bail;
2349 }
2350
2351 zip = openReadWrite(zipFileName, true);
2352 if (zip == NULL) {
2353 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2354 goto bail;
2355 }
2356
2357 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2358 const char* fileName = bundle->getFileSpecEntry(i);
2359
2360 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
2361 printf(" '%s'... (from gzip)\n", fileName);
2362 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
2363 } else {
2364 if (bundle->getJunkPath()) {
2365 String8 storageName = String8(fileName).getPathLeaf();
Maurice Chu2675f762013-10-22 17:33:11 -07002366 printf(" '%s' as '%s'...\n", fileName,
2367 ResTable::normalizeForOutput(storageName.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08002368 result = zip->add(fileName, storageName.string(),
2369 bundle->getCompressionMethod(), NULL);
2370 } else {
2371 printf(" '%s'...\n", fileName);
2372 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2373 }
2374 }
2375 if (result != NO_ERROR) {
2376 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2377 if (result == NAME_NOT_FOUND) {
2378 fprintf(stderr, ": file not found\n");
2379 } else if (result == ALREADY_EXISTS) {
2380 fprintf(stderr, ": already exists in archive\n");
2381 } else {
2382 fprintf(stderr, "\n");
2383 }
2384 goto bail;
2385 }
2386 }
2387
2388 result = NO_ERROR;
2389
2390bail:
2391 delete zip;
2392 return (result != NO_ERROR);
2393}
2394
2395
2396/*
2397 * Delete files from an existing archive.
2398 */
2399int doRemove(Bundle* bundle)
2400{
2401 ZipFile* zip = NULL;
2402 status_t result = UNKNOWN_ERROR;
2403 const char* zipFileName;
2404
2405 if (bundle->getFileSpecCount() < 1) {
2406 fprintf(stderr, "ERROR: must specify zip file name\n");
2407 goto bail;
2408 }
2409 zipFileName = bundle->getFileSpecEntry(0);
2410
2411 if (bundle->getFileSpecCount() < 2) {
2412 fprintf(stderr, "NOTE: nothing to do\n");
2413 goto bail;
2414 }
2415
2416 zip = openReadWrite(zipFileName, false);
2417 if (zip == NULL) {
2418 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2419 zipFileName);
2420 goto bail;
2421 }
2422
2423 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2424 const char* fileName = bundle->getFileSpecEntry(i);
2425 ZipEntry* entry;
2426
2427 entry = zip->getEntryByName(fileName);
2428 if (entry == NULL) {
2429 printf(" '%s' NOT FOUND\n", fileName);
2430 continue;
2431 }
2432
2433 result = zip->remove(entry);
2434
2435 if (result != NO_ERROR) {
2436 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2437 bundle->getFileSpecEntry(i), zipFileName);
2438 goto bail;
2439 }
2440 }
2441
2442 /* update the archive */
2443 zip->flush();
2444
2445bail:
2446 delete zip;
2447 return (result != NO_ERROR);
2448}
2449
Adam Lesinski3921e872014-05-13 10:56:25 -07002450static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002451 const size_t numDirs = dir->getDirs().size();
2452 for (size_t i = 0; i < numDirs; i++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002453 bool ignore = ignoreConfig;
2454 const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2455 const char* dirStr = subDir->getLeaf().string();
2456 if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2457 ignore = true;
2458 }
2459 status_t err = addResourcesToBuilder(subDir, builder, ignore);
Adam Lesinskifab50872014-04-16 14:40:42 -07002460 if (err != NO_ERROR) {
2461 return err;
2462 }
2463 }
2464
2465 const size_t numFiles = dir->getFiles().size();
2466 for (size_t i = 0; i < numFiles; i++) {
2467 sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2468 const size_t numConfigs = gp->getFiles().size();
2469 for (size_t j = 0; j < numConfigs; j++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002470 status_t err = NO_ERROR;
2471 if (ignoreConfig) {
2472 err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2473 } else {
2474 err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2475 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002476 if (err != NO_ERROR) {
2477 fprintf(stderr, "Failed to add %s (%s) to builder.\n",
2478 gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
2479 return err;
2480 }
2481 }
2482 }
2483 return NO_ERROR;
2484}
2485
2486static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2487 if (split->isBase()) {
2488 return original;
2489 }
2490
2491 String8 ext(original.getPathExtension());
2492 if (ext == String8(".apk")) {
2493 return String8::format("%s_%s%s",
2494 original.getBasePath().string(),
2495 split->getDirectorySafeName().string(),
2496 ext.string());
2497 }
2498
2499 return String8::format("%s_%s", original.string(),
2500 split->getDirectorySafeName().string());
2501}
Adam Lesinski282e1812014-01-23 18:17:42 -08002502
2503/*
2504 * Package up an asset directory and associated application files.
2505 */
2506int doPackage(Bundle* bundle)
2507{
2508 const char* outputAPKFile;
2509 int retVal = 1;
2510 status_t err;
2511 sp<AaptAssets> assets;
2512 int N;
2513 FILE* fp;
2514 String8 dependencyFile;
Adam Lesinskifab50872014-04-16 14:40:42 -07002515 sp<ApkBuilder> builder;
Adam Lesinski282e1812014-01-23 18:17:42 -08002516
Anton Krumina2ef5c02014-03-12 14:46:44 -07002517 // -c en_XA or/and ar_XB means do pseudolocalization
Adam Lesinskifab50872014-04-16 14:40:42 -07002518 sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2519 err = configFilter->parse(bundle->getConfigurations());
Adam Lesinski282e1812014-01-23 18:17:42 -08002520 if (err != NO_ERROR) {
2521 goto bail;
2522 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002523 if (configFilter->containsPseudo()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002524 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2525 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002526 if (configFilter->containsPseudoBidi()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002527 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
Adam Lesinski282e1812014-01-23 18:17:42 -08002528 }
2529
2530 N = bundle->getFileSpecCount();
2531 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
Adam Lesinski09384302014-01-22 16:07:42 -08002532 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002533 fprintf(stderr, "ERROR: no input files\n");
2534 goto bail;
2535 }
2536
2537 outputAPKFile = bundle->getOutputAPKFile();
2538
2539 // Make sure the filenames provided exist and are of the appropriate type.
2540 if (outputAPKFile) {
2541 FileType type;
2542 type = getFileType(outputAPKFile);
2543 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2544 fprintf(stderr,
2545 "ERROR: output file '%s' exists but is not regular file\n",
2546 outputAPKFile);
2547 goto bail;
2548 }
2549 }
2550
2551 // Load the assets.
2552 assets = new AaptAssets();
2553
2554 // Set up the resource gathering in assets if we're going to generate
2555 // dependency files. Every time we encounter a resource while slurping
2556 // the tree, we'll add it to these stores so we have full resource paths
2557 // to write to a dependency file.
2558 if (bundle->getGenDependencies()) {
2559 sp<FilePathStore> resPathStore = new FilePathStore;
2560 assets->setFullResPaths(resPathStore);
2561 sp<FilePathStore> assetPathStore = new FilePathStore;
2562 assets->setFullAssetPaths(assetPathStore);
2563 }
2564
2565 err = assets->slurpFromArgs(bundle);
2566 if (err < 0) {
2567 goto bail;
2568 }
2569
2570 if (bundle->getVerbose()) {
2571 assets->print(String8());
2572 }
2573
Adam Lesinskifab50872014-04-16 14:40:42 -07002574 // Create the ApkBuilder, which will collect the compiled files
2575 // to write to the final APK (or sets of APKs if we are building
2576 // a Split APK.
2577 builder = new ApkBuilder(configFilter);
2578
2579 // If we are generating a Split APK, find out which configurations to split on.
2580 if (bundle->getSplitConfigurations().size() > 0) {
2581 const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2582 const size_t numSplits = splitStrs.size();
2583 for (size_t i = 0; i < numSplits; i++) {
2584 std::set<ConfigDescription> configs;
2585 if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2586 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2587 goto bail;
2588 }
2589
2590 err = builder->createSplitForConfigs(configs);
2591 if (err != NO_ERROR) {
2592 goto bail;
2593 }
2594 }
2595 }
2596
Adam Lesinski282e1812014-01-23 18:17:42 -08002597 // If they asked for any fileAs that need to be compiled, do so.
2598 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002599 err = buildResources(bundle, assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002600 if (err != 0) {
2601 goto bail;
2602 }
2603 }
2604
2605 // At this point we've read everything and processed everything. From here
2606 // on out it's just writing output files.
2607 if (SourcePos::hasErrors()) {
2608 goto bail;
2609 }
2610
2611 // Update symbols with information about which ones are needed as Java symbols.
2612 assets->applyJavaSymbols();
2613 if (SourcePos::hasErrors()) {
2614 goto bail;
2615 }
2616
2617 // If we've been asked to generate a dependency file, do that here
2618 if (bundle->getGenDependencies()) {
2619 // If this is the packaging step, generate the dependency file next to
2620 // the output apk (e.g. bin/resources.ap_.d)
2621 if (outputAPKFile) {
2622 dependencyFile = String8(outputAPKFile);
2623 // Add the .d extension to the dependency file.
2624 dependencyFile.append(".d");
2625 } else {
2626 // Else if this is the R.java dependency generation step,
2627 // generate the dependency file in the R.java package subdirectory
2628 // e.g. gen/com/foo/app/R.java.d
2629 dependencyFile = String8(bundle->getRClassDir());
2630 dependencyFile.appendPath("R.java.d");
2631 }
2632 // Make sure we have a clean dependency file to start with
2633 fp = fopen(dependencyFile, "w");
2634 fclose(fp);
2635 }
2636
2637 // Write out R.java constants
2638 if (!assets->havePrivateSymbols()) {
2639 if (bundle->getCustomPackage() == NULL) {
2640 // Write the R.java file into the appropriate class directory
2641 // e.g. gen/com/foo/app/R.java
Adam Lesinski1e4663852014-08-15 14:47:28 -07002642 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002643 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002644 } else {
2645 const String8 customPkg(bundle->getCustomPackage());
Adam Lesinski1e4663852014-08-15 14:47:28 -07002646 err = writeResourceSymbols(bundle, assets, customPkg, true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002647 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002648 }
2649 if (err < 0) {
2650 goto bail;
2651 }
2652 // If we have library files, we're going to write our R.java file into
2653 // the appropriate class directory for those libraries as well.
2654 // e.g. gen/com/foo/app/lib/R.java
2655 if (bundle->getExtraPackages() != NULL) {
2656 // Split on colon
2657 String8 libs(bundle->getExtraPackages());
2658 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2659 while (packageString != NULL) {
2660 // Write the R.java file out with the correct package name
Marcin Kosiba0f3a5a62014-09-11 13:48:48 +01002661 err = writeResourceSymbols(bundle, assets, String8(packageString), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002662 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002663 if (err < 0) {
2664 goto bail;
2665 }
2666 packageString = strtok(NULL, ":");
2667 }
2668 libs.unlockBuffer();
2669 }
2670 } else {
Adam Lesinski1e4663852014-08-15 14:47:28 -07002671 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002672 if (err < 0) {
2673 goto bail;
2674 }
Adam Lesinski1e4663852014-08-15 14:47:28 -07002675 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002676 if (err < 0) {
2677 goto bail;
2678 }
2679 }
2680
2681 // Write out the ProGuard file
2682 err = writeProguardFile(bundle, assets);
2683 if (err < 0) {
2684 goto bail;
2685 }
2686
Rohit Agrawal86229cb2016-04-21 16:29:58 -07002687 // Write out the Main Dex ProGuard file
2688 err = writeMainDexProguardFile(bundle, assets);
2689 if (err < 0) {
2690 goto bail;
2691 }
2692
Adam Lesinski282e1812014-01-23 18:17:42 -08002693 // Write the apk
2694 if (outputAPKFile) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002695 // Gather all resources and add them to the APK Builder. The builder will then
2696 // figure out which Split they belong in.
2697 err = addResourcesToBuilder(assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002698 if (err != NO_ERROR) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002699 goto bail;
2700 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002701
2702 const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2703 const size_t numSplits = splits.size();
2704 for (size_t i = 0; i < numSplits; i++) {
2705 const sp<ApkSplit>& split = splits[i];
2706 String8 outputPath = buildApkName(String8(outputAPKFile), split);
2707 err = writeAPK(bundle, outputPath, split);
2708 if (err != NO_ERROR) {
2709 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2710 goto bail;
2711 }
2712 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002713 }
2714
2715 // If we've been asked to generate a dependency file, we need to finish up here.
2716 // the writeResourceSymbols and writeAPK functions have already written the target
2717 // half of the dependency file, now we need to write the prerequisites. (files that
2718 // the R.java file or .ap_ file depend on)
2719 if (bundle->getGenDependencies()) {
2720 // Now that writeResourceSymbols or writeAPK has taken care of writing
2721 // the targets to our dependency file, we'll write the prereqs
2722 fp = fopen(dependencyFile, "a+");
2723 fprintf(fp, " : ");
2724 bool includeRaw = (outputAPKFile != NULL);
2725 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2726 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2727 // and therefore was not added to our pathstores during slurping
2728 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2729 fclose(fp);
2730 }
2731
2732 retVal = 0;
2733bail:
2734 if (SourcePos::hasErrors()) {
2735 SourcePos::printErrors(stderr);
2736 }
2737 return retVal;
2738}
2739
2740/*
2741 * Do PNG Crunching
2742 * PRECONDITIONS
2743 * -S flag points to a source directory containing drawable* folders
2744 * -C flag points to destination directory. The folder structure in the
2745 * source directory will be mirrored to the destination (cache) directory
2746 *
2747 * POSTCONDITIONS
2748 * Destination directory will be updated to match the PNG files in
Maurice Chu2675f762013-10-22 17:33:11 -07002749 * the source directory.
Adam Lesinski282e1812014-01-23 18:17:42 -08002750 */
2751int doCrunch(Bundle* bundle)
2752{
2753 fprintf(stdout, "Crunching PNG Files in ");
2754 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2755 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2756
2757 updatePreProcessedCache(bundle);
2758
2759 return NO_ERROR;
2760}
2761
2762/*
2763 * Do PNG Crunching on a single flag
2764 * -i points to a single png file
2765 * -o points to a single png output file
2766 */
2767int doSingleCrunch(Bundle* bundle)
2768{
2769 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2770 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2771
2772 String8 input(bundle->getSingleCrunchInputFile());
2773 String8 output(bundle->getSingleCrunchOutputFile());
2774
2775 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2776 // we can't return the status_t as it gets truncate to the lower 8 bits.
2777 return 42;
2778 }
2779
2780 return NO_ERROR;
2781}
2782
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002783int runInDaemonMode(Bundle* bundle) {
2784 std::cout << "Ready" << std::endl;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002785 for (std::string cmd; std::getline(std::cin, cmd);) {
2786 if (cmd == "quit") {
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002787 return NO_ERROR;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002788 } else if (cmd == "s") {
2789 // Two argument crunch
2790 std::string inputFile, outputFile;
2791 std::getline(std::cin, inputFile);
2792 std::getline(std::cin, outputFile);
2793 bundle->setSingleCrunchInputFile(inputFile.c_str());
2794 bundle->setSingleCrunchOutputFile(outputFile.c_str());
2795 std::cout << "Crunching " << inputFile << std::endl;
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002796 if (doSingleCrunch(bundle) != NO_ERROR) {
2797 std::cout << "Error" << std::endl;
2798 }
2799 std::cout << "Done" << std::endl;
2800 } else {
2801 // in case of invalid command, just bail out.
2802 std::cerr << "Unknown command" << std::endl;
2803 return -1;
2804 }
2805 }
2806 return -1;
2807}
2808
Adam Lesinski282e1812014-01-23 18:17:42 -08002809char CONSOLE_DATA[2925] = {
2810 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2811 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2812 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2813 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2814 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2815 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2816 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2817 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2818 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2819 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2820 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2821 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2822 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2823 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2824 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2825 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2826 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2827 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2828 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2829 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2830 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2831 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2832 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2833 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2834 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2835 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2836 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2837 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2838 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2839 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2840 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2841 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2842 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2843 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2844 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2845 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2846 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2847 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2848 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2849 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2850 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2851 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2852 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2853 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2854 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2855 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2856 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2857 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2858 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2859 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2860 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2861 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2862 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2863 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2864 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2865 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2866 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2867 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2868 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2869 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2870 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2871 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2872 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2873 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2874 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2875 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2876 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2877 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2878 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2879 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2880 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2881 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2882 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2883 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2884 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2885 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2886 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2887 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2888 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2889 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2890 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2891 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2892 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2893 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2894 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2895 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2896 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2897 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2898 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2899 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2900 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2901 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2902 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2903 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2904 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2905 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2906 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2907 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2908 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2909 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2910 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2911 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2912 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2913 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2914 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2915 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2916 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2917 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2918 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2919 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2920 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2921 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2922 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2923 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2924 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2925 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2926 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2927 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2928 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2929 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2930 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2931 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2932 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2933 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2934 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2935 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2936 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2937 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2938 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2939 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2940 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2941 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2942 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2943 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2944 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2945 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2946 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2947 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2948 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2949 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2950 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2951 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2952 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2953 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2954 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2955 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2956 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2957 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2958 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2959 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2960 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2961 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2962 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2963 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2964 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2965 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2966 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2967 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2968 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2969 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2970 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2971 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2972 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2973 };