blob: 288b2a320de6d387542e898741c73822c557e9ad [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,
Maurice Chu76327312013-10-16 18:28:46 -0700251 uint32_t attrRes, String8 attrLabel, String8* outError)
252{
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
Adam Lesinski94fc9122013-09-30 17:16:09 -0700402Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool offHost,
403 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
532static void addImpliedFeature(KeyedVector<String8, ImpliedFeature>* impliedFeatures,
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800533 const char* name, const char* reason, bool sdk23) {
Adam Lesinski2c72b682014-06-24 09:56:01 -0700534 String8 name8(name);
535 ssize_t idx = impliedFeatures->indexOfKey(name8);
536 if (idx < 0) {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800537 idx = impliedFeatures->add(name8, ImpliedFeature(name8, sdk23));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700538 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800539
540 ImpliedFeature* feature = &impliedFeatures->editValueAt(idx);
541
542 // A non-sdk 23 implied feature takes precedence.
543 if (feature->impliedBySdk23 && !sdk23) {
544 feature->impliedBySdk23 = false;
545 }
546 feature->reasons.add(String8(reason));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700547}
548
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800549static void printFeatureGroupImpl(const FeatureGroup& grp,
550 const KeyedVector<String8, ImpliedFeature>* impliedFeatures) {
Adam Lesinski2c72b682014-06-24 09:56:01 -0700551 printf("feature-group: label='%s'\n", grp.label.string());
552
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700553 if (grp.openGLESVersion > 0) {
554 printf(" uses-gl-es: '0x%x'\n", grp.openGLESVersion);
555 }
556
Adam Lesinski2c72b682014-06-24 09:56:01 -0700557 const size_t numFeatures = grp.features.size();
558 for (size_t i = 0; i < numFeatures; i++) {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700559 const Feature& feature = grp.features[i];
560 const bool required = feature.required;
561 const int32_t version = feature.version;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700562
563 const String8& featureName = grp.features.keyAt(i);
Adam Lesinski694d0a72016-04-06 16:12:04 -0700564 printf(" uses-feature%s: name='%s'", (required ? "" : "-not-required"),
Adam Lesinski2c72b682014-06-24 09:56:01 -0700565 ResTable::normalizeForOutput(featureName.string()).string());
Adam Lesinski694d0a72016-04-06 16:12:04 -0700566
567 if (version > 0) {
568 printf(" version='%d'", version);
569 }
570 printf("\n");
Adam Lesinski2c72b682014-06-24 09:56:01 -0700571 }
572
573 const size_t numImpliedFeatures =
574 (impliedFeatures != NULL) ? impliedFeatures->size() : 0;
575 for (size_t i = 0; i < numImpliedFeatures; i++) {
576 const ImpliedFeature& impliedFeature = impliedFeatures->valueAt(i);
577 if (grp.features.indexOfKey(impliedFeature.name) >= 0) {
578 // The feature is explicitly set, no need to use implied
579 // definition.
580 continue;
581 }
582
583 String8 printableFeatureName(ResTable::normalizeForOutput(
584 impliedFeature.name.string()));
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800585 const char* sdk23Suffix = impliedFeature.impliedBySdk23 ? "-sdk-23" : "";
586
587 printf(" uses-feature%s: name='%s'\n", sdk23Suffix, printableFeatureName.string());
588 printf(" uses-implied-feature%s: name='%s' reason='", sdk23Suffix,
589 printableFeatureName.string());
Adam Lesinski2c72b682014-06-24 09:56:01 -0700590 const size_t numReasons = impliedFeature.reasons.size();
591 for (size_t j = 0; j < numReasons; j++) {
592 printf("%s", impliedFeature.reasons[j].string());
593 if (j + 2 < numReasons) {
594 printf(", ");
595 } else if (j + 1 < numReasons) {
596 printf(", and ");
597 }
598 }
599 printf("'\n");
600 }
601}
602
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800603static void printFeatureGroup(const FeatureGroup& grp) {
604 printFeatureGroupImpl(grp, NULL);
605}
606
607static void printDefaultFeatureGroup(const FeatureGroup& grp,
608 const KeyedVector<String8, ImpliedFeature>& impliedFeatures) {
609 printFeatureGroupImpl(grp, &impliedFeatures);
610}
611
Adam Lesinski2c72b682014-06-24 09:56:01 -0700612static void addParentFeatures(FeatureGroup* grp, const String8& name) {
613 if (name == "android.hardware.camera.autofocus" ||
614 name == "android.hardware.camera.flash") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700615 grp->features.add(String8("android.hardware.camera"), Feature(true));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700616 } else if (name == "android.hardware.location.gps" ||
617 name == "android.hardware.location.network") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700618 grp->features.add(String8("android.hardware.location"), Feature(true));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700619 } else if (name == "android.hardware.touchscreen.multitouch") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700620 grp->features.add(String8("android.hardware.touchscreen"), Feature(true));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700621 } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700622 grp->features.add(String8("android.hardware.touchscreen.multitouch"), Feature(true));
623 grp->features.add(String8("android.hardware.touchscreen"), Feature(true));
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700624 } else if (name == "android.hardware.opengles.aep") {
625 const int openGLESVersion31 = 0x00030001;
626 if (openGLESVersion31 > grp->openGLESVersion) {
627 grp->openGLESVersion = openGLESVersion31;
628 }
Adam Lesinski2c72b682014-06-24 09:56:01 -0700629 }
630}
631
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800632static void addImpliedFeaturesForPermission(const int targetSdk, const String8& name,
633 KeyedVector<String8, ImpliedFeature>* impliedFeatures,
634 bool impliedBySdk23Permission) {
635 if (name == "android.permission.CAMERA") {
636 addImpliedFeature(impliedFeatures, "android.hardware.camera",
637 String8::format("requested %s permission", name.string())
638 .string(), impliedBySdk23Permission);
639 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
640 addImpliedFeature(impliedFeatures, "android.hardware.location.gps",
641 String8::format("requested %s permission", name.string())
642 .string(), impliedBySdk23Permission);
643 addImpliedFeature(impliedFeatures, "android.hardware.location",
644 String8::format("requested %s permission", name.string())
645 .string(), impliedBySdk23Permission);
646 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
647 addImpliedFeature(impliedFeatures, "android.hardware.location",
648 String8::format("requested %s permission", name.string())
649 .string(), impliedBySdk23Permission);
650 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
651 addImpliedFeature(impliedFeatures, "android.hardware.location.network",
652 String8::format("requested %s permission", name.string())
653 .string(), impliedBySdk23Permission);
654 addImpliedFeature(impliedFeatures, "android.hardware.location",
655 String8::format("requested %s permission", name.string())
656 .string(), impliedBySdk23Permission);
657 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
658 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
659 addImpliedFeature(impliedFeatures, "android.hardware.location",
660 String8::format("requested %s permission", name.string())
661 .string(), impliedBySdk23Permission);
662 } else if (name == "android.permission.BLUETOOTH" ||
663 name == "android.permission.BLUETOOTH_ADMIN") {
664 if (targetSdk > 4) {
665 addImpliedFeature(impliedFeatures, "android.hardware.bluetooth",
666 String8::format("requested %s permission", name.string())
667 .string(), impliedBySdk23Permission);
668 addImpliedFeature(impliedFeatures, "android.hardware.bluetooth",
669 "targetSdkVersion > 4", impliedBySdk23Permission);
670 }
671 } else if (name == "android.permission.RECORD_AUDIO") {
672 addImpliedFeature(impliedFeatures, "android.hardware.microphone",
673 String8::format("requested %s permission", name.string())
674 .string(), impliedBySdk23Permission);
675 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
676 name == "android.permission.CHANGE_WIFI_STATE" ||
677 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
678 addImpliedFeature(impliedFeatures, "android.hardware.wifi",
679 String8::format("requested %s permission", name.string())
680 .string(), impliedBySdk23Permission);
681 } else if (name == "android.permission.CALL_PHONE" ||
682 name == "android.permission.CALL_PRIVILEGED" ||
683 name == "android.permission.MODIFY_PHONE_STATE" ||
684 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
685 name == "android.permission.READ_SMS" ||
686 name == "android.permission.RECEIVE_SMS" ||
687 name == "android.permission.RECEIVE_MMS" ||
688 name == "android.permission.RECEIVE_WAP_PUSH" ||
689 name == "android.permission.SEND_SMS" ||
690 name == "android.permission.WRITE_APN_SETTINGS" ||
691 name == "android.permission.WRITE_SMS") {
692 addImpliedFeature(impliedFeatures, "android.hardware.telephony",
693 String8("requested a telephony permission").string(),
694 impliedBySdk23Permission);
695 }
696}
697
Adam Lesinski282e1812014-01-23 18:17:42 -0800698/*
699 * Handle the "dump" command, to extract select data from an archive.
700 */
701extern char CONSOLE_DATA[2925]; // see EOF
702int doDump(Bundle* bundle)
703{
704 status_t result = UNKNOWN_ERROR;
Adam Lesinski282e1812014-01-23 18:17:42 -0800705
706 if (bundle->getFileSpecCount() < 1) {
707 fprintf(stderr, "ERROR: no dump option specified\n");
708 return 1;
709 }
710
711 if (bundle->getFileSpecCount() < 2) {
712 fprintf(stderr, "ERROR: no dump file specified\n");
713 return 1;
714 }
715
716 const char* option = bundle->getFileSpecEntry(0);
717 const char* filename = bundle->getFileSpecEntry(1);
718
719 AssetManager assets;
Narayan Kamathf85e41f2014-01-24 14:14:30 +0000720 int32_t assetsCookie;
Adam Lesinski282e1812014-01-23 18:17:42 -0800721 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
722 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
723 return 1;
724 }
725
726 // Make a dummy config for retrieving resources... we need to supply
727 // non-default values for some configs so that we can retrieve resources
728 // in the app that don't have a default. The most important of these is
729 // the API version because key resources like icons will have an implicit
730 // version if they are using newer config types like density.
731 ResTable_config config;
Narayan Kamath91447d82014-01-21 15:32:36 +0000732 memset(&config, 0, sizeof(ResTable_config));
Adam Lesinski282e1812014-01-23 18:17:42 -0800733 config.language[0] = 'e';
734 config.language[1] = 'n';
735 config.country[0] = 'U';
736 config.country[1] = 'S';
737 config.orientation = ResTable_config::ORIENTATION_PORT;
738 config.density = ResTable_config::DENSITY_MEDIUM;
739 config.sdkVersion = 10000; // Very high.
740 config.screenWidthDp = 320;
741 config.screenHeightDp = 480;
742 config.smallestScreenWidthDp = 320;
Adam Lesinskic2dea8d2014-08-04 16:40:41 -0700743 config.screenLayout |= ResTable_config::SCREENSIZE_NORMAL;
Adam Lesinski282e1812014-01-23 18:17:42 -0800744 assets.setConfiguration(config);
745
746 const ResTable& res = assets.getResources(false);
Dan Albert68001652014-09-09 09:51:01 -0700747 if (res.getError() != NO_ERROR) {
Adam Lesinski25e9d552014-05-19 15:01:43 -0700748 fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
Adam Lesinski63e646e2014-07-30 11:40:39 -0700749 return 1;
Adam Lesinski282e1812014-01-23 18:17:42 -0800750 }
751
Adam Lesinski694d0a72016-04-06 16:12:04 -0700752 // Source for AndroidManifest.xml
Adam Lesinski10de3af12016-07-13 10:14:03 -0700753 const String8 manifestFile("AndroidManifest.xml");
Adam Lesinski694d0a72016-04-06 16:12:04 -0700754
Adam Lesinski2cb761e2014-08-15 13:59:02 -0700755 // The dynamicRefTable can be null if there are no resources for this asset cookie.
756 // This fine.
Adam Lesinski63e646e2014-07-30 11:40:39 -0700757 const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700758
759 Asset* asset = NULL;
760
Adam Lesinski282e1812014-01-23 18:17:42 -0800761 if (strcmp("resources", option) == 0) {
Elliott Hughesba3fe562015-08-12 14:49:53 -0700762#ifndef __ANDROID__
Adam Lesinski282e1812014-01-23 18:17:42 -0800763 res.print(bundle->getValues());
764#endif
765
766 } else if (strcmp("strings", option) == 0) {
767 const ResStringPool* pool = res.getTableStringBlock(0);
768 printStringPool(pool);
769
770 } else if (strcmp("xmltree", option) == 0) {
771 if (bundle->getFileSpecCount() < 3) {
772 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
773 goto bail;
774 }
775
776 for (int i=2; i<bundle->getFileSpecCount(); i++) {
777 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700778 ResXMLTree tree(dynamicRefTable);
779 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800780 if (asset == NULL) {
781 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
782 goto bail;
783 }
784
785 if (tree.setTo(asset->getBuffer(true),
786 asset->getLength()) != NO_ERROR) {
787 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
788 goto bail;
789 }
790 tree.restart();
791 printXMLBlock(&tree);
792 tree.uninit();
793 delete asset;
794 asset = NULL;
795 }
796
797 } else if (strcmp("xmlstrings", option) == 0) {
798 if (bundle->getFileSpecCount() < 3) {
799 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
800 goto bail;
801 }
802
803 for (int i=2; i<bundle->getFileSpecCount(); i++) {
804 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700805 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800806 if (asset == NULL) {
807 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
808 goto bail;
809 }
810
Adam Lesinski63e646e2014-07-30 11:40:39 -0700811 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800812 if (tree.setTo(asset->getBuffer(true),
813 asset->getLength()) != NO_ERROR) {
814 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
815 goto bail;
816 }
817 printStringPool(&tree.getStrings());
818 delete asset;
819 asset = NULL;
820 }
821
822 } else {
Adam Lesinski63e646e2014-07-30 11:40:39 -0700823 asset = assets.openNonAsset(assetsCookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800824 if (asset == NULL) {
825 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
826 goto bail;
827 }
828
Adam Lesinski63e646e2014-07-30 11:40:39 -0700829 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800830 if (tree.setTo(asset->getBuffer(true),
831 asset->getLength()) != NO_ERROR) {
832 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
833 goto bail;
834 }
835 tree.restart();
836
837 if (strcmp("permissions", option) == 0) {
838 size_t len;
839 ResXMLTree::event_code_t code;
840 int depth = 0;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800841 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
842 code != ResXMLTree::BAD_DOCUMENT) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800843 if (code == ResXMLTree::END_TAG) {
844 depth--;
845 continue;
846 }
847 if (code != ResXMLTree::START_TAG) {
848 continue;
849 }
850 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700851 const char16_t* ctag16 = tree.getElementName(&len);
852 if (ctag16 == NULL) {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700853 SourcePos(manifestFile, tree.getLineNumber()).error(
854 "ERROR: failed to get XML element name (bad string pool)");
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700855 goto bail;
856 }
857 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800858 //printf("Depth %d tag %s\n", depth, tag.string());
859 if (depth == 1) {
860 if (tag != "manifest") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700861 SourcePos(manifestFile, tree.getLineNumber()).error(
862 "ERROR: manifest does not start with <manifest> tag");
Adam Lesinski282e1812014-01-23 18:17:42 -0800863 goto bail;
864 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700865 String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -0700866 printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800867 } else if (depth == 2) {
868 if (tag == "permission") {
869 String8 error;
870 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
871 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700872 SourcePos(manifestFile, tree.getLineNumber()).error(
873 "ERROR getting 'android:name': %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800874 goto bail;
875 }
876
877 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700878 SourcePos(manifestFile, tree.getLineNumber()).error(
879 "ERROR: missing 'android:name' for permission");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800880 goto bail;
881 }
882 printf("permission: %s\n",
883 ResTable::normalizeForOutput(name.string()).string());
884 } else if (tag == "uses-permission") {
885 String8 error;
886 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
887 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700888 SourcePos(manifestFile, tree.getLineNumber()).error(
889 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800890 goto bail;
891 }
892
893 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700894 SourcePos(manifestFile, tree.getLineNumber()).error(
895 "ERROR: missing 'android:name' for uses-permission");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800896 goto bail;
897 }
898 printUsesPermission(name,
899 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
900 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
901 } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
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-sdk-23");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800913 goto bail;
914 }
915 printUsesPermissionSdk23(
916 name,
917 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
Adam Lesinski282e1812014-01-23 18:17:42 -0800918 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800919 }
920 }
921 } else if (strcmp("badging", option) == 0) {
922 Vector<String8> locales;
923 res.getLocales(&locales);
924
925 Vector<ResTable_config> configs;
926 res.getConfigurations(&configs);
927 SortedVector<int> densities;
928 const size_t NC = configs.size();
929 for (size_t i=0; i<NC; i++) {
930 int dens = configs[i].density;
931 if (dens == 0) {
932 dens = 160;
933 }
934 densities.add(dens);
935 }
936
937 size_t len;
938 ResXMLTree::event_code_t code;
939 int depth = 0;
940 String8 error;
941 bool withinActivity = false;
942 bool isMainActivity = false;
943 bool isLauncherActivity = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800944 bool isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800945 bool isSearchable = false;
946 bool withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700947 bool withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700948 bool withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800949 bool withinReceiver = false;
950 bool withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700951 bool withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800952 bool withinIntentFilter = false;
953 bool hasMainActivity = false;
954 bool hasOtherActivities = false;
955 bool hasOtherReceivers = false;
956 bool hasOtherServices = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700957 bool hasIntentFilter = false;
958
Adam Lesinski282e1812014-01-23 18:17:42 -0800959 bool hasWallpaperService = false;
960 bool hasImeService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700961 bool hasAccessibilityService = false;
962 bool hasPrintService = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800963 bool hasWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700964 bool hasDeviceAdminReceiver = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700965 bool hasPaymentService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700966 bool hasDocumentsProvider = false;
967 bool hasCameraActivity = false;
968 bool hasCameraSecureActivity = false;
969 bool hasLauncher = false;
970 bool hasNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400971 bool hasDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700972
Adam Lesinski282e1812014-01-23 18:17:42 -0800973 bool actMainActivity = false;
974 bool actWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700975 bool actDeviceAdminEnabled = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800976 bool actImeService = false;
977 bool actWallpaperService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700978 bool actAccessibilityService = false;
979 bool actPrintService = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700980 bool actHostApduService = false;
981 bool actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700982 bool actDocumentsProvider = false;
983 bool actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400984 bool actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700985 bool actCamera = false;
986 bool actCameraSecure = false;
987 bool catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700988 bool hasMetaHostPaymentCategory = false;
989 bool hasMetaOffHostPaymentCategory = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700990
991 // These permissions are required by services implementing services
992 // the system binds to (IME, Accessibility, PrintServices, etc.)
993 bool hasBindDeviceAdminPermission = false;
994 bool hasBindInputMethodPermission = false;
995 bool hasBindAccessibilityServicePermission = false;
996 bool hasBindPrintServicePermission = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700997 bool hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700998 bool hasRequiredSafAttributes = false;
999 bool hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001000 bool hasBindDreamServicePermission = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001001
1002 // These two implement the implicit permissions that are granted
1003 // to pre-1.6 applications.
1004 bool hasWriteExternalStoragePermission = false;
1005 bool hasReadPhoneStatePermission = false;
1006
1007 // If an app requests write storage, they will also get read storage.
1008 bool hasReadExternalStoragePermission = false;
1009
1010 // Implement transition to read and write call log.
1011 bool hasReadContactsPermission = false;
1012 bool hasWriteContactsPermission = false;
1013 bool hasReadCallLogPermission = false;
1014 bool hasWriteCallLogPermission = false;
1015
Adam Lesinskie47fd122014-08-15 22:25:36 -07001016 // If an app declares itself as multiArch, we report the
1017 // native libraries differently.
1018 bool hasMultiArch = false;
1019
Adam Lesinski282e1812014-01-23 18:17:42 -08001020 // This next group of variables is used to implement a group of
1021 // backward-compatibility heuristics necessitated by the addition of
1022 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
1023 // heuristic is "if an app requests a permission but doesn't explicitly
1024 // request the corresponding <uses-feature>, presume it's there anyway".
Adam Lesinski2c72b682014-06-24 09:56:01 -07001025
Adam Lesinski282e1812014-01-23 18:17:42 -08001026 // 2.2 also added some other features that apps can request, but that
1027 // have no corresponding permission, so we cannot implement any
1028 // back-compatibility heuristic for them. The below are thus unnecessary
1029 // (but are retained here for documentary purposes.)
1030 //bool specCompassFeature = false;
1031 //bool specAccelerometerFeature = false;
1032 //bool specProximityFeature = false;
1033 //bool specAmbientLightFeature = false;
1034 //bool specLiveWallpaperFeature = false;
1035
1036 int targetSdk = 0;
1037 int smallScreen = 1;
1038 int normalScreen = 1;
1039 int largeScreen = 1;
1040 int xlargeScreen = 1;
1041 int anyDensity = 1;
1042 int requiresSmallestWidthDp = 0;
1043 int compatibleWidthLimitDp = 0;
1044 int largestWidthLimitDp = 0;
1045 String8 pkg;
1046 String8 activityName;
1047 String8 activityLabel;
1048 String8 activityIcon;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001049 String8 activityBanner;
Adam Lesinski282e1812014-01-23 18:17:42 -08001050 String8 receiverName;
1051 String8 serviceName;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001052 Vector<String8> supportedInput;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001053
1054 FeatureGroup commonFeatures;
1055 Vector<FeatureGroup> featureGroups;
1056 KeyedVector<String8, ImpliedFeature> impliedFeatures;
1057
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001058 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
1059 code != ResXMLTree::BAD_DOCUMENT) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001060 if (code == ResXMLTree::END_TAG) {
1061 depth--;
1062 if (depth < 2) {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001063 if (withinSupportsInput && !supportedInput.isEmpty()) {
1064 printf("supports-input: '");
1065 const size_t N = supportedInput.size();
1066 for (size_t i=0; i<N; i++) {
Maurice Chu2675f762013-10-22 17:33:11 -07001067 printf("%s", ResTable::normalizeForOutput(
1068 supportedInput[i].string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001069 if (i != N - 1) {
1070 printf("' '");
1071 } else {
1072 printf("'\n");
1073 }
1074 }
1075 supportedInput.clear();
1076 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001077 withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001078 withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001079 withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001080 } else if (depth < 3) {
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001081 if (withinActivity && isMainActivity) {
Maurice Chu2675f762013-10-22 17:33:11 -07001082 String8 aName(getComponentName(pkg, activityName));
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001083 if (isLauncherActivity) {
1084 printf("launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001085 if (aName.length() > 0) {
1086 printf(" name='%s' ",
1087 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001088 }
1089 printf(" label='%s' icon='%s'\n",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001090 ResTable::normalizeForOutput(activityLabel.string())
1091 .string(),
1092 ResTable::normalizeForOutput(activityIcon.string())
1093 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001094 }
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001095 if (isLeanbackLauncherActivity) {
1096 printf("leanback-launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001097 if (aName.length() > 0) {
1098 printf(" name='%s' ",
1099 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001100 }
1101 printf(" label='%s' icon='%s' banner='%s'\n",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001102 ResTable::normalizeForOutput(activityLabel.string())
1103 .string(),
1104 ResTable::normalizeForOutput(activityIcon.string())
1105 .string(),
1106 ResTable::normalizeForOutput(activityBanner.string())
1107 .string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001108 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001109 }
1110 if (!hasIntentFilter) {
1111 hasOtherActivities |= withinActivity;
1112 hasOtherReceivers |= withinReceiver;
1113 hasOtherServices |= withinService;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001114 } else {
1115 if (withinService) {
1116 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
1117 hasBindNfcServicePermission);
1118 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
1119 hasBindNfcServicePermission);
1120 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001121 }
1122 withinActivity = false;
1123 withinService = false;
1124 withinReceiver = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001125 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001126 hasIntentFilter = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001127 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001128 } else if (depth < 4) {
1129 if (withinIntentFilter) {
1130 if (withinActivity) {
1131 hasMainActivity |= actMainActivity;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001132 hasLauncher |= catLauncher;
1133 hasCameraActivity |= actCamera;
1134 hasCameraSecureActivity |= actCameraSecure;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001135 hasOtherActivities |=
1136 !actMainActivity && !actCamera && !actCameraSecure;
Adam Lesinski282e1812014-01-23 18:17:42 -08001137 } else if (withinReceiver) {
1138 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001139 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
1140 hasBindDeviceAdminPermission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001141 hasOtherReceivers |=
1142 (!actWidgetReceivers && !actDeviceAdminEnabled);
Adam Lesinski282e1812014-01-23 18:17:42 -08001143 } else if (withinService) {
1144 hasImeService |= actImeService;
1145 hasWallpaperService |= actWallpaperService;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001146 hasAccessibilityService |= (actAccessibilityService &&
1147 hasBindAccessibilityServicePermission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001148 hasPrintService |=
1149 (actPrintService && hasBindPrintServicePermission);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001150 hasNotificationListenerService |= actNotificationListenerService &&
1151 hasBindNotificationListenerServicePermission;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001152 hasDreamService |= actDreamService && hasBindDreamServicePermission;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001153 hasOtherServices |= (!actImeService && !actWallpaperService &&
Adam Lesinski94fc9122013-09-30 17:16:09 -07001154 !actAccessibilityService && !actPrintService &&
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001155 !actHostApduService && !actOffHostApduService &&
1156 !actNotificationListenerService);
1157 } else if (withinProvider) {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001158 hasDocumentsProvider |=
1159 actDocumentsProvider && hasRequiredSafAttributes;
Adam Lesinski282e1812014-01-23 18:17:42 -08001160 }
1161 }
1162 withinIntentFilter = false;
1163 }
1164 continue;
1165 }
1166 if (code != ResXMLTree::START_TAG) {
1167 continue;
1168 }
1169 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001170
1171 const char16_t* ctag16 = tree.getElementName(&len);
1172 if (ctag16 == NULL) {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001173 SourcePos(manifestFile, tree.getLineNumber()).error(
1174 "ERROR: failed to get XML element name (bad string pool)");
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001175 goto bail;
1176 }
1177 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -08001178 //printf("Depth %d, %s\n", depth, tag.string());
1179 if (depth == 1) {
1180 if (tag != "manifest") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001181 SourcePos(manifestFile, tree.getLineNumber()).error(
1182 "ERROR: manifest does not start with <manifest> tag");
Adam Lesinski282e1812014-01-23 18:17:42 -08001183 goto bail;
1184 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001185 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -07001186 printf("package: name='%s' ",
1187 ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001188 int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR,
1189 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001190 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001191 SourcePos(manifestFile, tree.getLineNumber()).error(
1192 "ERROR getting 'android:versionCode' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001193 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001194 goto bail;
1195 }
1196 if (versionCode > 0) {
1197 printf("versionCode='%d' ", versionCode);
1198 } else {
1199 printf("versionCode='' ");
1200 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001201 String8 versionName = AaptXml::getResolvedAttribute(res, tree,
1202 VERSION_NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001203 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001204 SourcePos(manifestFile, tree.getLineNumber()).error(
1205 "ERROR getting 'android:versionName' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001206 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001207 goto bail;
1208 }
Adam Lesinski25d35a92014-08-11 09:41:56 -07001209 printf("versionName='%s'",
Maurice Chu2675f762013-10-22 17:33:11 -07001210 ResTable::normalizeForOutput(versionName.string()).string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001211
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001212 String8 splitName = AaptXml::getAttribute(tree, NULL, "split");
Adam Lesinski25d35a92014-08-11 09:41:56 -07001213 if (!splitName.isEmpty()) {
1214 printf(" split='%s'", ResTable::normalizeForOutput(
1215 splitName.string()).string());
1216 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001217
Adam Lesinski5283fab2014-08-29 11:23:55 -07001218 String8 platformVersionName = AaptXml::getAttribute(tree, NULL,
1219 "platformBuildVersionName");
1220 printf(" platformBuildVersionName='%s'", platformVersionName.string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001221 printf("\n");
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001222
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001223 int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
1224 INSTALL_LOCATION_ATTR, &error);
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001225 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001226 SourcePos(manifestFile, tree.getLineNumber()).error(
1227 "ERROR getting 'android:installLocation' attribute: %s",
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001228 error.string());
1229 goto bail;
1230 }
1231
1232 if (installLocation >= 0) {
1233 printf("install-location:'");
1234 switch (installLocation) {
1235 case 0:
1236 printf("auto");
1237 break;
1238 case 1:
1239 printf("internalOnly");
1240 break;
1241 case 2:
1242 printf("preferExternal");
1243 break;
1244 default:
1245 fprintf(stderr, "Invalid installLocation %d\n", installLocation);
1246 goto bail;
1247 }
1248 printf("'\n");
1249 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001250 } else if (depth == 2) {
1251 withinApplication = false;
1252 if (tag == "application") {
1253 withinApplication = true;
1254
1255 String8 label;
1256 const size_t NL = locales.size();
1257 for (size_t i=0; i<NL; i++) {
1258 const char* localeStr = locales[i].string();
1259 assets.setLocale(localeStr != NULL ? localeStr : "");
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001260 String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1261 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001262 if (llabel != "") {
1263 if (localeStr == NULL || strlen(localeStr) == 0) {
1264 label = llabel;
Maurice Chu2675f762013-10-22 17:33:11 -07001265 printf("application-label:'%s'\n",
1266 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001267 } else {
1268 if (label == "") {
1269 label = llabel;
1270 }
1271 printf("application-label-%s:'%s'\n", localeStr,
Maurice Chu2675f762013-10-22 17:33:11 -07001272 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001273 }
1274 }
1275 }
1276
1277 ResTable_config tmpConfig = config;
1278 const size_t ND = densities.size();
1279 for (size_t i=0; i<ND; i++) {
1280 tmpConfig.density = densities[i];
1281 assets.setConfiguration(tmpConfig);
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001282 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1283 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001284 if (icon != "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001285 printf("application-icon-%d:'%s'\n", densities[i],
1286 ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001287 }
1288 }
1289 assets.setConfiguration(config);
1290
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001291 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001292 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001293 SourcePos(manifestFile, tree.getLineNumber()).error(
1294 "ERROR getting 'android:icon' attribute: %s", error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001295 goto bail;
1296 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001297 int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0,
1298 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001299 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001300 SourcePos(manifestFile, tree.getLineNumber()).error(
1301 "ERROR getting 'android:testOnly' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001302 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001303 goto bail;
1304 }
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001305
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001306 String8 banner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1307 &error);
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001308 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001309 SourcePos(manifestFile, tree.getLineNumber()).error(
1310 "ERROR getting 'android:banner' attribute: %s", error.string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001311 goto bail;
1312 }
Maurice Chu2675f762013-10-22 17:33:11 -07001313 printf("application: label='%s' ",
1314 ResTable::normalizeForOutput(label.string()).string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001315 printf("icon='%s'", ResTable::normalizeForOutput(icon.string()).string());
1316 if (banner != "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001317 printf(" banner='%s'",
1318 ResTable::normalizeForOutput(banner.string()).string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001319 }
1320 printf("\n");
Adam Lesinski282e1812014-01-23 18:17:42 -08001321 if (testOnly != 0) {
1322 printf("testOnly='%d'\n", testOnly);
1323 }
1324
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001325 int32_t isGame = AaptXml::getResolvedIntegerAttribute(res, tree,
1326 ISGAME_ATTR, 0, &error);
1327 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001328 SourcePos(manifestFile, tree.getLineNumber()).error(
1329 "ERROR getting 'android:isGame' attribute: %s", error.string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001330 goto bail;
1331 }
1332 if (isGame != 0) {
1333 printf("application-isGame\n");
1334 }
1335
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001336 int32_t debuggable = AaptXml::getResolvedIntegerAttribute(res, tree,
1337 DEBUGGABLE_ATTR, 0, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001338 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001339 SourcePos(manifestFile, tree.getLineNumber()).error(
1340 "ERROR getting 'android:debuggable' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001341 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001342 goto bail;
1343 }
1344 if (debuggable != 0) {
1345 printf("application-debuggable\n");
1346 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07001347
1348 // We must search by name because the multiArch flag hasn't been API
1349 // frozen yet.
1350 int32_t multiArchIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
1351 "multiArch");
1352 if (multiArchIndex >= 0) {
1353 Res_value value;
1354 if (tree.getAttributeValue(multiArchIndex, &value) != NO_ERROR) {
1355 if (value.dataType >= Res_value::TYPE_FIRST_INT &&
1356 value.dataType <= Res_value::TYPE_LAST_INT) {
1357 hasMultiArch = value.data;
1358 }
1359 }
1360 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001361 } else if (tag == "uses-sdk") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001362 int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR,
1363 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001364 if (error != "") {
1365 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001366 String8 name = AaptXml::getResolvedAttribute(res, tree,
1367 MIN_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001368 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001369 SourcePos(manifestFile, tree.getLineNumber()).error(
1370 "ERROR getting 'android:minSdkVersion' attribute: %s",
Adam Lesinski282e1812014-01-23 18:17:42 -08001371 error.string());
1372 goto bail;
1373 }
1374 if (name == "Donut") targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001375 printf("sdkVersion:'%s'\n",
1376 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001377 } else if (code != -1) {
1378 targetSdk = code;
1379 printf("sdkVersion:'%d'\n", code);
1380 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001381 code = AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -08001382 if (code != -1) {
1383 printf("maxSdkVersion:'%d'\n", code);
1384 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001385 code = AaptXml::getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001386 if (error != "") {
1387 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001388 String8 name = AaptXml::getResolvedAttribute(res, tree,
1389 TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001390 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001391 SourcePos(manifestFile, tree.getLineNumber()).error(
1392 "ERROR getting 'android:targetSdkVersion' attribute: %s",
Adam Lesinski282e1812014-01-23 18:17:42 -08001393 error.string());
1394 goto bail;
1395 }
1396 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001397 printf("targetSdkVersion:'%s'\n",
1398 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001399 } else if (code != -1) {
1400 if (targetSdk < code) {
1401 targetSdk = code;
1402 }
1403 printf("targetSdkVersion:'%d'\n", code);
1404 }
1405 } else if (tag == "uses-configuration") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001406 int32_t reqTouchScreen = AaptXml::getIntegerAttribute(tree,
1407 REQ_TOUCH_SCREEN_ATTR, 0);
1408 int32_t reqKeyboardType = AaptXml::getIntegerAttribute(tree,
1409 REQ_KEYBOARD_TYPE_ATTR, 0);
1410 int32_t reqHardKeyboard = AaptXml::getIntegerAttribute(tree,
1411 REQ_HARD_KEYBOARD_ATTR, 0);
1412 int32_t reqNavigation = AaptXml::getIntegerAttribute(tree,
1413 REQ_NAVIGATION_ATTR, 0);
1414 int32_t reqFiveWayNav = AaptXml::getIntegerAttribute(tree,
1415 REQ_FIVE_WAY_NAV_ATTR, 0);
Adam Lesinski282e1812014-01-23 18:17:42 -08001416 printf("uses-configuration:");
1417 if (reqTouchScreen != 0) {
1418 printf(" reqTouchScreen='%d'", reqTouchScreen);
1419 }
1420 if (reqKeyboardType != 0) {
1421 printf(" reqKeyboardType='%d'", reqKeyboardType);
1422 }
1423 if (reqHardKeyboard != 0) {
1424 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1425 }
1426 if (reqNavigation != 0) {
1427 printf(" reqNavigation='%d'", reqNavigation);
1428 }
1429 if (reqFiveWayNav != 0) {
1430 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1431 }
1432 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001433 } else if (tag == "supports-input") {
1434 withinSupportsInput = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001435 } else if (tag == "supports-screens") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001436 smallScreen = AaptXml::getIntegerAttribute(tree,
1437 SMALL_SCREEN_ATTR, 1);
1438 normalScreen = AaptXml::getIntegerAttribute(tree,
1439 NORMAL_SCREEN_ATTR, 1);
1440 largeScreen = AaptXml::getIntegerAttribute(tree,
1441 LARGE_SCREEN_ATTR, 1);
1442 xlargeScreen = AaptXml::getIntegerAttribute(tree,
1443 XLARGE_SCREEN_ATTR, 1);
1444 anyDensity = AaptXml::getIntegerAttribute(tree,
1445 ANY_DENSITY_ATTR, 1);
1446 requiresSmallestWidthDp = AaptXml::getIntegerAttribute(tree,
1447 REQUIRES_SMALLEST_WIDTH_DP_ATTR, 0);
1448 compatibleWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1449 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, 0);
1450 largestWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1451 LARGEST_WIDTH_LIMIT_DP_ATTR, 0);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001452 } else if (tag == "feature-group") {
1453 withinFeatureGroup = true;
1454 FeatureGroup group;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001455 group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001456 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001457 SourcePos(manifestFile, tree.getLineNumber()).error(
1458 "ERROR getting 'android:label' attribute: %s", error.string());
Adam Lesinski2c72b682014-06-24 09:56:01 -07001459 goto bail;
1460 }
1461 featureGroups.add(group);
1462
Adam Lesinski282e1812014-01-23 18:17:42 -08001463 } else if (tag == "uses-feature") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001464 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001465 if (name != "" && error == "") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001466 const char* androidSchema =
1467 "http://schemas.android.com/apk/res/android";
Adam Lesinski282e1812014-01-23 18:17:42 -08001468
Adam Lesinski694d0a72016-04-06 16:12:04 -07001469 int32_t req = AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1,
1470 &error);
1471 if (error != "") {
1472 SourcePos(manifestFile, tree.getLineNumber()).error(
1473 "failed to read attribute 'android:required': %s",
1474 error.string());
1475 goto bail;
1476 }
1477
1478 int32_t version = AaptXml::getIntegerAttribute(tree, androidSchema,
1479 "version", 0, &error);
1480 if (error != "") {
1481 SourcePos(manifestFile, tree.getLineNumber()).error(
1482 "failed to read attribute 'android:version': %s",
1483 error.string());
1484 goto bail;
1485 }
1486
1487 commonFeatures.features.add(name, Feature(req != 0, version));
Adam Lesinski2c72b682014-06-24 09:56:01 -07001488 if (req) {
1489 addParentFeatures(&commonFeatures, name);
Adam Lesinski282e1812014-01-23 18:17:42 -08001490 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001491 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001492 int vers = AaptXml::getIntegerAttribute(tree,
Adam Lesinski282e1812014-01-23 18:17:42 -08001493 GL_ES_VERSION_ATTR, &error);
1494 if (error == "") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001495 if (vers > commonFeatures.openGLESVersion) {
1496 commonFeatures.openGLESVersion = vers;
1497 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001498 }
1499 }
1500 } else if (tag == "uses-permission") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001501 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001502 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001503 SourcePos(manifestFile, tree.getLineNumber()).error(
1504 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001505 goto bail;
1506 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001507
1508 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001509 SourcePos(manifestFile, tree.getLineNumber()).error(
1510 "ERROR: missing 'android:name' for uses-permission");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001511 goto bail;
1512 }
1513
1514 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, false);
1515
1516 if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1517 hasWriteExternalStoragePermission = true;
1518 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1519 hasReadExternalStoragePermission = true;
1520 } else if (name == "android.permission.READ_PHONE_STATE") {
1521 hasReadPhoneStatePermission = true;
1522 } else if (name == "android.permission.READ_CONTACTS") {
1523 hasReadContactsPermission = true;
1524 } else if (name == "android.permission.WRITE_CONTACTS") {
1525 hasWriteContactsPermission = true;
1526 } else if (name == "android.permission.READ_CALL_LOG") {
1527 hasReadCallLogPermission = true;
1528 } else if (name == "android.permission.WRITE_CALL_LOG") {
1529 hasWriteCallLogPermission = true;
1530 }
1531
1532 printUsesPermission(name,
1533 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
1534 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
1535
1536 } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
1537 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1538 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001539 SourcePos(manifestFile, tree.getLineNumber()).error(
1540 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001541 goto bail;
1542 }
1543
1544 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001545 SourcePos(manifestFile, tree.getLineNumber()).error(
1546 "ERROR: missing 'android:name' for uses-permission-sdk-23");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001547 goto bail;
1548 }
1549
1550 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, true);
1551
1552 printUsesPermissionSdk23(
1553 name, AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
1554
Adam Lesinski282e1812014-01-23 18:17:42 -08001555 } else if (tag == "uses-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001556 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001557 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001558 printf("uses-package:'%s'\n",
1559 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001560 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001561 SourcePos(manifestFile, tree.getLineNumber()).error(
1562 "ERROR getting 'android:name' attribute: %s", error.string());
1563 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001564 }
1565 } else if (tag == "original-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001566 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001567 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001568 printf("original-package:'%s'\n",
1569 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001570 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001571 SourcePos(manifestFile, tree.getLineNumber()).error(
1572 "ERROR getting 'android:name' attribute: %s", error.string());
1573 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001574 }
1575 } else if (tag == "supports-gl-texture") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001576 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001577 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001578 printf("supports-gl-texture:'%s'\n",
1579 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001580 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001581 SourcePos(manifestFile, tree.getLineNumber()).error(
1582 "ERROR getting 'android:name' attribute: %s", error.string());
1583 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001584 }
1585 } else if (tag == "compatible-screens") {
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001586 printCompatibleScreens(tree, &error);
1587 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001588 SourcePos(manifestFile, tree.getLineNumber()).error(
1589 "ERROR getting compatible screens: %s", error.string());
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001590 goto bail;
1591 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001592 depth--;
1593 } else if (tag == "package-verifier") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001594 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001595 if (name != "" && error == "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001596 String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR,
1597 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001598 if (publicKey != "" && error == "") {
1599 printf("package-verifier: name='%s' publicKey='%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001600 ResTable::normalizeForOutput(name.string()).string(),
1601 ResTable::normalizeForOutput(publicKey.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001602 }
1603 }
1604 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001605 } else if (depth == 3) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001606 withinActivity = false;
1607 withinReceiver = false;
1608 withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001609 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001610 hasIntentFilter = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001611 hasMetaHostPaymentCategory = false;
1612 hasMetaOffHostPaymentCategory = false;
1613 hasBindDeviceAdminPermission = false;
1614 hasBindInputMethodPermission = false;
1615 hasBindAccessibilityServicePermission = false;
1616 hasBindPrintServicePermission = false;
1617 hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001618 hasRequiredSafAttributes = false;
1619 hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001620 hasBindDreamServicePermission = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001621 if (withinApplication) {
1622 if(tag == "activity") {
1623 withinActivity = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001624 activityName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001625 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001626 SourcePos(manifestFile, tree.getLineNumber()).error(
1627 "ERROR getting 'android:name' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001628 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001629 goto bail;
1630 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001631
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001632 activityLabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1633 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001634 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001635 SourcePos(manifestFile, tree.getLineNumber()).error(
1636 "ERROR getting 'android:label' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001637 error.string());
1638 goto bail;
1639 }
1640
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001641 activityIcon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1642 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001643 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001644 SourcePos(manifestFile, tree.getLineNumber()).error(
1645 "ERROR getting 'android:icon' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001646 error.string());
1647 goto bail;
1648 }
1649
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001650 activityBanner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1651 &error);
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001652 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001653 SourcePos(manifestFile, tree.getLineNumber()).error(
1654 "ERROR getting 'android:banner' attribute: %s",
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001655 error.string());
1656 goto bail;
1657 }
1658
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001659 int32_t orien = AaptXml::getResolvedIntegerAttribute(res, tree,
Michael Wrightec4fdec2013-09-06 16:50:52 -07001660 SCREEN_ORIENTATION_ATTR, &error);
1661 if (error == "") {
1662 if (orien == 0 || orien == 6 || orien == 8) {
1663 // Requests landscape, sensorLandscape, or reverseLandscape.
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001664 addImpliedFeature(&impliedFeatures,
1665 "android.hardware.screen.landscape",
1666 "one or more activities have specified a "
1667 "landscape orientation",
1668 false);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001669 } else if (orien == 1 || orien == 7 || orien == 9) {
1670 // Requests portrait, sensorPortrait, or reversePortrait.
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001671 addImpliedFeature(&impliedFeatures,
1672 "android.hardware.screen.portrait",
1673 "one or more activities have specified a "
1674 "portrait orientation",
1675 false);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001676 }
1677 }
1678 } else if (tag == "uses-library") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001679 String8 libraryName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001680 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001681 SourcePos(manifestFile, tree.getLineNumber()).error(
Michael Wrightec4fdec2013-09-06 16:50:52 -07001682 "ERROR getting 'android:name' attribute for uses-library"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001683 " %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001684 goto bail;
1685 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001686 int req = AaptXml::getIntegerAttribute(tree,
1687 REQUIRED_ATTR, 1);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001688 printf("uses-library%s:'%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001689 req ? "" : "-not-required", ResTable::normalizeForOutput(
1690 libraryName.string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001691 } else if (tag == "receiver") {
1692 withinReceiver = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001693 receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001694
1695 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001696 SourcePos(manifestFile, tree.getLineNumber()).error(
Michael Wrightec4fdec2013-09-06 16:50:52 -07001697 "ERROR getting 'android:name' attribute for receiver:"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001698 " %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001699 goto bail;
1700 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001701
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001702 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1703 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001704 if (error == "") {
1705 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1706 hasBindDeviceAdminPermission = true;
1707 }
1708 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001709 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001710 "ERROR getting 'android:permission' attribute for"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001711 " receiver '%s': %s",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001712 receiverName.string(), error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001713 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001714 } else if (tag == "service") {
1715 withinService = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001716 serviceName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001717
1718 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001719 SourcePos(manifestFile, tree.getLineNumber()).error(
1720 "ERROR getting 'android:name' attribute for "
1721 "service:%s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001722 goto bail;
1723 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001724
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001725 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1726 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001727 if (error == "") {
1728 if (permission == "android.permission.BIND_INPUT_METHOD") {
1729 hasBindInputMethodPermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001730 } else if (permission ==
1731 "android.permission.BIND_ACCESSIBILITY_SERVICE") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001732 hasBindAccessibilityServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001733 } else if (permission ==
1734 "android.permission.BIND_PRINT_SERVICE") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001735 hasBindPrintServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001736 } else if (permission ==
1737 "android.permission.BIND_NFC_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07001738 hasBindNfcServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001739 } else if (permission ==
1740 "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001741 hasBindNotificationListenerServicePermission = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001742 } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
1743 hasBindDreamServicePermission = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001744 }
1745 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001746 SourcePos(manifestFile, tree.getLineNumber()).error(
1747 "ERROR getting 'android:permission' attribute for "
1748 "service '%s': %s", serviceName.string(), error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001749 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001750 } else if (tag == "provider") {
1751 withinProvider = true;
1752
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001753 bool exported = AaptXml::getResolvedIntegerAttribute(res, tree,
1754 EXPORTED_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001755 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001756 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001757 "ERROR getting 'android:exported' attribute for provider:"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001758 " %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001759 goto bail;
1760 }
1761
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001762 bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute(
1763 res, tree, GRANT_URI_PERMISSIONS_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001764 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001765 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001766 "ERROR getting 'android:grantUriPermissions' attribute for "
Adam Lesinski10de3af12016-07-13 10:14:03 -07001767 "provider: %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001768 goto bail;
1769 }
1770
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001771 String8 permission = AaptXml::getResolvedAttribute(res, tree,
1772 PERMISSION_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001773 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001774 SourcePos(manifestFile, tree.getLineNumber()).error(
1775 "ERROR getting 'android:permission' attribute for "
1776 "provider: %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001777 goto bail;
1778 }
1779
1780 hasRequiredSafAttributes |= exported && grantUriPermissions &&
1781 permission == "android.permission.MANAGE_DOCUMENTS";
1782
Michael Wrightec4fdec2013-09-06 16:50:52 -07001783 } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001784 String8 metaDataName = AaptXml::getResolvedAttribute(res, tree,
1785 NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001786 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001787 SourcePos(manifestFile, tree.getLineNumber()).error(
1788 "ERROR getting 'android:name' attribute for "
1789 "meta-data: %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001790 goto bail;
1791 }
Maurice Chu2675f762013-10-22 17:33:11 -07001792 printf("meta-data: name='%s' ",
1793 ResTable::normalizeForOutput(metaDataName.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001794 printResolvedResourceAttribute(res, tree, VALUE_ATTR, String8("value"),
Maurice Chu76327312013-10-16 18:28:46 -07001795 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001796 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001797 // Try looking for a RESOURCE_ATTR
1798 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001799 printResolvedResourceAttribute(res, tree, RESOURCE_ATTR,
Maurice Chu76327312013-10-16 18:28:46 -07001800 String8("resource"), &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001801 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001802 SourcePos(manifestFile, tree.getLineNumber()).error(
1803 "ERROR getting 'android:value' or "
Maurice Chu76327312013-10-16 18:28:46 -07001804 "'android:resource' attribute for "
Adam Lesinski10de3af12016-07-13 10:14:03 -07001805 "meta-data: %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001806 goto bail;
1807 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001808 }
Maurice Chu76327312013-10-16 18:28:46 -07001809 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001810 } else if (withinSupportsInput && tag == "input-type") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001811 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001812 if (name != "" && error == "") {
1813 supportedInput.add(name);
1814 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001815 SourcePos(manifestFile, tree.getLineNumber()).error(
1816 "ERROR getting 'android:name' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001817 error.string());
1818 goto bail;
1819 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001820 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001821 } else if (withinFeatureGroup && tag == "uses-feature") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001822 const String8 androidSchema("http://schemas.android.com/apk/res/android");
Adam Lesinski2c72b682014-06-24 09:56:01 -07001823 FeatureGroup& top = featureGroups.editTop();
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001824
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001825 String8 name = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001826 if (name != "" && error == "") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001827 Feature feature(true);
1828
1829 int32_t featureVers = AaptXml::getIntegerAttribute(
1830 tree, androidSchema.string(), "version", 0, &error);
1831 if (error == "") {
1832 feature.version = featureVers;
1833 } else {
1834 SourcePos(manifestFile, tree.getLineNumber()).error(
1835 "failed to read attribute 'android:version': %s",
1836 error.string());
1837 goto bail;
1838 }
1839
1840 top.features.add(name, feature);
Adam Lesinskid3edfde2014-08-08 17:32:44 -07001841 addParentFeatures(&top, name);
Adam Lesinski694d0a72016-04-06 16:12:04 -07001842
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001843 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001844 int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR,
1845 &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001846 if (error == "") {
1847 if (vers > top.openGLESVersion) {
1848 top.openGLESVersion = vers;
1849 }
1850 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001851 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001852 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001853 } else if (depth == 4) {
1854 if (tag == "intent-filter") {
1855 hasIntentFilter = true;
1856 withinIntentFilter = true;
1857 actMainActivity = false;
1858 actWidgetReceivers = false;
1859 actImeService = false;
1860 actWallpaperService = false;
1861 actAccessibilityService = false;
1862 actPrintService = false;
1863 actDeviceAdminEnabled = false;
1864 actHostApduService = false;
1865 actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001866 actDocumentsProvider = false;
1867 actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001868 actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001869 actCamera = false;
1870 actCameraSecure = false;
1871 catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001872 } else if (withinService && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001873 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001874 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001875 SourcePos(manifestFile, tree.getLineNumber()).error(
1876 "ERROR getting 'android:name' attribute for "
1877 "meta-data tag in service '%s': %s", serviceName.string(),
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001878 error.string());
Adam Lesinski94fc9122013-09-30 17:16:09 -07001879 goto bail;
1880 }
1881
1882 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1883 name == "android.nfc.cardemulation.off_host_apdu_service") {
1884 bool offHost = true;
1885 if (name == "android.nfc.cardemulation.host_apdu_service") {
1886 offHost = false;
1887 }
1888
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001889 String8 xmlPath = AaptXml::getResolvedAttribute(res, tree,
1890 RESOURCE_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:resource' attribute for "
1894 "meta-data tag in service '%s': %s",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001895 serviceName.string(), error.string());
Adam Lesinski94fc9122013-09-30 17:16:09 -07001896 goto bail;
1897 }
1898
1899 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1900 offHost, &error);
1901 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001902 SourcePos(manifestFile, tree.getLineNumber()).error(
1903 "ERROR getting AID category for service '%s'",
Adam Lesinski94fc9122013-09-30 17:16:09 -07001904 serviceName.string());
1905 goto bail;
1906 }
1907
1908 const size_t catLen = categories.size();
1909 for (size_t i = 0; i < catLen; i++) {
1910 bool paymentCategory = (categories[i] == "payment");
1911 if (offHost) {
1912 hasMetaOffHostPaymentCategory |= paymentCategory;
1913 } else {
1914 hasMetaHostPaymentCategory |= paymentCategory;
1915 }
1916 }
1917 }
1918 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001919 } else if ((depth == 5) && withinIntentFilter) {
1920 String8 action;
1921 if (tag == "action") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001922 action = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001923 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001924 SourcePos(manifestFile, tree.getLineNumber()).error(
1925 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001926 goto bail;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001927 }
1928
Adam Lesinskia5018c92013-09-30 16:23:15 -07001929 if (withinActivity) {
1930 if (action == "android.intent.action.MAIN") {
1931 isMainActivity = true;
1932 actMainActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001933 } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
1934 action == "android.media.action.VIDEO_CAMERA") {
1935 actCamera = true;
1936 } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
1937 actCameraSecure = true;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001938 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001939 } else if (withinReceiver) {
1940 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1941 actWidgetReceivers = true;
1942 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1943 actDeviceAdminEnabled = true;
1944 }
1945 } else if (withinService) {
1946 if (action == "android.view.InputMethod") {
1947 actImeService = true;
1948 } else if (action == "android.service.wallpaper.WallpaperService") {
1949 actWallpaperService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001950 } else if (action ==
1951 "android.accessibilityservice.AccessibilityService") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001952 actAccessibilityService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001953 } else if (action =="android.printservice.PrintService") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001954 actPrintService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001955 } else if (action ==
1956 "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07001957 actHostApduService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001958 } else if (action ==
1959 "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07001960 actOffHostApduService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001961 } else if (action ==
1962 "android.service.notification.NotificationListenerService") {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001963 actNotificationListenerService = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001964 } else if (action == "android.service.dreams.DreamService") {
1965 actDreamService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001966 }
1967 } else if (withinProvider) {
1968 if (action == "android.content.action.DOCUMENTS_PROVIDER") {
1969 actDocumentsProvider = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001970 }
1971 }
1972 if (action == "android.intent.action.SEARCH") {
1973 isSearchable = true;
1974 }
1975 }
1976
1977 if (tag == "category") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001978 String8 category = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001979 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001980 SourcePos(manifestFile, tree.getLineNumber()).error(
1981 "ERROR getting 'name' attribute: %s", error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001982 goto bail;
1983 }
1984 if (withinActivity) {
1985 if (category == "android.intent.category.LAUNCHER") {
1986 isLauncherActivity = true;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001987 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
1988 isLeanbackLauncherActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001989 } else if (category == "android.intent.category.HOME") {
1990 catLauncher = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001991 }
1992 }
1993 }
1994 }
1995 }
1996
1997 // Pre-1.6 implicitly granted permission compatibility logic
1998 if (targetSdk < 4) {
1999 if (!hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002000 printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
2001 printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
2002 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002003 hasWriteExternalStoragePermission = true;
2004 }
2005 if (!hasReadPhoneStatePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002006 printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
2007 printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
2008 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002009 }
2010 }
2011
2012 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
2013 // force them to always take READ_EXTERNAL_STORAGE as well. We always
2014 // do this (regardless of target API version) because we can't have
2015 // an app with write permission but not read permission.
2016 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002017 printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"));
2018 printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
2019 String8("requested WRITE_EXTERNAL_STORAGE"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002020 }
2021
2022 // Pre-JellyBean call log permission compatibility.
2023 if (targetSdk < 16) {
2024 if (!hasReadCallLogPermission && hasReadContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002025 printUsesPermission(String8("android.permission.READ_CALL_LOG"));
2026 printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
2027 String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002028 }
2029 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002030 printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
2031 printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
2032 String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002033 }
2034 }
2035
Adam Lesinski2c72b682014-06-24 09:56:01 -07002036 addImpliedFeature(&impliedFeatures, "android.hardware.touchscreen",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002037 "default feature for all apps", false);
Adam Lesinski2c72b682014-06-24 09:56:01 -07002038
2039 const size_t numFeatureGroups = featureGroups.size();
2040 if (numFeatureGroups == 0) {
2041 // If no <feature-group> tags were defined, apply auto-implied features.
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002042 printDefaultFeatureGroup(commonFeatures, impliedFeatures);
Adam Lesinski2c72b682014-06-24 09:56:01 -07002043
2044 } else {
2045 // <feature-group> tags are defined, so we ignore implied features and
2046 for (size_t i = 0; i < numFeatureGroups; i++) {
2047 FeatureGroup& grp = featureGroups.editItemAt(i);
2048
Adam Lesinskid7a94da2014-07-25 14:38:54 -07002049 if (commonFeatures.openGLESVersion > grp.openGLESVersion) {
2050 grp.openGLESVersion = commonFeatures.openGLESVersion;
2051 }
2052
Adam Lesinski2c72b682014-06-24 09:56:01 -07002053 // Merge the features defined in the top level (not inside a <feature-group>)
2054 // with this feature group.
2055 const size_t numCommonFeatures = commonFeatures.features.size();
2056 for (size_t j = 0; j < numCommonFeatures; j++) {
2057 if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07002058 grp.features.add(commonFeatures.features.keyAt(j),
2059 commonFeatures.features[j]);
Adam Lesinski2c72b682014-06-24 09:56:01 -07002060 }
2061 }
2062
Adam Lesinski73a05112014-12-08 12:53:17 -08002063 if (!grp.features.isEmpty()) {
Adam Lesinski2c72b682014-06-24 09:56:01 -07002064 printFeatureGroup(grp);
Adam Lesinski282e1812014-01-23 18:17:42 -08002065 }
2066 }
2067 }
2068
Adam Lesinski282e1812014-01-23 18:17:42 -08002069
Adam Lesinski282e1812014-01-23 18:17:42 -08002070 if (hasWidgetReceivers) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002071 printComponentPresence("app-widget");
Adam Lesinski282e1812014-01-23 18:17:42 -08002072 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002073 if (hasDeviceAdminReceiver) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002074 printComponentPresence("device-admin");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002075 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002076 if (hasImeService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002077 printComponentPresence("ime");
Adam Lesinski282e1812014-01-23 18:17:42 -08002078 }
2079 if (hasWallpaperService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002080 printComponentPresence("wallpaper");
Adam Lesinski282e1812014-01-23 18:17:42 -08002081 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002082 if (hasAccessibilityService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002083 printComponentPresence("accessibility");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002084 }
2085 if (hasPrintService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002086 printComponentPresence("print-service");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002087 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07002088 if (hasPaymentService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002089 printComponentPresence("payment");
2090 }
2091 if (isSearchable) {
2092 printComponentPresence("search");
2093 }
2094 if (hasDocumentsProvider) {
2095 printComponentPresence("document-provider");
2096 }
2097 if (hasLauncher) {
2098 printComponentPresence("launcher");
2099 }
2100 if (hasNotificationListenerService) {
2101 printComponentPresence("notification-listener");
2102 }
John Spurlockeb8d1be2014-06-25 17:46:15 -04002103 if (hasDreamService) {
2104 printComponentPresence("dream");
2105 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002106 if (hasCameraActivity) {
2107 printComponentPresence("camera");
2108 }
2109 if (hasCameraSecureActivity) {
2110 printComponentPresence("camera-secure");
2111 }
2112
2113 if (hasMainActivity) {
2114 printf("main\n");
Adam Lesinski94fc9122013-09-30 17:16:09 -07002115 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002116 if (hasOtherActivities) {
2117 printf("other-activities\n");
2118 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002119 if (hasOtherReceivers) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002120 printf("other-receivers\n");
2121 }
2122 if (hasOtherServices) {
2123 printf("other-services\n");
2124 }
2125
2126 // For modern apps, if screen size buckets haven't been specified
2127 // but the new width ranges have, then infer the buckets from them.
2128 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
2129 && requiresSmallestWidthDp > 0) {
2130 int compatWidth = compatibleWidthLimitDp;
2131 if (compatWidth <= 0) {
2132 compatWidth = requiresSmallestWidthDp;
2133 }
2134 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
2135 smallScreen = -1;
2136 } else {
2137 smallScreen = 0;
2138 }
2139 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
2140 normalScreen = -1;
2141 } else {
2142 normalScreen = 0;
2143 }
2144 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
2145 largeScreen = -1;
2146 } else {
2147 largeScreen = 0;
2148 }
2149 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
2150 xlargeScreen = -1;
2151 } else {
2152 xlargeScreen = 0;
2153 }
2154 }
2155
2156 // Determine default values for any unspecified screen sizes,
2157 // based on the target SDK of the package. As of 4 (donut)
2158 // the screen size support was introduced, so all default to
2159 // enabled.
2160 if (smallScreen > 0) {
2161 smallScreen = targetSdk >= 4 ? -1 : 0;
2162 }
2163 if (normalScreen > 0) {
2164 normalScreen = -1;
2165 }
2166 if (largeScreen > 0) {
2167 largeScreen = targetSdk >= 4 ? -1 : 0;
2168 }
2169 if (xlargeScreen > 0) {
2170 // Introduced in Gingerbread.
2171 xlargeScreen = targetSdk >= 9 ? -1 : 0;
2172 }
2173 if (anyDensity > 0) {
2174 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
2175 || compatibleWidthLimitDp > 0) ? -1 : 0;
2176 }
2177 printf("supports-screens:");
2178 if (smallScreen != 0) {
2179 printf(" 'small'");
2180 }
2181 if (normalScreen != 0) {
2182 printf(" 'normal'");
2183 }
2184 if (largeScreen != 0) {
2185 printf(" 'large'");
2186 }
2187 if (xlargeScreen != 0) {
2188 printf(" 'xlarge'");
2189 }
2190 printf("\n");
2191 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
2192 if (requiresSmallestWidthDp > 0) {
2193 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
2194 }
2195 if (compatibleWidthLimitDp > 0) {
2196 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
2197 }
2198 if (largestWidthLimitDp > 0) {
2199 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
2200 }
2201
2202 printf("locales:");
2203 const size_t NL = locales.size();
2204 for (size_t i=0; i<NL; i++) {
2205 const char* localeStr = locales[i].string();
2206 if (localeStr == NULL || strlen(localeStr) == 0) {
2207 localeStr = "--_--";
2208 }
2209 printf(" '%s'", localeStr);
2210 }
2211 printf("\n");
2212
2213 printf("densities:");
2214 const size_t ND = densities.size();
2215 for (size_t i=0; i<ND; i++) {
2216 printf(" '%d'", densities[i]);
2217 }
2218 printf("\n");
2219
2220 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
2221 if (dir != NULL) {
2222 if (dir->getFileCount() > 0) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002223 SortedVector<String8> architectures;
Adam Lesinski282e1812014-01-23 18:17:42 -08002224 for (size_t i=0; i<dir->getFileCount(); i++) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002225 architectures.add(ResTable::normalizeForOutput(
2226 dir->getFileName(i).string()));
Adam Lesinski282e1812014-01-23 18:17:42 -08002227 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07002228
2229 bool outputAltNativeCode = false;
2230 // A multiArch package is one that contains 64-bit and
2231 // 32-bit versions of native code and expects 3rd-party
2232 // apps to load these native code libraries. Since most
2233 // 64-bit systems also support 32-bit apps, the apps
2234 // loading this multiArch package's code may be either
2235 // 32-bit or 64-bit.
2236 if (hasMultiArch) {
2237 // If this is a multiArch package, report the 64-bit
2238 // version only. Then as a separate entry, report the
2239 // rest.
2240 //
2241 // If we report the 32-bit architecture, this APK will
2242 // be installed on a 32-bit device, causing a large waste
2243 // of bandwidth and disk space. This assumes that
2244 // the developer of the multiArch package has also
2245 // made a version that is 32-bit only.
2246 String8 intel64("x86_64");
2247 String8 arm64("arm64-v8a");
2248 ssize_t index = architectures.indexOf(intel64);
2249 if (index < 0) {
2250 index = architectures.indexOf(arm64);
2251 }
2252
2253 if (index >= 0) {
2254 printf("native-code: '%s'\n", architectures[index].string());
2255 architectures.removeAt(index);
2256 outputAltNativeCode = true;
2257 }
2258 }
2259
2260 const size_t archCount = architectures.size();
2261 if (archCount > 0) {
2262 if (outputAltNativeCode) {
2263 printf("alt-");
2264 }
2265 printf("native-code:");
2266 for (size_t i = 0; i < archCount; i++) {
2267 printf(" '%s'", architectures[i].string());
2268 }
2269 printf("\n");
2270 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002271 }
2272 delete dir;
2273 }
2274 } else if (strcmp("badger", option) == 0) {
2275 printf("%s", CONSOLE_DATA);
2276 } else if (strcmp("configurations", option) == 0) {
2277 Vector<ResTable_config> configs;
2278 res.getConfigurations(&configs);
2279 const size_t N = configs.size();
2280 for (size_t i=0; i<N; i++) {
2281 printf("%s\n", configs[i].toString().string());
2282 }
2283 } else {
2284 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2285 goto bail;
2286 }
2287 }
2288
2289 result = NO_ERROR;
2290
2291bail:
Adam Lesinski10de3af12016-07-13 10:14:03 -07002292 if (SourcePos::hasErrors()) {
2293 SourcePos::printErrors(stderr);
2294 }
2295
Adam Lesinski282e1812014-01-23 18:17:42 -08002296 if (asset) {
2297 delete asset;
2298 }
2299 return (result != NO_ERROR);
2300}
2301
2302
2303/*
2304 * Handle the "add" command, which wants to add files to a new or
2305 * pre-existing archive.
2306 */
2307int doAdd(Bundle* bundle)
2308{
2309 ZipFile* zip = NULL;
2310 status_t result = UNKNOWN_ERROR;
2311 const char* zipFileName;
2312
2313 if (bundle->getUpdate()) {
2314 /* avoid confusion */
2315 fprintf(stderr, "ERROR: can't use '-u' with add\n");
2316 goto bail;
2317 }
2318
2319 if (bundle->getFileSpecCount() < 1) {
2320 fprintf(stderr, "ERROR: must specify zip file name\n");
2321 goto bail;
2322 }
2323 zipFileName = bundle->getFileSpecEntry(0);
2324
2325 if (bundle->getFileSpecCount() < 2) {
2326 fprintf(stderr, "NOTE: nothing to do\n");
2327 goto bail;
2328 }
2329
2330 zip = openReadWrite(zipFileName, true);
2331 if (zip == NULL) {
2332 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2333 goto bail;
2334 }
2335
2336 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2337 const char* fileName = bundle->getFileSpecEntry(i);
2338
2339 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
2340 printf(" '%s'... (from gzip)\n", fileName);
2341 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
2342 } else {
2343 if (bundle->getJunkPath()) {
2344 String8 storageName = String8(fileName).getPathLeaf();
Maurice Chu2675f762013-10-22 17:33:11 -07002345 printf(" '%s' as '%s'...\n", fileName,
2346 ResTable::normalizeForOutput(storageName.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08002347 result = zip->add(fileName, storageName.string(),
2348 bundle->getCompressionMethod(), NULL);
2349 } else {
2350 printf(" '%s'...\n", fileName);
2351 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2352 }
2353 }
2354 if (result != NO_ERROR) {
2355 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2356 if (result == NAME_NOT_FOUND) {
2357 fprintf(stderr, ": file not found\n");
2358 } else if (result == ALREADY_EXISTS) {
2359 fprintf(stderr, ": already exists in archive\n");
2360 } else {
2361 fprintf(stderr, "\n");
2362 }
2363 goto bail;
2364 }
2365 }
2366
2367 result = NO_ERROR;
2368
2369bail:
2370 delete zip;
2371 return (result != NO_ERROR);
2372}
2373
2374
2375/*
2376 * Delete files from an existing archive.
2377 */
2378int doRemove(Bundle* bundle)
2379{
2380 ZipFile* zip = NULL;
2381 status_t result = UNKNOWN_ERROR;
2382 const char* zipFileName;
2383
2384 if (bundle->getFileSpecCount() < 1) {
2385 fprintf(stderr, "ERROR: must specify zip file name\n");
2386 goto bail;
2387 }
2388 zipFileName = bundle->getFileSpecEntry(0);
2389
2390 if (bundle->getFileSpecCount() < 2) {
2391 fprintf(stderr, "NOTE: nothing to do\n");
2392 goto bail;
2393 }
2394
2395 zip = openReadWrite(zipFileName, false);
2396 if (zip == NULL) {
2397 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2398 zipFileName);
2399 goto bail;
2400 }
2401
2402 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2403 const char* fileName = bundle->getFileSpecEntry(i);
2404 ZipEntry* entry;
2405
2406 entry = zip->getEntryByName(fileName);
2407 if (entry == NULL) {
2408 printf(" '%s' NOT FOUND\n", fileName);
2409 continue;
2410 }
2411
2412 result = zip->remove(entry);
2413
2414 if (result != NO_ERROR) {
2415 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2416 bundle->getFileSpecEntry(i), zipFileName);
2417 goto bail;
2418 }
2419 }
2420
2421 /* update the archive */
2422 zip->flush();
2423
2424bail:
2425 delete zip;
2426 return (result != NO_ERROR);
2427}
2428
Adam Lesinski3921e872014-05-13 10:56:25 -07002429static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002430 const size_t numDirs = dir->getDirs().size();
2431 for (size_t i = 0; i < numDirs; i++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002432 bool ignore = ignoreConfig;
2433 const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2434 const char* dirStr = subDir->getLeaf().string();
2435 if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2436 ignore = true;
2437 }
2438 status_t err = addResourcesToBuilder(subDir, builder, ignore);
Adam Lesinskifab50872014-04-16 14:40:42 -07002439 if (err != NO_ERROR) {
2440 return err;
2441 }
2442 }
2443
2444 const size_t numFiles = dir->getFiles().size();
2445 for (size_t i = 0; i < numFiles; i++) {
2446 sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2447 const size_t numConfigs = gp->getFiles().size();
2448 for (size_t j = 0; j < numConfigs; j++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002449 status_t err = NO_ERROR;
2450 if (ignoreConfig) {
2451 err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2452 } else {
2453 err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2454 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002455 if (err != NO_ERROR) {
2456 fprintf(stderr, "Failed to add %s (%s) to builder.\n",
2457 gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
2458 return err;
2459 }
2460 }
2461 }
2462 return NO_ERROR;
2463}
2464
2465static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2466 if (split->isBase()) {
2467 return original;
2468 }
2469
2470 String8 ext(original.getPathExtension());
2471 if (ext == String8(".apk")) {
2472 return String8::format("%s_%s%s",
2473 original.getBasePath().string(),
2474 split->getDirectorySafeName().string(),
2475 ext.string());
2476 }
2477
2478 return String8::format("%s_%s", original.string(),
2479 split->getDirectorySafeName().string());
2480}
Adam Lesinski282e1812014-01-23 18:17:42 -08002481
2482/*
2483 * Package up an asset directory and associated application files.
2484 */
2485int doPackage(Bundle* bundle)
2486{
2487 const char* outputAPKFile;
2488 int retVal = 1;
2489 status_t err;
2490 sp<AaptAssets> assets;
2491 int N;
2492 FILE* fp;
2493 String8 dependencyFile;
Adam Lesinskifab50872014-04-16 14:40:42 -07002494 sp<ApkBuilder> builder;
Adam Lesinski282e1812014-01-23 18:17:42 -08002495
Anton Krumina2ef5c02014-03-12 14:46:44 -07002496 // -c en_XA or/and ar_XB means do pseudolocalization
Adam Lesinskifab50872014-04-16 14:40:42 -07002497 sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2498 err = configFilter->parse(bundle->getConfigurations());
Adam Lesinski282e1812014-01-23 18:17:42 -08002499 if (err != NO_ERROR) {
2500 goto bail;
2501 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002502 if (configFilter->containsPseudo()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002503 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2504 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002505 if (configFilter->containsPseudoBidi()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002506 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
Adam Lesinski282e1812014-01-23 18:17:42 -08002507 }
2508
2509 N = bundle->getFileSpecCount();
2510 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
Adam Lesinski09384302014-01-22 16:07:42 -08002511 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002512 fprintf(stderr, "ERROR: no input files\n");
2513 goto bail;
2514 }
2515
2516 outputAPKFile = bundle->getOutputAPKFile();
2517
2518 // Make sure the filenames provided exist and are of the appropriate type.
2519 if (outputAPKFile) {
2520 FileType type;
2521 type = getFileType(outputAPKFile);
2522 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2523 fprintf(stderr,
2524 "ERROR: output file '%s' exists but is not regular file\n",
2525 outputAPKFile);
2526 goto bail;
2527 }
2528 }
2529
2530 // Load the assets.
2531 assets = new AaptAssets();
2532
2533 // Set up the resource gathering in assets if we're going to generate
2534 // dependency files. Every time we encounter a resource while slurping
2535 // the tree, we'll add it to these stores so we have full resource paths
2536 // to write to a dependency file.
2537 if (bundle->getGenDependencies()) {
2538 sp<FilePathStore> resPathStore = new FilePathStore;
2539 assets->setFullResPaths(resPathStore);
2540 sp<FilePathStore> assetPathStore = new FilePathStore;
2541 assets->setFullAssetPaths(assetPathStore);
2542 }
2543
2544 err = assets->slurpFromArgs(bundle);
2545 if (err < 0) {
2546 goto bail;
2547 }
2548
2549 if (bundle->getVerbose()) {
2550 assets->print(String8());
2551 }
2552
Adam Lesinskifab50872014-04-16 14:40:42 -07002553 // Create the ApkBuilder, which will collect the compiled files
2554 // to write to the final APK (or sets of APKs if we are building
2555 // a Split APK.
2556 builder = new ApkBuilder(configFilter);
2557
2558 // If we are generating a Split APK, find out which configurations to split on.
2559 if (bundle->getSplitConfigurations().size() > 0) {
2560 const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2561 const size_t numSplits = splitStrs.size();
2562 for (size_t i = 0; i < numSplits; i++) {
2563 std::set<ConfigDescription> configs;
2564 if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2565 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2566 goto bail;
2567 }
2568
2569 err = builder->createSplitForConfigs(configs);
2570 if (err != NO_ERROR) {
2571 goto bail;
2572 }
2573 }
2574 }
2575
Adam Lesinski282e1812014-01-23 18:17:42 -08002576 // If they asked for any fileAs that need to be compiled, do so.
2577 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002578 err = buildResources(bundle, assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002579 if (err != 0) {
2580 goto bail;
2581 }
2582 }
2583
2584 // At this point we've read everything and processed everything. From here
2585 // on out it's just writing output files.
2586 if (SourcePos::hasErrors()) {
2587 goto bail;
2588 }
2589
2590 // Update symbols with information about which ones are needed as Java symbols.
2591 assets->applyJavaSymbols();
2592 if (SourcePos::hasErrors()) {
2593 goto bail;
2594 }
2595
2596 // If we've been asked to generate a dependency file, do that here
2597 if (bundle->getGenDependencies()) {
2598 // If this is the packaging step, generate the dependency file next to
2599 // the output apk (e.g. bin/resources.ap_.d)
2600 if (outputAPKFile) {
2601 dependencyFile = String8(outputAPKFile);
2602 // Add the .d extension to the dependency file.
2603 dependencyFile.append(".d");
2604 } else {
2605 // Else if this is the R.java dependency generation step,
2606 // generate the dependency file in the R.java package subdirectory
2607 // e.g. gen/com/foo/app/R.java.d
2608 dependencyFile = String8(bundle->getRClassDir());
2609 dependencyFile.appendPath("R.java.d");
2610 }
2611 // Make sure we have a clean dependency file to start with
2612 fp = fopen(dependencyFile, "w");
2613 fclose(fp);
2614 }
2615
2616 // Write out R.java constants
2617 if (!assets->havePrivateSymbols()) {
2618 if (bundle->getCustomPackage() == NULL) {
2619 // Write the R.java file into the appropriate class directory
2620 // e.g. gen/com/foo/app/R.java
Adam Lesinski1e4663852014-08-15 14:47:28 -07002621 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002622 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002623 } else {
2624 const String8 customPkg(bundle->getCustomPackage());
Adam Lesinski1e4663852014-08-15 14:47:28 -07002625 err = writeResourceSymbols(bundle, assets, customPkg, true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002626 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002627 }
2628 if (err < 0) {
2629 goto bail;
2630 }
2631 // If we have library files, we're going to write our R.java file into
2632 // the appropriate class directory for those libraries as well.
2633 // e.g. gen/com/foo/app/lib/R.java
2634 if (bundle->getExtraPackages() != NULL) {
2635 // Split on colon
2636 String8 libs(bundle->getExtraPackages());
2637 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2638 while (packageString != NULL) {
2639 // Write the R.java file out with the correct package name
Marcin Kosiba0f3a5a62014-09-11 13:48:48 +01002640 err = writeResourceSymbols(bundle, assets, String8(packageString), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002641 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002642 if (err < 0) {
2643 goto bail;
2644 }
2645 packageString = strtok(NULL, ":");
2646 }
2647 libs.unlockBuffer();
2648 }
2649 } else {
Adam Lesinski1e4663852014-08-15 14:47:28 -07002650 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002651 if (err < 0) {
2652 goto bail;
2653 }
Adam Lesinski1e4663852014-08-15 14:47:28 -07002654 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002655 if (err < 0) {
2656 goto bail;
2657 }
2658 }
2659
2660 // Write out the ProGuard file
2661 err = writeProguardFile(bundle, assets);
2662 if (err < 0) {
2663 goto bail;
2664 }
2665
Rohit Agrawal86229cb2016-04-21 16:29:58 -07002666 // Write out the Main Dex ProGuard file
2667 err = writeMainDexProguardFile(bundle, assets);
2668 if (err < 0) {
2669 goto bail;
2670 }
2671
Adam Lesinski282e1812014-01-23 18:17:42 -08002672 // Write the apk
2673 if (outputAPKFile) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002674 // Gather all resources and add them to the APK Builder. The builder will then
2675 // figure out which Split they belong in.
2676 err = addResourcesToBuilder(assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002677 if (err != NO_ERROR) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002678 goto bail;
2679 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002680
2681 const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2682 const size_t numSplits = splits.size();
2683 for (size_t i = 0; i < numSplits; i++) {
2684 const sp<ApkSplit>& split = splits[i];
2685 String8 outputPath = buildApkName(String8(outputAPKFile), split);
2686 err = writeAPK(bundle, outputPath, split);
2687 if (err != NO_ERROR) {
2688 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2689 goto bail;
2690 }
2691 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002692 }
2693
2694 // If we've been asked to generate a dependency file, we need to finish up here.
2695 // the writeResourceSymbols and writeAPK functions have already written the target
2696 // half of the dependency file, now we need to write the prerequisites. (files that
2697 // the R.java file or .ap_ file depend on)
2698 if (bundle->getGenDependencies()) {
2699 // Now that writeResourceSymbols or writeAPK has taken care of writing
2700 // the targets to our dependency file, we'll write the prereqs
2701 fp = fopen(dependencyFile, "a+");
2702 fprintf(fp, " : ");
2703 bool includeRaw = (outputAPKFile != NULL);
2704 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2705 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2706 // and therefore was not added to our pathstores during slurping
2707 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2708 fclose(fp);
2709 }
2710
2711 retVal = 0;
2712bail:
2713 if (SourcePos::hasErrors()) {
2714 SourcePos::printErrors(stderr);
2715 }
2716 return retVal;
2717}
2718
2719/*
2720 * Do PNG Crunching
2721 * PRECONDITIONS
2722 * -S flag points to a source directory containing drawable* folders
2723 * -C flag points to destination directory. The folder structure in the
2724 * source directory will be mirrored to the destination (cache) directory
2725 *
2726 * POSTCONDITIONS
2727 * Destination directory will be updated to match the PNG files in
Maurice Chu2675f762013-10-22 17:33:11 -07002728 * the source directory.
Adam Lesinski282e1812014-01-23 18:17:42 -08002729 */
2730int doCrunch(Bundle* bundle)
2731{
2732 fprintf(stdout, "Crunching PNG Files in ");
2733 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2734 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2735
2736 updatePreProcessedCache(bundle);
2737
2738 return NO_ERROR;
2739}
2740
2741/*
2742 * Do PNG Crunching on a single flag
2743 * -i points to a single png file
2744 * -o points to a single png output file
2745 */
2746int doSingleCrunch(Bundle* bundle)
2747{
2748 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2749 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2750
2751 String8 input(bundle->getSingleCrunchInputFile());
2752 String8 output(bundle->getSingleCrunchOutputFile());
2753
2754 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2755 // we can't return the status_t as it gets truncate to the lower 8 bits.
2756 return 42;
2757 }
2758
2759 return NO_ERROR;
2760}
2761
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002762int runInDaemonMode(Bundle* bundle) {
2763 std::cout << "Ready" << std::endl;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002764 for (std::string cmd; std::getline(std::cin, cmd);) {
2765 if (cmd == "quit") {
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002766 return NO_ERROR;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002767 } else if (cmd == "s") {
2768 // Two argument crunch
2769 std::string inputFile, outputFile;
2770 std::getline(std::cin, inputFile);
2771 std::getline(std::cin, outputFile);
2772 bundle->setSingleCrunchInputFile(inputFile.c_str());
2773 bundle->setSingleCrunchOutputFile(outputFile.c_str());
2774 std::cout << "Crunching " << inputFile << std::endl;
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002775 if (doSingleCrunch(bundle) != NO_ERROR) {
2776 std::cout << "Error" << std::endl;
2777 }
2778 std::cout << "Done" << std::endl;
2779 } else {
2780 // in case of invalid command, just bail out.
2781 std::cerr << "Unknown command" << std::endl;
2782 return -1;
2783 }
2784 }
2785 return -1;
2786}
2787
Adam Lesinski282e1812014-01-23 18:17:42 -08002788char CONSOLE_DATA[2925] = {
2789 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2790 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2791 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2792 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2793 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2794 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2795 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2796 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2797 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2798 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2799 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2800 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2801 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2802 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2803 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2804 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2805 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2806 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2807 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2808 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2809 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2810 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2811 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2812 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2813 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2814 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2815 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2816 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2817 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2818 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2819 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2820 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2821 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2822 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2823 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2824 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2825 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2826 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2827 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2828 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2829 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2830 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2831 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2832 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2833 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2834 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2835 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2836 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2837 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2838 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2839 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2840 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2841 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2842 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2843 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2844 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2845 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2846 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2847 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2848 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2849 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2850 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2851 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2852 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2853 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2854 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2855 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2856 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2857 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2858 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2859 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2860 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2861 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2862 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2863 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2864 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2865 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2866 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2867 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2868 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2869 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2870 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2871 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2872 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2873 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2874 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2875 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2876 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2877 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2878 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2879 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2880 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2881 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2882 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2883 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2884 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2885 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2886 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2887 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2888 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2889 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2890 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2891 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2892 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2893 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2894 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2895 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2896 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2897 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2898 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2899 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2900 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2901 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2902 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2903 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2904 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2905 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2906 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2907 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2908 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2909 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2910 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2911 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2912 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2913 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2914 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2915 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2916 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2917 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2918 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2919 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2920 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2921 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2922 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2923 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2924 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2925 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2926 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2927 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2928 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2929 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2930 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2931 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2932 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2933 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2934 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2935 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2936 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2937 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2938 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2939 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2940 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2941 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2942 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2943 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2944 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2945 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2946 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2947 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2948 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2949 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2950 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2951 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2952 };