blob: 0e031e74dd2f2e15a1b775e98753be6ce053a135 [file] [log] [blame]
Adam Lesinski282e1812014-01-23 18:17:42 -08001//
2// Copyright 2006 The Android Open Source Project
3//
4// Android Asset Packaging Tool main entry point.
5//
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07006#include "AaptXml.h"
Adam Lesinskifab50872014-04-16 14:40:42 -07007#include "ApkBuilder.h"
Adam Lesinski282e1812014-01-23 18:17:42 -08008#include "Bundle.h"
Adam Lesinski2c72b682014-06-24 09:56:01 -07009#include "Images.h"
10#include "Main.h"
Adam Lesinski282e1812014-01-23 18:17:42 -080011#include "ResourceFilter.h"
12#include "ResourceTable.h"
Adam Lesinski282e1812014-01-23 18:17:42 -080013#include "XMLNode.h"
14
Adam Lesinski282e1812014-01-23 18:17:42 -080015#include <utils/Errors.h>
Adam Lesinski2c72b682014-06-24 09:56:01 -070016#include <utils/KeyedVector.h>
17#include <utils/List.h>
18#include <utils/Log.h>
19#include <utils/SortedVector.h>
20#include <utils/threads.h>
21#include <utils/Vector.h>
Adam Lesinski282e1812014-01-23 18:17:42 -080022
Adam Lesinski282e1812014-01-23 18:17:42 -080023#include <errno.h>
Adam Lesinski2c72b682014-06-24 09:56:01 -070024#include <fcntl.h>
Adam Lesinski282e1812014-01-23 18:17:42 -080025
Jerome Dochez6f1280c2014-09-26 10:21:21 -070026#include <iostream>
27#include <string>
28#include <sstream>
29
Adam Lesinski282e1812014-01-23 18:17:42 -080030using namespace android;
31
Adam Lesinskiad751222014-08-18 14:06:38 -070032#ifndef AAPT_VERSION
33 #define AAPT_VERSION ""
34#endif
35
Adam Lesinski282e1812014-01-23 18:17:42 -080036/*
37 * Show version info. All the cool kids do it.
38 */
39int doVersion(Bundle* bundle)
40{
41 if (bundle->getFileSpecCount() != 0) {
42 printf("(ignoring extra arguments)\n");
43 }
Adam Lesinskiad751222014-08-18 14:06:38 -070044 printf("Android Asset Packaging Tool, v0.2-" AAPT_VERSION "\n");
Adam Lesinski282e1812014-01-23 18:17:42 -080045
46 return 0;
47}
48
49
50/*
51 * Open the file read only. The call fails if the file doesn't exist.
52 *
53 * Returns NULL on failure.
54 */
55ZipFile* openReadOnly(const char* fileName)
56{
57 ZipFile* zip;
58 status_t result;
59
60 zip = new ZipFile;
61 result = zip->open(fileName, ZipFile::kOpenReadOnly);
62 if (result != NO_ERROR) {
63 if (result == NAME_NOT_FOUND) {
64 fprintf(stderr, "ERROR: '%s' not found\n", fileName);
65 } else if (result == PERMISSION_DENIED) {
66 fprintf(stderr, "ERROR: '%s' access denied\n", fileName);
67 } else {
68 fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n",
69 fileName);
70 }
71 delete zip;
72 return NULL;
73 }
74
75 return zip;
76}
77
78/*
79 * Open the file read-write. The file will be created if it doesn't
80 * already exist and "okayToCreate" is set.
81 *
82 * Returns NULL on failure.
83 */
84ZipFile* openReadWrite(const char* fileName, bool okayToCreate)
85{
86 ZipFile* zip = NULL;
87 status_t result;
88 int flags;
89
90 flags = ZipFile::kOpenReadWrite;
91 if (okayToCreate) {
92 flags |= ZipFile::kOpenCreate;
93 }
94
95 zip = new ZipFile;
96 result = zip->open(fileName, flags);
97 if (result != NO_ERROR) {
98 delete zip;
99 zip = NULL;
100 goto bail;
101 }
102
103bail:
104 return zip;
105}
106
107
108/*
109 * Return a short string describing the compression method.
110 */
111const char* compressionName(int method)
112{
113 if (method == ZipEntry::kCompressStored) {
114 return "Stored";
115 } else if (method == ZipEntry::kCompressDeflated) {
116 return "Deflated";
117 } else {
118 return "Unknown";
119 }
120}
121
122/*
123 * Return the percent reduction in size (0% == no compression).
124 */
125int calcPercent(long uncompressedLen, long compressedLen)
126{
127 if (!uncompressedLen) {
128 return 0;
129 } else {
130 return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5);
131 }
132}
133
134/*
135 * Handle the "list" command, which can be a simple file dump or
136 * a verbose listing.
137 *
138 * The verbose listing closely matches the output of the Info-ZIP "unzip"
139 * command.
140 */
141int doList(Bundle* bundle)
142{
143 int result = 1;
144 ZipFile* zip = NULL;
145 const ZipEntry* entry;
146 long totalUncLen, totalCompLen;
147 const char* zipFileName;
148
149 if (bundle->getFileSpecCount() != 1) {
150 fprintf(stderr, "ERROR: specify zip file name (only)\n");
151 goto bail;
152 }
153 zipFileName = bundle->getFileSpecEntry(0);
154
155 zip = openReadOnly(zipFileName);
156 if (zip == NULL) {
157 goto bail;
158 }
159
160 int count, i;
161
162 if (bundle->getVerbose()) {
163 printf("Archive: %s\n", zipFileName);
164 printf(
165 " Length Method Size Ratio Offset Date Time CRC-32 Name\n");
166 printf(
167 "-------- ------ ------- ----- ------- ---- ---- ------ ----\n");
168 }
169
170 totalUncLen = totalCompLen = 0;
171
172 count = zip->getNumEntries();
173 for (i = 0; i < count; i++) {
174 entry = zip->getEntryByIndex(i);
175 if (bundle->getVerbose()) {
176 char dateBuf[32];
177 time_t when;
178
179 when = entry->getModWhen();
180 strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M",
181 localtime(&when));
182
183 printf("%8ld %-7.7s %7ld %3d%% %8zd %s %08lx %s\n",
184 (long) entry->getUncompressedLen(),
185 compressionName(entry->getCompressionMethod()),
186 (long) entry->getCompressedLen(),
187 calcPercent(entry->getUncompressedLen(),
188 entry->getCompressedLen()),
189 (size_t) entry->getLFHOffset(),
190 dateBuf,
191 entry->getCRC32(),
192 entry->getFileName());
193 } else {
194 printf("%s\n", entry->getFileName());
195 }
196
197 totalUncLen += entry->getUncompressedLen();
198 totalCompLen += entry->getCompressedLen();
199 }
200
201 if (bundle->getVerbose()) {
202 printf(
203 "-------- ------- --- -------\n");
204 printf("%8ld %7ld %2d%% %d files\n",
205 totalUncLen,
206 totalCompLen,
207 calcPercent(totalUncLen, totalCompLen),
208 zip->getNumEntries());
209 }
210
211 if (bundle->getAndroidList()) {
212 AssetManager assets;
213 if (!assets.addAssetPath(String8(zipFileName), NULL)) {
214 fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n");
215 goto bail;
216 }
217
Elliott Hughesba3fe562015-08-12 14:49:53 -0700218#ifdef __ANDROID__
Andreas Gampeb8dc7bc2014-10-01 19:07:51 -0700219 static const bool kHaveAndroidOs = true;
220#else
221 static const bool kHaveAndroidOs = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800222#endif
Andreas Gampeb8dc7bc2014-10-01 19:07:51 -0700223 const ResTable& res = assets.getResources(false);
224 if (!kHaveAndroidOs) {
225 printf("\nResource table:\n");
226 res.print(false);
227 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800228
229 Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
230 Asset::ACCESS_BUFFER);
231 if (manifestAsset == NULL) {
232 printf("\nNo AndroidManifest.xml found.\n");
233 } else {
234 printf("\nAndroid manifest:\n");
235 ResXMLTree tree;
236 tree.setTo(manifestAsset->getBuffer(true),
237 manifestAsset->getLength());
238 printXMLBlock(&tree);
239 }
240 delete manifestAsset;
241 }
242
243 result = 0;
244
245bail:
246 delete zip;
247 return result;
248}
249
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700250static void printResolvedResourceAttribute(const ResTable& resTable, const ResXMLTree& tree,
Chih-Hung Hsieh9b8528f2016-08-10 14:15:30 -0700251 uint32_t attrRes, const String8& attrLabel, String8* outError)
Maurice Chu76327312013-10-16 18:28:46 -0700252{
253 Res_value value;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700254 AaptXml::getResolvedResourceAttribute(resTable, tree, attrRes, &value, outError);
Maurice Chu76327312013-10-16 18:28:46 -0700255 if (*outError != "") {
256 *outError = "error print resolved resource attribute";
257 return;
258 }
259 if (value.dataType == Res_value::TYPE_STRING) {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700260 String8 result = AaptXml::getResolvedAttribute(resTable, tree, attrRes, outError);
Maurice Chu2675f762013-10-22 17:33:11 -0700261 printf("%s='%s'", attrLabel.string(),
262 ResTable::normalizeForOutput(result.string()).string());
Maurice Chu76327312013-10-16 18:28:46 -0700263 } else if (Res_value::TYPE_FIRST_INT <= value.dataType &&
264 value.dataType <= Res_value::TYPE_LAST_INT) {
265 printf("%s='%d'", attrLabel.string(), value.data);
266 } else {
267 printf("%s='0x%x'", attrLabel.string(), (int)value.data);
268 }
269}
270
Adam Lesinski282e1812014-01-23 18:17:42 -0800271// These are attribute resource constants for the platform, as found
272// in android.R.attr
273enum {
274 LABEL_ATTR = 0x01010001,
275 ICON_ATTR = 0x01010002,
276 NAME_ATTR = 0x01010003,
Adam Lesinskia5018c92013-09-30 16:23:15 -0700277 PERMISSION_ATTR = 0x01010006,
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700278 EXPORTED_ATTR = 0x01010010,
279 GRANT_URI_PERMISSIONS_ATTR = 0x0101001b,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700280 RESOURCE_ATTR = 0x01010025,
Adam Lesinski282e1812014-01-23 18:17:42 -0800281 DEBUGGABLE_ATTR = 0x0101000f,
282 VALUE_ATTR = 0x01010024,
283 VERSION_CODE_ATTR = 0x0101021b,
284 VERSION_NAME_ATTR = 0x0101021c,
285 SCREEN_ORIENTATION_ATTR = 0x0101001e,
286 MIN_SDK_VERSION_ATTR = 0x0101020c,
287 MAX_SDK_VERSION_ATTR = 0x01010271,
288 REQ_TOUCH_SCREEN_ATTR = 0x01010227,
289 REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
290 REQ_HARD_KEYBOARD_ATTR = 0x01010229,
291 REQ_NAVIGATION_ATTR = 0x0101022a,
292 REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
293 TARGET_SDK_VERSION_ATTR = 0x01010270,
294 TEST_ONLY_ATTR = 0x01010272,
295 ANY_DENSITY_ATTR = 0x0101026c,
296 GL_ES_VERSION_ATTR = 0x01010281,
297 SMALL_SCREEN_ATTR = 0x01010284,
298 NORMAL_SCREEN_ATTR = 0x01010285,
299 LARGE_SCREEN_ATTR = 0x01010286,
300 XLARGE_SCREEN_ATTR = 0x010102bf,
301 REQUIRED_ATTR = 0x0101028e,
Adam Lesinskicaf797c2014-08-22 12:56:26 -0700302 INSTALL_LOCATION_ATTR = 0x010102b7,
Adam Lesinski282e1812014-01-23 18:17:42 -0800303 SCREEN_SIZE_ATTR = 0x010102ca,
304 SCREEN_DENSITY_ATTR = 0x010102cb,
305 REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
306 COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
307 LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
308 PUBLIC_KEY_ATTR = 0x010103a6,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700309 CATEGORY_ATTR = 0x010103e8,
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800310 BANNER_ATTR = 0x10103f2,
Tim Kilbournd9b1cad2014-10-24 12:43:41 -0700311 ISGAME_ATTR = 0x10103f4,
Adam Lesinski282e1812014-01-23 18:17:42 -0800312};
313
Maurice Chu2675f762013-10-22 17:33:11 -0700314String8 getComponentName(String8 &pkgName, String8 &componentName) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800315 ssize_t idx = componentName.find(".");
316 String8 retStr(pkgName);
317 if (idx == 0) {
318 retStr += componentName;
319 } else if (idx < 0) {
320 retStr += ".";
321 retStr += componentName;
322 } else {
Maurice Chu2675f762013-10-22 17:33:11 -0700323 return componentName;
Adam Lesinski282e1812014-01-23 18:17:42 -0800324 }
Maurice Chu2675f762013-10-22 17:33:11 -0700325 return retStr;
Adam Lesinski282e1812014-01-23 18:17:42 -0800326}
327
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700328static void printCompatibleScreens(ResXMLTree& tree, String8* outError) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800329 size_t len;
330 ResXMLTree::event_code_t code;
331 int depth = 0;
332 bool first = true;
333 printf("compatible-screens:");
334 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
335 if (code == ResXMLTree::END_TAG) {
336 depth--;
337 if (depth < 0) {
338 break;
339 }
340 continue;
341 }
342 if (code != ResXMLTree::START_TAG) {
343 continue;
344 }
345 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700346 const char16_t* ctag16 = tree.getElementName(&len);
347 if (ctag16 == NULL) {
348 *outError = "failed to get XML element name (bad string pool)";
349 return;
350 }
351 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800352 if (tag == "screen") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700353 int32_t screenSize = AaptXml::getIntegerAttribute(tree,
354 SCREEN_SIZE_ATTR);
355 int32_t screenDensity = AaptXml::getIntegerAttribute(tree,
356 SCREEN_DENSITY_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -0800357 if (screenSize > 0 && screenDensity > 0) {
358 if (!first) {
359 printf(",");
360 }
361 first = false;
362 printf("'%d/%d'", screenSize, screenDensity);
363 }
364 }
365 }
366 printf("\n");
367}
368
Adam Lesinski58f1f362013-11-12 12:59:08 -0800369static void printUsesPermission(const String8& name, bool optional=false, int maxSdkVersion=-1) {
370 printf("uses-permission: name='%s'", ResTable::normalizeForOutput(name.string()).string());
371 if (maxSdkVersion != -1) {
372 printf(" maxSdkVersion='%d'", maxSdkVersion);
373 }
374 printf("\n");
375
376 if (optional) {
377 printf("optional-permission: name='%s'",
378 ResTable::normalizeForOutput(name.string()).string());
379 if (maxSdkVersion != -1) {
380 printf(" maxSdkVersion='%d'", maxSdkVersion);
381 }
382 printf("\n");
383 }
384}
385
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800386static void printUsesPermissionSdk23(const String8& name, int maxSdkVersion=-1) {
387 printf("uses-permission-sdk-23: ");
388
389 printf("name='%s'", ResTable::normalizeForOutput(name.string()).string());
390 if (maxSdkVersion != -1) {
391 printf(" maxSdkVersion='%d'", maxSdkVersion);
392 }
393 printf("\n");
394}
395
Adam Lesinski2386df22016-12-28 15:08:58 -0500396static void printUsesImpliedPermission(const String8& name, const String8& reason,
397 const int32_t maxSdkVersion = -1) {
398 printf("uses-implied-permission: name='%s'",
399 ResTable::normalizeForOutput(name.string()).string());
400 if (maxSdkVersion != -1) {
401 printf(" maxSdkVersion='%d'", maxSdkVersion);
402 }
403 printf(" reason='%s'\n", ResTable::normalizeForOutput(reason.string()).string());
Adam Lesinski58f1f362013-11-12 12:59:08 -0800404}
405
Chih-Hung Hsieh9b8528f2016-08-10 14:15:30 -0700406Vector<String8> getNfcAidCategories(AssetManager& assets, const String8& xmlPath, bool offHost,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700407 String8 *outError = NULL)
408{
409 Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER);
410 if (aidAsset == NULL) {
411 if (outError != NULL) *outError = "xml resource does not exist";
412 return Vector<String8>();
413 }
414
415 const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service");
416
417 bool withinApduService = false;
418 Vector<String8> categories;
419
420 String8 error;
421 ResXMLTree tree;
422 tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength());
423
424 size_t len;
425 int depth = 0;
426 ResXMLTree::event_code_t code;
427 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
428 if (code == ResXMLTree::END_TAG) {
429 depth--;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700430 const char16_t* ctag16 = tree.getElementName(&len);
431 if (ctag16 == NULL) {
432 *outError = "failed to get XML element name (bad string pool)";
433 return Vector<String8>();
434 }
435 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700436
437 if (depth == 0 && tag == serviceTagName) {
438 withinApduService = false;
439 }
440
441 } else if (code == ResXMLTree::START_TAG) {
442 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700443 const char16_t* ctag16 = tree.getElementName(&len);
444 if (ctag16 == NULL) {
445 *outError = "failed to get XML element name (bad string pool)";
446 return Vector<String8>();
447 }
448 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700449
450 if (depth == 1) {
451 if (tag == serviceTagName) {
452 withinApduService = true;
453 }
454 } else if (depth == 2 && withinApduService) {
455 if (tag == "aid-group") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700456 String8 category = AaptXml::getAttribute(tree, CATEGORY_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700457 if (error != "") {
458 if (outError != NULL) *outError = error;
459 return Vector<String8>();
460 }
461
462 categories.add(category);
463 }
464 }
465 }
466 }
467 aidAsset->close();
468 return categories;
469}
470
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700471static void printComponentPresence(const char* componentName) {
472 printf("provides-component:'%s'\n", componentName);
473}
474
Adam Lesinski2c72b682014-06-24 09:56:01 -0700475/**
476 * Represents a feature that has been automatically added due to
477 * a pre-requisite or some other reason.
478 */
479struct ImpliedFeature {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800480 ImpliedFeature() : impliedBySdk23(false) {}
481 ImpliedFeature(const String8& n, bool sdk23) : name(n), impliedBySdk23(sdk23) {}
482
Adam Lesinski2c72b682014-06-24 09:56:01 -0700483 /**
484 * Name of the implied feature.
485 */
486 String8 name;
487
488 /**
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800489 * Was this implied by a permission from SDK 23 (<uses-permission-sdk-23 />)?
490 */
491 bool impliedBySdk23;
492
493 /**
Adam Lesinski2c72b682014-06-24 09:56:01 -0700494 * List of human-readable reasons for why this feature was implied.
495 */
496 SortedVector<String8> reasons;
497};
498
Adam Lesinski694d0a72016-04-06 16:12:04 -0700499struct Feature {
500 Feature() : required(false), version(-1) {}
Chih-Hung Hsiehd53e3be2016-05-03 10:02:51 -0700501 explicit Feature(bool required, int32_t version = -1) : required(required), version(version) {}
Adam Lesinski694d0a72016-04-06 16:12:04 -0700502
503 /**
504 * Whether the feature is required.
505 */
506 bool required;
507
508 /**
509 * What version of the feature is requested.
510 */
511 int32_t version;
512};
513
Adam Lesinski2c72b682014-06-24 09:56:01 -0700514/**
515 * Represents a <feature-group> tag in the AndroidManifest.xml
516 */
517struct FeatureGroup {
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700518 FeatureGroup() : openGLESVersion(-1) {}
519
Adam Lesinski2c72b682014-06-24 09:56:01 -0700520 /**
521 * Human readable label
522 */
523 String8 label;
524
525 /**
526 * Explicit features defined in the group
527 */
Adam Lesinski694d0a72016-04-06 16:12:04 -0700528 KeyedVector<String8, Feature> features;
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700529
530 /**
531 * OpenGL ES version required
532 */
533 int openGLESVersion;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700534};
535
Adam Lesinskica955a42016-08-01 16:44:29 -0700536static bool hasFeature(const char* name, const FeatureGroup& grp,
537 const KeyedVector<String8, ImpliedFeature>& implied) {
538 String8 name8(name);
539 ssize_t idx = grp.features.indexOfKey(name8);
540 if (idx < 0) {
541 idx = implied.indexOfKey(name8);
542 }
543 return idx >= 0;
544}
545
Adam Lesinski2c72b682014-06-24 09:56:01 -0700546static void addImpliedFeature(KeyedVector<String8, ImpliedFeature>* impliedFeatures,
Adam Lesinski43158772015-11-11 15:13:55 -0800547 const char* name, const String8& reason, bool sdk23) {
Adam Lesinski2c72b682014-06-24 09:56:01 -0700548 String8 name8(name);
549 ssize_t idx = impliedFeatures->indexOfKey(name8);
550 if (idx < 0) {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800551 idx = impliedFeatures->add(name8, ImpliedFeature(name8, sdk23));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700552 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800553
554 ImpliedFeature* feature = &impliedFeatures->editValueAt(idx);
555
556 // A non-sdk 23 implied feature takes precedence.
557 if (feature->impliedBySdk23 && !sdk23) {
558 feature->impliedBySdk23 = false;
559 }
Adam Lesinski43158772015-11-11 15:13:55 -0800560 feature->reasons.add(reason);
Adam Lesinski2c72b682014-06-24 09:56:01 -0700561}
562
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800563static void printFeatureGroupImpl(const FeatureGroup& grp,
564 const KeyedVector<String8, ImpliedFeature>* impliedFeatures) {
Adam Lesinski2c72b682014-06-24 09:56:01 -0700565 printf("feature-group: label='%s'\n", grp.label.string());
566
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700567 if (grp.openGLESVersion > 0) {
568 printf(" uses-gl-es: '0x%x'\n", grp.openGLESVersion);
569 }
570
Adam Lesinski2c72b682014-06-24 09:56:01 -0700571 const size_t numFeatures = grp.features.size();
572 for (size_t i = 0; i < numFeatures; i++) {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700573 const Feature& feature = grp.features[i];
574 const bool required = feature.required;
575 const int32_t version = feature.version;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700576
577 const String8& featureName = grp.features.keyAt(i);
Adam Lesinski694d0a72016-04-06 16:12:04 -0700578 printf(" uses-feature%s: name='%s'", (required ? "" : "-not-required"),
Adam Lesinski2c72b682014-06-24 09:56:01 -0700579 ResTable::normalizeForOutput(featureName.string()).string());
Adam Lesinski694d0a72016-04-06 16:12:04 -0700580
581 if (version > 0) {
582 printf(" version='%d'", version);
583 }
584 printf("\n");
Adam Lesinski2c72b682014-06-24 09:56:01 -0700585 }
586
587 const size_t numImpliedFeatures =
588 (impliedFeatures != NULL) ? impliedFeatures->size() : 0;
589 for (size_t i = 0; i < numImpliedFeatures; i++) {
590 const ImpliedFeature& impliedFeature = impliedFeatures->valueAt(i);
591 if (grp.features.indexOfKey(impliedFeature.name) >= 0) {
592 // The feature is explicitly set, no need to use implied
593 // definition.
594 continue;
595 }
596
597 String8 printableFeatureName(ResTable::normalizeForOutput(
598 impliedFeature.name.string()));
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800599 const char* sdk23Suffix = impliedFeature.impliedBySdk23 ? "-sdk-23" : "";
600
601 printf(" uses-feature%s: name='%s'\n", sdk23Suffix, printableFeatureName.string());
602 printf(" uses-implied-feature%s: name='%s' reason='", sdk23Suffix,
603 printableFeatureName.string());
Adam Lesinski2c72b682014-06-24 09:56:01 -0700604 const size_t numReasons = impliedFeature.reasons.size();
605 for (size_t j = 0; j < numReasons; j++) {
606 printf("%s", impliedFeature.reasons[j].string());
607 if (j + 2 < numReasons) {
608 printf(", ");
609 } else if (j + 1 < numReasons) {
610 printf(", and ");
611 }
612 }
613 printf("'\n");
614 }
615}
616
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800617static void printFeatureGroup(const FeatureGroup& grp) {
618 printFeatureGroupImpl(grp, NULL);
619}
620
621static void printDefaultFeatureGroup(const FeatureGroup& grp,
622 const KeyedVector<String8, ImpliedFeature>& impliedFeatures) {
623 printFeatureGroupImpl(grp, &impliedFeatures);
624}
625
Adam Lesinski2c72b682014-06-24 09:56:01 -0700626static void addParentFeatures(FeatureGroup* grp, const String8& name) {
627 if (name == "android.hardware.camera.autofocus" ||
628 name == "android.hardware.camera.flash") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700629 grp->features.add(String8("android.hardware.camera"), Feature(true));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700630 } else if (name == "android.hardware.location.gps" ||
631 name == "android.hardware.location.network") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700632 grp->features.add(String8("android.hardware.location"), Feature(true));
Adam Lesinskica955a42016-08-01 16:44:29 -0700633 } else if (name == "android.hardware.faketouch.multitouch") {
634 grp->features.add(String8("android.hardware.faketouch"), Feature(true));
635 } else if (name == "android.hardware.faketouch.multitouch.distinct" ||
636 name == "android.hardware.faketouch.multitouch.jazzhands") {
637 grp->features.add(String8("android.hardware.faketouch.multitouch"), Feature(true));
638 grp->features.add(String8("android.hardware.faketouch"), Feature(true));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700639 } else if (name == "android.hardware.touchscreen.multitouch") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700640 grp->features.add(String8("android.hardware.touchscreen"), Feature(true));
Adam Lesinskica955a42016-08-01 16:44:29 -0700641 } else if (name == "android.hardware.touchscreen.multitouch.distinct" ||
642 name == "android.hardware.touchscreen.multitouch.jazzhands") {
Adam Lesinski694d0a72016-04-06 16:12:04 -0700643 grp->features.add(String8("android.hardware.touchscreen.multitouch"), Feature(true));
644 grp->features.add(String8("android.hardware.touchscreen"), Feature(true));
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700645 } else if (name == "android.hardware.opengles.aep") {
646 const int openGLESVersion31 = 0x00030001;
647 if (openGLESVersion31 > grp->openGLESVersion) {
648 grp->openGLESVersion = openGLESVersion31;
649 }
Adam Lesinski2c72b682014-06-24 09:56:01 -0700650 }
651}
652
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800653static void addImpliedFeaturesForPermission(const int targetSdk, const String8& name,
654 KeyedVector<String8, ImpliedFeature>* impliedFeatures,
655 bool impliedBySdk23Permission) {
656 if (name == "android.permission.CAMERA") {
657 addImpliedFeature(impliedFeatures, "android.hardware.camera",
Adam Lesinski43158772015-11-11 15:13:55 -0800658 String8::format("requested %s permission", name.string()),
659 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800660 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
Adam Lesinski43158772015-11-11 15:13:55 -0800661 if (targetSdk < SDK_LOLLIPOP) {
662 addImpliedFeature(impliedFeatures, "android.hardware.location.gps",
663 String8::format("requested %s permission", name.string()),
664 impliedBySdk23Permission);
665 addImpliedFeature(impliedFeatures, "android.hardware.location.gps",
666 String8::format("targetSdkVersion < %d", SDK_LOLLIPOP),
667 impliedBySdk23Permission);
668 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800669 addImpliedFeature(impliedFeatures, "android.hardware.location",
Adam Lesinski43158772015-11-11 15:13:55 -0800670 String8::format("requested %s permission", name.string()),
671 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800672 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
Adam Lesinski43158772015-11-11 15:13:55 -0800673 if (targetSdk < SDK_LOLLIPOP) {
674 addImpliedFeature(impliedFeatures, "android.hardware.location.network",
675 String8::format("requested %s permission", name.string()),
676 impliedBySdk23Permission);
677 addImpliedFeature(impliedFeatures, "android.hardware.location.network",
678 String8::format("targetSdkVersion < %d", SDK_LOLLIPOP),
679 impliedBySdk23Permission);
680 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800681 addImpliedFeature(impliedFeatures, "android.hardware.location",
Adam Lesinski43158772015-11-11 15:13:55 -0800682 String8::format("requested %s permission", name.string()),
683 impliedBySdk23Permission);
684 } else if (name == "android.permission.ACCESS_MOCK_LOCATION" ||
685 name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800686 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
687 addImpliedFeature(impliedFeatures, "android.hardware.location",
Adam Lesinski43158772015-11-11 15:13:55 -0800688 String8::format("requested %s permission", name.string()),
689 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800690 } else if (name == "android.permission.BLUETOOTH" ||
691 name == "android.permission.BLUETOOTH_ADMIN") {
Adam Lesinski43158772015-11-11 15:13:55 -0800692 if (targetSdk > SDK_DONUT) {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800693 addImpliedFeature(impliedFeatures, "android.hardware.bluetooth",
Adam Lesinski43158772015-11-11 15:13:55 -0800694 String8::format("requested %s permission", name.string()),
695 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800696 addImpliedFeature(impliedFeatures, "android.hardware.bluetooth",
Adam Lesinski43158772015-11-11 15:13:55 -0800697 String8::format("targetSdkVersion > %d", SDK_DONUT),
698 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800699 }
700 } else if (name == "android.permission.RECORD_AUDIO") {
701 addImpliedFeature(impliedFeatures, "android.hardware.microphone",
Adam Lesinski43158772015-11-11 15:13:55 -0800702 String8::format("requested %s permission", name.string()),
703 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800704 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
705 name == "android.permission.CHANGE_WIFI_STATE" ||
706 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
707 addImpliedFeature(impliedFeatures, "android.hardware.wifi",
Adam Lesinski43158772015-11-11 15:13:55 -0800708 String8::format("requested %s permission", name.string()),
709 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800710 } else if (name == "android.permission.CALL_PHONE" ||
711 name == "android.permission.CALL_PRIVILEGED" ||
712 name == "android.permission.MODIFY_PHONE_STATE" ||
713 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
714 name == "android.permission.READ_SMS" ||
715 name == "android.permission.RECEIVE_SMS" ||
716 name == "android.permission.RECEIVE_MMS" ||
717 name == "android.permission.RECEIVE_WAP_PUSH" ||
718 name == "android.permission.SEND_SMS" ||
719 name == "android.permission.WRITE_APN_SETTINGS" ||
720 name == "android.permission.WRITE_SMS") {
721 addImpliedFeature(impliedFeatures, "android.hardware.telephony",
Adam Lesinski43158772015-11-11 15:13:55 -0800722 String8("requested a telephony permission"),
723 impliedBySdk23Permission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800724 }
725}
726
Adam Lesinski282e1812014-01-23 18:17:42 -0800727/*
728 * Handle the "dump" command, to extract select data from an archive.
729 */
730extern char CONSOLE_DATA[2925]; // see EOF
731int doDump(Bundle* bundle)
732{
733 status_t result = UNKNOWN_ERROR;
Adam Lesinski282e1812014-01-23 18:17:42 -0800734
735 if (bundle->getFileSpecCount() < 1) {
736 fprintf(stderr, "ERROR: no dump option specified\n");
737 return 1;
738 }
739
740 if (bundle->getFileSpecCount() < 2) {
741 fprintf(stderr, "ERROR: no dump file specified\n");
742 return 1;
743 }
744
745 const char* option = bundle->getFileSpecEntry(0);
746 const char* filename = bundle->getFileSpecEntry(1);
747
748 AssetManager assets;
Narayan Kamathf85e41f2014-01-24 14:14:30 +0000749 int32_t assetsCookie;
Adam Lesinski282e1812014-01-23 18:17:42 -0800750 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
751 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
752 return 1;
753 }
754
755 // Make a dummy config for retrieving resources... we need to supply
756 // non-default values for some configs so that we can retrieve resources
757 // in the app that don't have a default. The most important of these is
758 // the API version because key resources like icons will have an implicit
759 // version if they are using newer config types like density.
760 ResTable_config config;
Narayan Kamath91447d82014-01-21 15:32:36 +0000761 memset(&config, 0, sizeof(ResTable_config));
Adam Lesinski282e1812014-01-23 18:17:42 -0800762 config.language[0] = 'e';
763 config.language[1] = 'n';
764 config.country[0] = 'U';
765 config.country[1] = 'S';
766 config.orientation = ResTable_config::ORIENTATION_PORT;
767 config.density = ResTable_config::DENSITY_MEDIUM;
768 config.sdkVersion = 10000; // Very high.
769 config.screenWidthDp = 320;
770 config.screenHeightDp = 480;
771 config.smallestScreenWidthDp = 320;
Adam Lesinskic2dea8d2014-08-04 16:40:41 -0700772 config.screenLayout |= ResTable_config::SCREENSIZE_NORMAL;
Adam Lesinski282e1812014-01-23 18:17:42 -0800773 assets.setConfiguration(config);
774
775 const ResTable& res = assets.getResources(false);
Dan Albert68001652014-09-09 09:51:01 -0700776 if (res.getError() != NO_ERROR) {
Adam Lesinski25e9d552014-05-19 15:01:43 -0700777 fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
Adam Lesinski63e646e2014-07-30 11:40:39 -0700778 return 1;
Adam Lesinski282e1812014-01-23 18:17:42 -0800779 }
780
Adam Lesinski694d0a72016-04-06 16:12:04 -0700781 // Source for AndroidManifest.xml
Adam Lesinski10de3af12016-07-13 10:14:03 -0700782 const String8 manifestFile("AndroidManifest.xml");
Adam Lesinski694d0a72016-04-06 16:12:04 -0700783
Adam Lesinski2cb761e2014-08-15 13:59:02 -0700784 // The dynamicRefTable can be null if there are no resources for this asset cookie.
785 // This fine.
Adam Lesinski63e646e2014-07-30 11:40:39 -0700786 const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700787
788 Asset* asset = NULL;
789
Adam Lesinski282e1812014-01-23 18:17:42 -0800790 if (strcmp("resources", option) == 0) {
Elliott Hughesba3fe562015-08-12 14:49:53 -0700791#ifndef __ANDROID__
Adam Lesinski282e1812014-01-23 18:17:42 -0800792 res.print(bundle->getValues());
793#endif
794
795 } else if (strcmp("strings", option) == 0) {
796 const ResStringPool* pool = res.getTableStringBlock(0);
797 printStringPool(pool);
798
799 } else if (strcmp("xmltree", option) == 0) {
800 if (bundle->getFileSpecCount() < 3) {
801 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
802 goto bail;
803 }
804
805 for (int i=2; i<bundle->getFileSpecCount(); i++) {
806 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700807 ResXMLTree tree(dynamicRefTable);
808 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800809 if (asset == NULL) {
Adam Lesinskifcb5f7b2016-11-02 13:17:10 -0700810 fprintf(stderr, "ERROR: dump failed because resource %s not found\n", resname);
Adam Lesinski282e1812014-01-23 18:17:42 -0800811 goto bail;
812 }
813
814 if (tree.setTo(asset->getBuffer(true),
815 asset->getLength()) != NO_ERROR) {
816 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
817 goto bail;
818 }
819 tree.restart();
820 printXMLBlock(&tree);
821 tree.uninit();
822 delete asset;
823 asset = NULL;
824 }
825
826 } else if (strcmp("xmlstrings", option) == 0) {
827 if (bundle->getFileSpecCount() < 3) {
828 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
829 goto bail;
830 }
831
832 for (int i=2; i<bundle->getFileSpecCount(); i++) {
833 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700834 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800835 if (asset == NULL) {
836 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
837 goto bail;
838 }
839
Adam Lesinski63e646e2014-07-30 11:40:39 -0700840 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800841 if (tree.setTo(asset->getBuffer(true),
842 asset->getLength()) != NO_ERROR) {
843 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
844 goto bail;
845 }
846 printStringPool(&tree.getStrings());
847 delete asset;
848 asset = NULL;
849 }
850
851 } else {
Adam Lesinski63e646e2014-07-30 11:40:39 -0700852 asset = assets.openNonAsset(assetsCookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800853 if (asset == NULL) {
854 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
855 goto bail;
856 }
857
Adam Lesinski63e646e2014-07-30 11:40:39 -0700858 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800859 if (tree.setTo(asset->getBuffer(true),
860 asset->getLength()) != NO_ERROR) {
861 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
862 goto bail;
863 }
864 tree.restart();
865
866 if (strcmp("permissions", option) == 0) {
867 size_t len;
868 ResXMLTree::event_code_t code;
869 int depth = 0;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800870 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
871 code != ResXMLTree::BAD_DOCUMENT) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800872 if (code == ResXMLTree::END_TAG) {
873 depth--;
874 continue;
875 }
876 if (code != ResXMLTree::START_TAG) {
877 continue;
878 }
879 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700880 const char16_t* ctag16 = tree.getElementName(&len);
881 if (ctag16 == NULL) {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700882 SourcePos(manifestFile, tree.getLineNumber()).error(
883 "ERROR: failed to get XML element name (bad string pool)");
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700884 goto bail;
885 }
886 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800887 //printf("Depth %d tag %s\n", depth, tag.string());
888 if (depth == 1) {
889 if (tag != "manifest") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700890 SourcePos(manifestFile, tree.getLineNumber()).error(
891 "ERROR: manifest does not start with <manifest> tag");
Adam Lesinski282e1812014-01-23 18:17:42 -0800892 goto bail;
893 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700894 String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -0700895 printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800896 } else if (depth == 2) {
897 if (tag == "permission") {
898 String8 error;
899 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
900 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700901 SourcePos(manifestFile, tree.getLineNumber()).error(
902 "ERROR getting 'android:name': %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800903 goto bail;
904 }
905
906 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700907 SourcePos(manifestFile, tree.getLineNumber()).error(
908 "ERROR: missing 'android:name' for permission");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800909 goto bail;
910 }
911 printf("permission: %s\n",
912 ResTable::normalizeForOutput(name.string()).string());
913 } else if (tag == "uses-permission") {
914 String8 error;
915 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
916 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700917 SourcePos(manifestFile, tree.getLineNumber()).error(
918 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800919 goto bail;
920 }
921
922 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700923 SourcePos(manifestFile, tree.getLineNumber()).error(
924 "ERROR: missing 'android:name' for uses-permission");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800925 goto bail;
926 }
927 printUsesPermission(name,
928 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
929 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
930 } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
931 String8 error;
932 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
933 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700934 SourcePos(manifestFile, tree.getLineNumber()).error(
935 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800936 goto bail;
937 }
938
939 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -0700940 SourcePos(manifestFile, tree.getLineNumber()).error(
941 "ERROR: missing 'android:name' for uses-permission-sdk-23");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -0800942 goto bail;
943 }
944 printUsesPermissionSdk23(
945 name,
946 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
Adam Lesinski282e1812014-01-23 18:17:42 -0800947 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800948 }
949 }
950 } else if (strcmp("badging", option) == 0) {
951 Vector<String8> locales;
952 res.getLocales(&locales);
953
954 Vector<ResTable_config> configs;
955 res.getConfigurations(&configs);
956 SortedVector<int> densities;
957 const size_t NC = configs.size();
958 for (size_t i=0; i<NC; i++) {
959 int dens = configs[i].density;
960 if (dens == 0) {
961 dens = 160;
962 }
963 densities.add(dens);
964 }
965
966 size_t len;
967 ResXMLTree::event_code_t code;
968 int depth = 0;
969 String8 error;
970 bool withinActivity = false;
971 bool isMainActivity = false;
972 bool isLauncherActivity = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800973 bool isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800974 bool isSearchable = false;
975 bool withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700976 bool withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700977 bool withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800978 bool withinReceiver = false;
979 bool withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700980 bool withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800981 bool withinIntentFilter = false;
982 bool hasMainActivity = false;
983 bool hasOtherActivities = false;
984 bool hasOtherReceivers = false;
985 bool hasOtherServices = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700986 bool hasIntentFilter = false;
987
Adam Lesinski282e1812014-01-23 18:17:42 -0800988 bool hasWallpaperService = false;
989 bool hasImeService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700990 bool hasAccessibilityService = false;
991 bool hasPrintService = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800992 bool hasWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700993 bool hasDeviceAdminReceiver = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700994 bool hasPaymentService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700995 bool hasDocumentsProvider = false;
996 bool hasCameraActivity = false;
997 bool hasCameraSecureActivity = false;
998 bool hasLauncher = false;
999 bool hasNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001000 bool hasDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001001
Adam Lesinski282e1812014-01-23 18:17:42 -08001002 bool actMainActivity = false;
1003 bool actWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001004 bool actDeviceAdminEnabled = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001005 bool actImeService = false;
1006 bool actWallpaperService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001007 bool actAccessibilityService = false;
1008 bool actPrintService = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001009 bool actHostApduService = false;
1010 bool actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001011 bool actDocumentsProvider = false;
1012 bool actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001013 bool actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001014 bool actCamera = false;
1015 bool actCameraSecure = false;
1016 bool catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001017 bool hasMetaHostPaymentCategory = false;
1018 bool hasMetaOffHostPaymentCategory = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001019
1020 // These permissions are required by services implementing services
1021 // the system binds to (IME, Accessibility, PrintServices, etc.)
1022 bool hasBindDeviceAdminPermission = false;
1023 bool hasBindInputMethodPermission = false;
1024 bool hasBindAccessibilityServicePermission = false;
1025 bool hasBindPrintServicePermission = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001026 bool hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001027 bool hasRequiredSafAttributes = false;
1028 bool hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001029 bool hasBindDreamServicePermission = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001030
1031 // These two implement the implicit permissions that are granted
1032 // to pre-1.6 applications.
1033 bool hasWriteExternalStoragePermission = false;
Adam Lesinski2386df22016-12-28 15:08:58 -05001034 int32_t writeExternalStoragePermissionMaxSdkVersion = -1;
Adam Lesinski282e1812014-01-23 18:17:42 -08001035 bool hasReadPhoneStatePermission = false;
1036
1037 // If an app requests write storage, they will also get read storage.
1038 bool hasReadExternalStoragePermission = false;
1039
1040 // Implement transition to read and write call log.
1041 bool hasReadContactsPermission = false;
1042 bool hasWriteContactsPermission = false;
1043 bool hasReadCallLogPermission = false;
1044 bool hasWriteCallLogPermission = false;
1045
Adam Lesinskie47fd122014-08-15 22:25:36 -07001046 // If an app declares itself as multiArch, we report the
1047 // native libraries differently.
1048 bool hasMultiArch = false;
1049
Adam Lesinski282e1812014-01-23 18:17:42 -08001050 // This next group of variables is used to implement a group of
1051 // backward-compatibility heuristics necessitated by the addition of
1052 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
1053 // heuristic is "if an app requests a permission but doesn't explicitly
1054 // request the corresponding <uses-feature>, presume it's there anyway".
Adam Lesinski2c72b682014-06-24 09:56:01 -07001055
Adam Lesinski282e1812014-01-23 18:17:42 -08001056 // 2.2 also added some other features that apps can request, but that
1057 // have no corresponding permission, so we cannot implement any
1058 // back-compatibility heuristic for them. The below are thus unnecessary
1059 // (but are retained here for documentary purposes.)
1060 //bool specCompassFeature = false;
1061 //bool specAccelerometerFeature = false;
1062 //bool specProximityFeature = false;
1063 //bool specAmbientLightFeature = false;
1064 //bool specLiveWallpaperFeature = false;
1065
1066 int targetSdk = 0;
1067 int smallScreen = 1;
1068 int normalScreen = 1;
1069 int largeScreen = 1;
1070 int xlargeScreen = 1;
1071 int anyDensity = 1;
1072 int requiresSmallestWidthDp = 0;
1073 int compatibleWidthLimitDp = 0;
1074 int largestWidthLimitDp = 0;
1075 String8 pkg;
1076 String8 activityName;
1077 String8 activityLabel;
1078 String8 activityIcon;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001079 String8 activityBanner;
Adam Lesinski282e1812014-01-23 18:17:42 -08001080 String8 receiverName;
1081 String8 serviceName;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001082 Vector<String8> supportedInput;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001083
1084 FeatureGroup commonFeatures;
1085 Vector<FeatureGroup> featureGroups;
1086 KeyedVector<String8, ImpliedFeature> impliedFeatures;
1087
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001088 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
1089 code != ResXMLTree::BAD_DOCUMENT) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001090 if (code == ResXMLTree::END_TAG) {
1091 depth--;
1092 if (depth < 2) {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001093 if (withinSupportsInput && !supportedInput.isEmpty()) {
1094 printf("supports-input: '");
1095 const size_t N = supportedInput.size();
1096 for (size_t i=0; i<N; i++) {
Maurice Chu2675f762013-10-22 17:33:11 -07001097 printf("%s", ResTable::normalizeForOutput(
1098 supportedInput[i].string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001099 if (i != N - 1) {
1100 printf("' '");
1101 } else {
1102 printf("'\n");
1103 }
1104 }
1105 supportedInput.clear();
1106 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001107 withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001108 withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001109 withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001110 } else if (depth < 3) {
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001111 if (withinActivity && isMainActivity) {
Maurice Chu2675f762013-10-22 17:33:11 -07001112 String8 aName(getComponentName(pkg, activityName));
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001113 if (isLauncherActivity) {
1114 printf("launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001115 if (aName.length() > 0) {
1116 printf(" name='%s' ",
1117 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001118 }
1119 printf(" label='%s' icon='%s'\n",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001120 ResTable::normalizeForOutput(activityLabel.string())
1121 .string(),
1122 ResTable::normalizeForOutput(activityIcon.string())
1123 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001124 }
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001125 if (isLeanbackLauncherActivity) {
1126 printf("leanback-launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001127 if (aName.length() > 0) {
1128 printf(" name='%s' ",
1129 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001130 }
1131 printf(" label='%s' icon='%s' banner='%s'\n",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001132 ResTable::normalizeForOutput(activityLabel.string())
1133 .string(),
1134 ResTable::normalizeForOutput(activityIcon.string())
1135 .string(),
1136 ResTable::normalizeForOutput(activityBanner.string())
1137 .string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001138 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001139 }
1140 if (!hasIntentFilter) {
1141 hasOtherActivities |= withinActivity;
1142 hasOtherReceivers |= withinReceiver;
1143 hasOtherServices |= withinService;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001144 } else {
1145 if (withinService) {
1146 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
1147 hasBindNfcServicePermission);
1148 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
1149 hasBindNfcServicePermission);
1150 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001151 }
1152 withinActivity = false;
1153 withinService = false;
1154 withinReceiver = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001155 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001156 hasIntentFilter = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001157 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001158 } else if (depth < 4) {
1159 if (withinIntentFilter) {
1160 if (withinActivity) {
1161 hasMainActivity |= actMainActivity;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001162 hasLauncher |= catLauncher;
1163 hasCameraActivity |= actCamera;
1164 hasCameraSecureActivity |= actCameraSecure;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001165 hasOtherActivities |=
1166 !actMainActivity && !actCamera && !actCameraSecure;
Adam Lesinski282e1812014-01-23 18:17:42 -08001167 } else if (withinReceiver) {
1168 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001169 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
1170 hasBindDeviceAdminPermission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001171 hasOtherReceivers |=
1172 (!actWidgetReceivers && !actDeviceAdminEnabled);
Adam Lesinski282e1812014-01-23 18:17:42 -08001173 } else if (withinService) {
1174 hasImeService |= actImeService;
1175 hasWallpaperService |= actWallpaperService;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001176 hasAccessibilityService |= (actAccessibilityService &&
1177 hasBindAccessibilityServicePermission);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001178 hasPrintService |=
1179 (actPrintService && hasBindPrintServicePermission);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001180 hasNotificationListenerService |= actNotificationListenerService &&
1181 hasBindNotificationListenerServicePermission;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001182 hasDreamService |= actDreamService && hasBindDreamServicePermission;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001183 hasOtherServices |= (!actImeService && !actWallpaperService &&
Adam Lesinski94fc9122013-09-30 17:16:09 -07001184 !actAccessibilityService && !actPrintService &&
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001185 !actHostApduService && !actOffHostApduService &&
1186 !actNotificationListenerService);
1187 } else if (withinProvider) {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001188 hasDocumentsProvider |=
1189 actDocumentsProvider && hasRequiredSafAttributes;
Adam Lesinski282e1812014-01-23 18:17:42 -08001190 }
1191 }
1192 withinIntentFilter = false;
1193 }
1194 continue;
1195 }
1196 if (code != ResXMLTree::START_TAG) {
1197 continue;
1198 }
1199 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001200
1201 const char16_t* ctag16 = tree.getElementName(&len);
1202 if (ctag16 == NULL) {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001203 SourcePos(manifestFile, tree.getLineNumber()).error(
1204 "ERROR: failed to get XML element name (bad string pool)");
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001205 goto bail;
1206 }
1207 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -08001208 //printf("Depth %d, %s\n", depth, tag.string());
1209 if (depth == 1) {
1210 if (tag != "manifest") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001211 SourcePos(manifestFile, tree.getLineNumber()).error(
1212 "ERROR: manifest does not start with <manifest> tag");
Adam Lesinski282e1812014-01-23 18:17:42 -08001213 goto bail;
1214 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001215 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -07001216 printf("package: name='%s' ",
1217 ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001218 int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR,
1219 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001220 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001221 SourcePos(manifestFile, tree.getLineNumber()).error(
1222 "ERROR getting 'android:versionCode' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001223 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001224 goto bail;
1225 }
1226 if (versionCode > 0) {
1227 printf("versionCode='%d' ", versionCode);
1228 } else {
1229 printf("versionCode='' ");
1230 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001231 String8 versionName = AaptXml::getResolvedAttribute(res, tree,
1232 VERSION_NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001233 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001234 SourcePos(manifestFile, tree.getLineNumber()).error(
1235 "ERROR getting 'android:versionName' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001236 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001237 goto bail;
1238 }
Adam Lesinski25d35a92014-08-11 09:41:56 -07001239 printf("versionName='%s'",
Maurice Chu2675f762013-10-22 17:33:11 -07001240 ResTable::normalizeForOutput(versionName.string()).string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001241
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001242 String8 splitName = AaptXml::getAttribute(tree, NULL, "split");
Adam Lesinski25d35a92014-08-11 09:41:56 -07001243 if (!splitName.isEmpty()) {
1244 printf(" split='%s'", ResTable::normalizeForOutput(
1245 splitName.string()).string());
1246 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001247
Adam Lesinski5283fab2014-08-29 11:23:55 -07001248 String8 platformVersionName = AaptXml::getAttribute(tree, NULL,
1249 "platformBuildVersionName");
1250 printf(" platformBuildVersionName='%s'", platformVersionName.string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001251 printf("\n");
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001252
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001253 int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
1254 INSTALL_LOCATION_ATTR, &error);
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001255 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001256 SourcePos(manifestFile, tree.getLineNumber()).error(
1257 "ERROR getting 'android:installLocation' attribute: %s",
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001258 error.string());
1259 goto bail;
1260 }
1261
1262 if (installLocation >= 0) {
1263 printf("install-location:'");
1264 switch (installLocation) {
1265 case 0:
1266 printf("auto");
1267 break;
1268 case 1:
1269 printf("internalOnly");
1270 break;
1271 case 2:
1272 printf("preferExternal");
1273 break;
1274 default:
1275 fprintf(stderr, "Invalid installLocation %d\n", installLocation);
1276 goto bail;
1277 }
1278 printf("'\n");
1279 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001280 } else if (depth == 2) {
1281 withinApplication = false;
1282 if (tag == "application") {
1283 withinApplication = true;
1284
1285 String8 label;
1286 const size_t NL = locales.size();
1287 for (size_t i=0; i<NL; i++) {
1288 const char* localeStr = locales[i].string();
Adam Lesinskia77685f2016-10-03 16:26:28 -07001289 assets.setConfiguration(config, localeStr != NULL ? localeStr : "");
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001290 String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1291 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001292 if (llabel != "") {
1293 if (localeStr == NULL || strlen(localeStr) == 0) {
1294 label = llabel;
Maurice Chu2675f762013-10-22 17:33:11 -07001295 printf("application-label:'%s'\n",
1296 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001297 } else {
1298 if (label == "") {
1299 label = llabel;
1300 }
1301 printf("application-label-%s:'%s'\n", localeStr,
Maurice Chu2675f762013-10-22 17:33:11 -07001302 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001303 }
1304 }
1305 }
1306
1307 ResTable_config tmpConfig = config;
1308 const size_t ND = densities.size();
1309 for (size_t i=0; i<ND; i++) {
1310 tmpConfig.density = densities[i];
1311 assets.setConfiguration(tmpConfig);
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001312 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1313 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001314 if (icon != "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001315 printf("application-icon-%d:'%s'\n", densities[i],
1316 ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001317 }
1318 }
1319 assets.setConfiguration(config);
1320
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001321 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001322 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001323 SourcePos(manifestFile, tree.getLineNumber()).error(
1324 "ERROR getting 'android:icon' attribute: %s", error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001325 goto bail;
1326 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001327 int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0,
1328 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001329 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001330 SourcePos(manifestFile, tree.getLineNumber()).error(
1331 "ERROR getting 'android:testOnly' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001332 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001333 goto bail;
1334 }
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001335
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001336 String8 banner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1337 &error);
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001338 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001339 SourcePos(manifestFile, tree.getLineNumber()).error(
1340 "ERROR getting 'android:banner' attribute: %s", error.string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001341 goto bail;
1342 }
Maurice Chu2675f762013-10-22 17:33:11 -07001343 printf("application: label='%s' ",
1344 ResTable::normalizeForOutput(label.string()).string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001345 printf("icon='%s'", ResTable::normalizeForOutput(icon.string()).string());
1346 if (banner != "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001347 printf(" banner='%s'",
1348 ResTable::normalizeForOutput(banner.string()).string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001349 }
1350 printf("\n");
Adam Lesinski282e1812014-01-23 18:17:42 -08001351 if (testOnly != 0) {
1352 printf("testOnly='%d'\n", testOnly);
1353 }
1354
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001355 int32_t isGame = AaptXml::getResolvedIntegerAttribute(res, tree,
1356 ISGAME_ATTR, 0, &error);
1357 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001358 SourcePos(manifestFile, tree.getLineNumber()).error(
1359 "ERROR getting 'android:isGame' attribute: %s", error.string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001360 goto bail;
1361 }
1362 if (isGame != 0) {
1363 printf("application-isGame\n");
1364 }
1365
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001366 int32_t debuggable = AaptXml::getResolvedIntegerAttribute(res, tree,
1367 DEBUGGABLE_ATTR, 0, &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:debuggable' attribute: %s",
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001371 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001372 goto bail;
1373 }
1374 if (debuggable != 0) {
1375 printf("application-debuggable\n");
1376 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07001377
1378 // We must search by name because the multiArch flag hasn't been API
1379 // frozen yet.
1380 int32_t multiArchIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
1381 "multiArch");
1382 if (multiArchIndex >= 0) {
1383 Res_value value;
1384 if (tree.getAttributeValue(multiArchIndex, &value) != NO_ERROR) {
1385 if (value.dataType >= Res_value::TYPE_FIRST_INT &&
1386 value.dataType <= Res_value::TYPE_LAST_INT) {
1387 hasMultiArch = value.data;
1388 }
1389 }
1390 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001391 } else if (tag == "uses-sdk") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001392 int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR,
1393 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001394 if (error != "") {
1395 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001396 String8 name = AaptXml::getResolvedAttribute(res, tree,
1397 MIN_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001398 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001399 SourcePos(manifestFile, tree.getLineNumber()).error(
1400 "ERROR getting 'android:minSdkVersion' attribute: %s",
Adam Lesinski282e1812014-01-23 18:17:42 -08001401 error.string());
1402 goto bail;
1403 }
1404 if (name == "Donut") targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001405 printf("sdkVersion:'%s'\n",
1406 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001407 } else if (code != -1) {
1408 targetSdk = code;
1409 printf("sdkVersion:'%d'\n", code);
1410 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001411 code = AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -08001412 if (code != -1) {
1413 printf("maxSdkVersion:'%d'\n", code);
1414 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001415 code = AaptXml::getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001416 if (error != "") {
1417 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001418 String8 name = AaptXml::getResolvedAttribute(res, tree,
1419 TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001420 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001421 SourcePos(manifestFile, tree.getLineNumber()).error(
1422 "ERROR getting 'android:targetSdkVersion' attribute: %s",
Adam Lesinski282e1812014-01-23 18:17:42 -08001423 error.string());
1424 goto bail;
1425 }
1426 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001427 printf("targetSdkVersion:'%s'\n",
1428 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001429 } else if (code != -1) {
1430 if (targetSdk < code) {
1431 targetSdk = code;
1432 }
1433 printf("targetSdkVersion:'%d'\n", code);
1434 }
1435 } else if (tag == "uses-configuration") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001436 int32_t reqTouchScreen = AaptXml::getIntegerAttribute(tree,
1437 REQ_TOUCH_SCREEN_ATTR, 0);
1438 int32_t reqKeyboardType = AaptXml::getIntegerAttribute(tree,
1439 REQ_KEYBOARD_TYPE_ATTR, 0);
1440 int32_t reqHardKeyboard = AaptXml::getIntegerAttribute(tree,
1441 REQ_HARD_KEYBOARD_ATTR, 0);
1442 int32_t reqNavigation = AaptXml::getIntegerAttribute(tree,
1443 REQ_NAVIGATION_ATTR, 0);
1444 int32_t reqFiveWayNav = AaptXml::getIntegerAttribute(tree,
1445 REQ_FIVE_WAY_NAV_ATTR, 0);
Adam Lesinski282e1812014-01-23 18:17:42 -08001446 printf("uses-configuration:");
1447 if (reqTouchScreen != 0) {
1448 printf(" reqTouchScreen='%d'", reqTouchScreen);
1449 }
1450 if (reqKeyboardType != 0) {
1451 printf(" reqKeyboardType='%d'", reqKeyboardType);
1452 }
1453 if (reqHardKeyboard != 0) {
1454 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1455 }
1456 if (reqNavigation != 0) {
1457 printf(" reqNavigation='%d'", reqNavigation);
1458 }
1459 if (reqFiveWayNav != 0) {
1460 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1461 }
1462 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001463 } else if (tag == "supports-input") {
1464 withinSupportsInput = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001465 } else if (tag == "supports-screens") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001466 smallScreen = AaptXml::getIntegerAttribute(tree,
1467 SMALL_SCREEN_ATTR, 1);
1468 normalScreen = AaptXml::getIntegerAttribute(tree,
1469 NORMAL_SCREEN_ATTR, 1);
1470 largeScreen = AaptXml::getIntegerAttribute(tree,
1471 LARGE_SCREEN_ATTR, 1);
1472 xlargeScreen = AaptXml::getIntegerAttribute(tree,
1473 XLARGE_SCREEN_ATTR, 1);
1474 anyDensity = AaptXml::getIntegerAttribute(tree,
1475 ANY_DENSITY_ATTR, 1);
1476 requiresSmallestWidthDp = AaptXml::getIntegerAttribute(tree,
1477 REQUIRES_SMALLEST_WIDTH_DP_ATTR, 0);
1478 compatibleWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1479 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, 0);
1480 largestWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1481 LARGEST_WIDTH_LIMIT_DP_ATTR, 0);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001482 } else if (tag == "feature-group") {
1483 withinFeatureGroup = true;
1484 FeatureGroup group;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001485 group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001486 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001487 SourcePos(manifestFile, tree.getLineNumber()).error(
1488 "ERROR getting 'android:label' attribute: %s", error.string());
Adam Lesinski2c72b682014-06-24 09:56:01 -07001489 goto bail;
1490 }
1491 featureGroups.add(group);
1492
Adam Lesinski282e1812014-01-23 18:17:42 -08001493 } else if (tag == "uses-feature") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001494 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001495 if (name != "" && error == "") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001496 const char* androidSchema =
1497 "http://schemas.android.com/apk/res/android";
Adam Lesinski282e1812014-01-23 18:17:42 -08001498
Adam Lesinski694d0a72016-04-06 16:12:04 -07001499 int32_t req = AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1,
1500 &error);
1501 if (error != "") {
1502 SourcePos(manifestFile, tree.getLineNumber()).error(
1503 "failed to read attribute 'android:required': %s",
1504 error.string());
1505 goto bail;
1506 }
1507
1508 int32_t version = AaptXml::getIntegerAttribute(tree, androidSchema,
1509 "version", 0, &error);
1510 if (error != "") {
1511 SourcePos(manifestFile, tree.getLineNumber()).error(
1512 "failed to read attribute 'android:version': %s",
1513 error.string());
1514 goto bail;
1515 }
1516
1517 commonFeatures.features.add(name, Feature(req != 0, version));
Adam Lesinski2c72b682014-06-24 09:56:01 -07001518 if (req) {
1519 addParentFeatures(&commonFeatures, name);
Adam Lesinski282e1812014-01-23 18:17:42 -08001520 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001521 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001522 int vers = AaptXml::getIntegerAttribute(tree,
Adam Lesinski282e1812014-01-23 18:17:42 -08001523 GL_ES_VERSION_ATTR, &error);
1524 if (error == "") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001525 if (vers > commonFeatures.openGLESVersion) {
1526 commonFeatures.openGLESVersion = vers;
1527 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001528 }
1529 }
1530 } else if (tag == "uses-permission") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001531 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001532 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001533 SourcePos(manifestFile, tree.getLineNumber()).error(
1534 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001535 goto bail;
1536 }
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001537
1538 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001539 SourcePos(manifestFile, tree.getLineNumber()).error(
1540 "ERROR: missing 'android:name' for uses-permission");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001541 goto bail;
1542 }
1543
1544 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, false);
1545
Adam Lesinski2386df22016-12-28 15:08:58 -05001546 const int32_t maxSdkVersion =
1547 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, -1);
1548
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001549 if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1550 hasWriteExternalStoragePermission = true;
Adam Lesinski2386df22016-12-28 15:08:58 -05001551 writeExternalStoragePermissionMaxSdkVersion = maxSdkVersion;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001552 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1553 hasReadExternalStoragePermission = true;
1554 } else if (name == "android.permission.READ_PHONE_STATE") {
1555 hasReadPhoneStatePermission = true;
1556 } else if (name == "android.permission.READ_CONTACTS") {
1557 hasReadContactsPermission = true;
1558 } else if (name == "android.permission.WRITE_CONTACTS") {
1559 hasWriteContactsPermission = true;
1560 } else if (name == "android.permission.READ_CALL_LOG") {
1561 hasReadCallLogPermission = true;
1562 } else if (name == "android.permission.WRITE_CALL_LOG") {
1563 hasWriteCallLogPermission = true;
1564 }
1565
1566 printUsesPermission(name,
1567 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
Adam Lesinski2386df22016-12-28 15:08:58 -05001568 maxSdkVersion);
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001569
1570 } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
1571 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1572 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001573 SourcePos(manifestFile, tree.getLineNumber()).error(
1574 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001575 goto bail;
1576 }
1577
1578 if (name == "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001579 SourcePos(manifestFile, tree.getLineNumber()).error(
1580 "ERROR: missing 'android:name' for uses-permission-sdk-23");
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001581 goto bail;
1582 }
1583
1584 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, true);
1585
1586 printUsesPermissionSdk23(
1587 name, AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
1588
Adam Lesinski282e1812014-01-23 18:17:42 -08001589 } else if (tag == "uses-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001590 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001591 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001592 printf("uses-package:'%s'\n",
1593 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001594 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001595 SourcePos(manifestFile, tree.getLineNumber()).error(
1596 "ERROR getting 'android:name' attribute: %s", error.string());
1597 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001598 }
1599 } else if (tag == "original-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001600 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001601 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001602 printf("original-package:'%s'\n",
1603 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001604 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001605 SourcePos(manifestFile, tree.getLineNumber()).error(
1606 "ERROR getting 'android:name' attribute: %s", error.string());
1607 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001608 }
1609 } else if (tag == "supports-gl-texture") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001610 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001611 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001612 printf("supports-gl-texture:'%s'\n",
1613 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001614 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001615 SourcePos(manifestFile, tree.getLineNumber()).error(
1616 "ERROR getting 'android:name' attribute: %s", error.string());
1617 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -08001618 }
1619 } else if (tag == "compatible-screens") {
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001620 printCompatibleScreens(tree, &error);
1621 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001622 SourcePos(manifestFile, tree.getLineNumber()).error(
1623 "ERROR getting compatible screens: %s", error.string());
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001624 goto bail;
1625 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001626 depth--;
1627 } else if (tag == "package-verifier") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001628 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001629 if (name != "" && error == "") {
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001630 String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR,
1631 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001632 if (publicKey != "" && error == "") {
1633 printf("package-verifier: name='%s' publicKey='%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001634 ResTable::normalizeForOutput(name.string()).string(),
1635 ResTable::normalizeForOutput(publicKey.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001636 }
1637 }
1638 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001639 } else if (depth == 3) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001640 withinActivity = false;
1641 withinReceiver = false;
1642 withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001643 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001644 hasIntentFilter = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001645 hasMetaHostPaymentCategory = false;
1646 hasMetaOffHostPaymentCategory = false;
1647 hasBindDeviceAdminPermission = false;
1648 hasBindInputMethodPermission = false;
1649 hasBindAccessibilityServicePermission = false;
1650 hasBindPrintServicePermission = false;
1651 hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001652 hasRequiredSafAttributes = false;
1653 hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001654 hasBindDreamServicePermission = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001655 if (withinApplication) {
1656 if(tag == "activity") {
1657 withinActivity = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001658 activityName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001659 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001660 SourcePos(manifestFile, tree.getLineNumber()).error(
1661 "ERROR getting 'android:name' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001662 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001663 goto bail;
1664 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001665
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001666 activityLabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1667 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001668 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001669 SourcePos(manifestFile, tree.getLineNumber()).error(
1670 "ERROR getting 'android:label' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001671 error.string());
1672 goto bail;
1673 }
1674
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001675 activityIcon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1676 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001677 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001678 SourcePos(manifestFile, tree.getLineNumber()).error(
1679 "ERROR getting 'android:icon' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001680 error.string());
1681 goto bail;
1682 }
1683
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001684 activityBanner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1685 &error);
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001686 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001687 SourcePos(manifestFile, tree.getLineNumber()).error(
1688 "ERROR getting 'android:banner' attribute: %s",
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001689 error.string());
1690 goto bail;
1691 }
1692
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001693 int32_t orien = AaptXml::getResolvedIntegerAttribute(res, tree,
Michael Wrightec4fdec2013-09-06 16:50:52 -07001694 SCREEN_ORIENTATION_ATTR, &error);
1695 if (error == "") {
1696 if (orien == 0 || orien == 6 || orien == 8) {
1697 // Requests landscape, sensorLandscape, or reverseLandscape.
Adam Lesinski43158772015-11-11 15:13:55 -08001698 addImpliedFeature(
1699 &impliedFeatures, "android.hardware.screen.landscape",
1700 String8("one or more activities have specified a "
1701 "landscape orientation"),
1702 false);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001703 } else if (orien == 1 || orien == 7 || orien == 9) {
1704 // Requests portrait, sensorPortrait, or reversePortrait.
Adam Lesinski43158772015-11-11 15:13:55 -08001705 addImpliedFeature(
1706 &impliedFeatures, "android.hardware.screen.portrait",
1707 String8("one or more activities have specified a "
1708 "portrait orientation"),
1709 false);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001710 }
1711 }
1712 } else if (tag == "uses-library") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001713 String8 libraryName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001714 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001715 SourcePos(manifestFile, tree.getLineNumber()).error(
Michael Wrightec4fdec2013-09-06 16:50:52 -07001716 "ERROR getting 'android:name' attribute for uses-library"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001717 " %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001718 goto bail;
1719 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001720 int req = AaptXml::getIntegerAttribute(tree,
1721 REQUIRED_ATTR, 1);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001722 printf("uses-library%s:'%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001723 req ? "" : "-not-required", ResTable::normalizeForOutput(
1724 libraryName.string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001725 } else if (tag == "receiver") {
1726 withinReceiver = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001727 receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001728
1729 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001730 SourcePos(manifestFile, tree.getLineNumber()).error(
Michael Wrightec4fdec2013-09-06 16:50:52 -07001731 "ERROR getting 'android:name' attribute for receiver:"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001732 " %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001733 goto bail;
1734 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001735
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001736 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1737 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001738 if (error == "") {
1739 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1740 hasBindDeviceAdminPermission = true;
1741 }
1742 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001743 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001744 "ERROR getting 'android:permission' attribute for"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001745 " receiver '%s': %s",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001746 receiverName.string(), error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001747 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001748 } else if (tag == "service") {
1749 withinService = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001750 serviceName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001751
1752 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001753 SourcePos(manifestFile, tree.getLineNumber()).error(
1754 "ERROR getting 'android:name' attribute for "
1755 "service:%s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001756 goto bail;
1757 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001758
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001759 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1760 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001761 if (error == "") {
1762 if (permission == "android.permission.BIND_INPUT_METHOD") {
1763 hasBindInputMethodPermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001764 } else if (permission ==
1765 "android.permission.BIND_ACCESSIBILITY_SERVICE") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001766 hasBindAccessibilityServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001767 } else if (permission ==
1768 "android.permission.BIND_PRINT_SERVICE") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001769 hasBindPrintServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001770 } else if (permission ==
1771 "android.permission.BIND_NFC_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07001772 hasBindNfcServicePermission = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001773 } else if (permission ==
1774 "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001775 hasBindNotificationListenerServicePermission = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001776 } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
1777 hasBindDreamServicePermission = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001778 }
1779 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001780 SourcePos(manifestFile, tree.getLineNumber()).error(
1781 "ERROR getting 'android:permission' attribute for "
1782 "service '%s': %s", serviceName.string(), error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001783 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001784 } else if (tag == "provider") {
1785 withinProvider = true;
1786
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001787 bool exported = AaptXml::getResolvedIntegerAttribute(res, tree,
1788 EXPORTED_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001789 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001790 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001791 "ERROR getting 'android:exported' attribute for provider:"
Adam Lesinski10de3af12016-07-13 10:14:03 -07001792 " %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001793 goto bail;
1794 }
1795
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001796 bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute(
1797 res, tree, GRANT_URI_PERMISSIONS_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001798 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001799 SourcePos(manifestFile, tree.getLineNumber()).error(
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001800 "ERROR getting 'android:grantUriPermissions' attribute for "
Adam Lesinski10de3af12016-07-13 10:14:03 -07001801 "provider: %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001802 goto bail;
1803 }
1804
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001805 String8 permission = AaptXml::getResolvedAttribute(res, tree,
1806 PERMISSION_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001807 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001808 SourcePos(manifestFile, tree.getLineNumber()).error(
1809 "ERROR getting 'android:permission' attribute for "
1810 "provider: %s", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001811 goto bail;
1812 }
1813
1814 hasRequiredSafAttributes |= exported && grantUriPermissions &&
1815 permission == "android.permission.MANAGE_DOCUMENTS";
1816
Michael Wrightec4fdec2013-09-06 16:50:52 -07001817 } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001818 String8 metaDataName = AaptXml::getResolvedAttribute(res, tree,
1819 NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001820 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001821 SourcePos(manifestFile, tree.getLineNumber()).error(
1822 "ERROR getting 'android:name' attribute for "
1823 "meta-data: %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001824 goto bail;
1825 }
Maurice Chu2675f762013-10-22 17:33:11 -07001826 printf("meta-data: name='%s' ",
1827 ResTable::normalizeForOutput(metaDataName.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001828 printResolvedResourceAttribute(res, tree, VALUE_ATTR, String8("value"),
Maurice Chu76327312013-10-16 18:28:46 -07001829 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001830 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001831 // Try looking for a RESOURCE_ATTR
1832 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001833 printResolvedResourceAttribute(res, tree, RESOURCE_ATTR,
Maurice Chu76327312013-10-16 18:28:46 -07001834 String8("resource"), &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001835 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001836 SourcePos(manifestFile, tree.getLineNumber()).error(
1837 "ERROR getting 'android:value' or "
Maurice Chu76327312013-10-16 18:28:46 -07001838 "'android:resource' attribute for "
Adam Lesinski10de3af12016-07-13 10:14:03 -07001839 "meta-data: %s", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001840 goto bail;
1841 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001842 }
Maurice Chu76327312013-10-16 18:28:46 -07001843 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001844 } else if (withinSupportsInput && tag == "input-type") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001845 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001846 if (name != "" && error == "") {
1847 supportedInput.add(name);
1848 } else {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001849 SourcePos(manifestFile, tree.getLineNumber()).error(
1850 "ERROR getting 'android:name' attribute: %s",
Michael Wrightec4fdec2013-09-06 16:50:52 -07001851 error.string());
1852 goto bail;
1853 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001854 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001855 } else if (withinFeatureGroup && tag == "uses-feature") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001856 const String8 androidSchema("http://schemas.android.com/apk/res/android");
Adam Lesinski2c72b682014-06-24 09:56:01 -07001857 FeatureGroup& top = featureGroups.editTop();
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001858
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001859 String8 name = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001860 if (name != "" && error == "") {
Adam Lesinski694d0a72016-04-06 16:12:04 -07001861 Feature feature(true);
1862
1863 int32_t featureVers = AaptXml::getIntegerAttribute(
1864 tree, androidSchema.string(), "version", 0, &error);
1865 if (error == "") {
1866 feature.version = featureVers;
1867 } else {
1868 SourcePos(manifestFile, tree.getLineNumber()).error(
1869 "failed to read attribute 'android:version': %s",
1870 error.string());
1871 goto bail;
1872 }
1873
1874 top.features.add(name, feature);
Adam Lesinskid3edfde2014-08-08 17:32:44 -07001875 addParentFeatures(&top, name);
Adam Lesinski694d0a72016-04-06 16:12:04 -07001876
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001877 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001878 int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR,
1879 &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001880 if (error == "") {
1881 if (vers > top.openGLESVersion) {
1882 top.openGLESVersion = vers;
1883 }
1884 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001885 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001886 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001887 } else if (depth == 4) {
1888 if (tag == "intent-filter") {
1889 hasIntentFilter = true;
1890 withinIntentFilter = true;
1891 actMainActivity = false;
1892 actWidgetReceivers = false;
1893 actImeService = false;
1894 actWallpaperService = false;
1895 actAccessibilityService = false;
1896 actPrintService = false;
1897 actDeviceAdminEnabled = false;
1898 actHostApduService = false;
1899 actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001900 actDocumentsProvider = false;
1901 actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001902 actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001903 actCamera = false;
1904 actCameraSecure = false;
1905 catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001906 } else if (withinService && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001907 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001908 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001909 SourcePos(manifestFile, tree.getLineNumber()).error(
1910 "ERROR getting 'android:name' attribute for "
1911 "meta-data tag in service '%s': %s", serviceName.string(),
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001912 error.string());
Adam Lesinski94fc9122013-09-30 17:16:09 -07001913 goto bail;
1914 }
1915
1916 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1917 name == "android.nfc.cardemulation.off_host_apdu_service") {
1918 bool offHost = true;
1919 if (name == "android.nfc.cardemulation.host_apdu_service") {
1920 offHost = false;
1921 }
1922
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001923 String8 xmlPath = AaptXml::getResolvedAttribute(res, tree,
1924 RESOURCE_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001925 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001926 SourcePos(manifestFile, tree.getLineNumber()).error(
1927 "ERROR getting 'android:resource' attribute for "
1928 "meta-data tag in service '%s': %s",
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001929 serviceName.string(), error.string());
Adam Lesinski94fc9122013-09-30 17:16:09 -07001930 goto bail;
1931 }
1932
1933 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1934 offHost, &error);
1935 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001936 SourcePos(manifestFile, tree.getLineNumber()).error(
1937 "ERROR getting AID category for service '%s'",
Adam Lesinski94fc9122013-09-30 17:16:09 -07001938 serviceName.string());
1939 goto bail;
1940 }
1941
1942 const size_t catLen = categories.size();
1943 for (size_t i = 0; i < catLen; i++) {
1944 bool paymentCategory = (categories[i] == "payment");
1945 if (offHost) {
1946 hasMetaOffHostPaymentCategory |= paymentCategory;
1947 } else {
1948 hasMetaHostPaymentCategory |= paymentCategory;
1949 }
1950 }
1951 }
1952 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001953 } else if ((depth == 5) && withinIntentFilter) {
1954 String8 action;
1955 if (tag == "action") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001956 action = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001957 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07001958 SourcePos(manifestFile, tree.getLineNumber()).error(
1959 "ERROR getting 'android:name' attribute: %s", error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001960 goto bail;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001961 }
1962
Adam Lesinskia5018c92013-09-30 16:23:15 -07001963 if (withinActivity) {
1964 if (action == "android.intent.action.MAIN") {
1965 isMainActivity = true;
1966 actMainActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001967 } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
1968 action == "android.media.action.VIDEO_CAMERA") {
1969 actCamera = true;
1970 } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
1971 actCameraSecure = true;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001972 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001973 } else if (withinReceiver) {
1974 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1975 actWidgetReceivers = true;
1976 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1977 actDeviceAdminEnabled = true;
1978 }
1979 } else if (withinService) {
1980 if (action == "android.view.InputMethod") {
1981 actImeService = true;
1982 } else if (action == "android.service.wallpaper.WallpaperService") {
1983 actWallpaperService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001984 } else if (action ==
1985 "android.accessibilityservice.AccessibilityService") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001986 actAccessibilityService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001987 } else if (action =="android.printservice.PrintService") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001988 actPrintService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001989 } else if (action ==
1990 "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07001991 actHostApduService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001992 } else if (action ==
1993 "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07001994 actOffHostApduService = true;
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08001995 } else if (action ==
1996 "android.service.notification.NotificationListenerService") {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001997 actNotificationListenerService = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001998 } else if (action == "android.service.dreams.DreamService") {
1999 actDreamService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002000 }
2001 } else if (withinProvider) {
2002 if (action == "android.content.action.DOCUMENTS_PROVIDER") {
2003 actDocumentsProvider = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07002004 }
2005 }
2006 if (action == "android.intent.action.SEARCH") {
2007 isSearchable = true;
2008 }
2009 }
2010
2011 if (tag == "category") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07002012 String8 category = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07002013 if (error != "") {
Adam Lesinski10de3af12016-07-13 10:14:03 -07002014 SourcePos(manifestFile, tree.getLineNumber()).error(
2015 "ERROR getting 'name' attribute: %s", error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07002016 goto bail;
2017 }
2018 if (withinActivity) {
2019 if (category == "android.intent.category.LAUNCHER") {
2020 isLauncherActivity = true;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08002021 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
2022 isLeanbackLauncherActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002023 } else if (category == "android.intent.category.HOME") {
2024 catLauncher = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08002025 }
2026 }
2027 }
2028 }
2029 }
2030
2031 // Pre-1.6 implicitly granted permission compatibility logic
2032 if (targetSdk < 4) {
2033 if (!hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002034 printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
2035 printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
2036 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002037 hasWriteExternalStoragePermission = true;
2038 }
2039 if (!hasReadPhoneStatePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002040 printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
2041 printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
2042 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002043 }
2044 }
2045
2046 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
2047 // force them to always take READ_EXTERNAL_STORAGE as well. We always
2048 // do this (regardless of target API version) because we can't have
2049 // an app with write permission but not read permission.
2050 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
Adam Lesinski2386df22016-12-28 15:08:58 -05002051 printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
2052 false /* optional */, writeExternalStoragePermissionMaxSdkVersion);
Adam Lesinski58f1f362013-11-12 12:59:08 -08002053 printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
Adam Lesinski2386df22016-12-28 15:08:58 -05002054 String8("requested WRITE_EXTERNAL_STORAGE"),
2055 writeExternalStoragePermissionMaxSdkVersion);
Adam Lesinski282e1812014-01-23 18:17:42 -08002056 }
2057
2058 // Pre-JellyBean call log permission compatibility.
2059 if (targetSdk < 16) {
2060 if (!hasReadCallLogPermission && hasReadContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002061 printUsesPermission(String8("android.permission.READ_CALL_LOG"));
2062 printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
2063 String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002064 }
2065 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08002066 printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
2067 printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
2068 String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08002069 }
2070 }
2071
Adam Lesinskica955a42016-08-01 16:44:29 -07002072 // If the app hasn't declared the touchscreen as a feature requirement (either
2073 // directly or implied, required or not), then the faketouch feature is implied.
2074 if (!hasFeature("android.hardware.touchscreen", commonFeatures, impliedFeatures)) {
2075 addImpliedFeature(&impliedFeatures, "android.hardware.faketouch",
Adam Lesinski43158772015-11-11 15:13:55 -08002076 String8("default feature for all apps"), false);
Adam Lesinskica955a42016-08-01 16:44:29 -07002077 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07002078
2079 const size_t numFeatureGroups = featureGroups.size();
2080 if (numFeatureGroups == 0) {
2081 // If no <feature-group> tags were defined, apply auto-implied features.
Adam Lesinski5f3b2ec2015-12-02 15:40:19 -08002082 printDefaultFeatureGroup(commonFeatures, impliedFeatures);
Adam Lesinski2c72b682014-06-24 09:56:01 -07002083
2084 } else {
2085 // <feature-group> tags are defined, so we ignore implied features and
2086 for (size_t i = 0; i < numFeatureGroups; i++) {
2087 FeatureGroup& grp = featureGroups.editItemAt(i);
2088
Adam Lesinskid7a94da2014-07-25 14:38:54 -07002089 if (commonFeatures.openGLESVersion > grp.openGLESVersion) {
2090 grp.openGLESVersion = commonFeatures.openGLESVersion;
2091 }
2092
Adam Lesinski2c72b682014-06-24 09:56:01 -07002093 // Merge the features defined in the top level (not inside a <feature-group>)
2094 // with this feature group.
2095 const size_t numCommonFeatures = commonFeatures.features.size();
2096 for (size_t j = 0; j < numCommonFeatures; j++) {
2097 if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07002098 grp.features.add(commonFeatures.features.keyAt(j),
2099 commonFeatures.features[j]);
Adam Lesinski2c72b682014-06-24 09:56:01 -07002100 }
2101 }
2102
Adam Lesinski73a05112014-12-08 12:53:17 -08002103 if (!grp.features.isEmpty()) {
Adam Lesinski2c72b682014-06-24 09:56:01 -07002104 printFeatureGroup(grp);
Adam Lesinski282e1812014-01-23 18:17:42 -08002105 }
2106 }
2107 }
2108
Adam Lesinski282e1812014-01-23 18:17:42 -08002109
Adam Lesinski282e1812014-01-23 18:17:42 -08002110 if (hasWidgetReceivers) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002111 printComponentPresence("app-widget");
Adam Lesinski282e1812014-01-23 18:17:42 -08002112 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002113 if (hasDeviceAdminReceiver) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002114 printComponentPresence("device-admin");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002115 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002116 if (hasImeService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002117 printComponentPresence("ime");
Adam Lesinski282e1812014-01-23 18:17:42 -08002118 }
2119 if (hasWallpaperService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002120 printComponentPresence("wallpaper");
Adam Lesinski282e1812014-01-23 18:17:42 -08002121 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07002122 if (hasAccessibilityService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002123 printComponentPresence("accessibility");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002124 }
2125 if (hasPrintService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002126 printComponentPresence("print-service");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002127 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07002128 if (hasPaymentService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002129 printComponentPresence("payment");
2130 }
2131 if (isSearchable) {
2132 printComponentPresence("search");
2133 }
2134 if (hasDocumentsProvider) {
2135 printComponentPresence("document-provider");
2136 }
2137 if (hasLauncher) {
2138 printComponentPresence("launcher");
2139 }
2140 if (hasNotificationListenerService) {
2141 printComponentPresence("notification-listener");
2142 }
John Spurlockeb8d1be2014-06-25 17:46:15 -04002143 if (hasDreamService) {
2144 printComponentPresence("dream");
2145 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002146 if (hasCameraActivity) {
2147 printComponentPresence("camera");
2148 }
2149 if (hasCameraSecureActivity) {
2150 printComponentPresence("camera-secure");
2151 }
2152
2153 if (hasMainActivity) {
2154 printf("main\n");
Adam Lesinski94fc9122013-09-30 17:16:09 -07002155 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002156 if (hasOtherActivities) {
2157 printf("other-activities\n");
2158 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002159 if (hasOtherReceivers) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002160 printf("other-receivers\n");
2161 }
2162 if (hasOtherServices) {
2163 printf("other-services\n");
2164 }
2165
2166 // For modern apps, if screen size buckets haven't been specified
2167 // but the new width ranges have, then infer the buckets from them.
2168 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
2169 && requiresSmallestWidthDp > 0) {
2170 int compatWidth = compatibleWidthLimitDp;
2171 if (compatWidth <= 0) {
2172 compatWidth = requiresSmallestWidthDp;
2173 }
2174 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
2175 smallScreen = -1;
2176 } else {
2177 smallScreen = 0;
2178 }
2179 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
2180 normalScreen = -1;
2181 } else {
2182 normalScreen = 0;
2183 }
2184 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
2185 largeScreen = -1;
2186 } else {
2187 largeScreen = 0;
2188 }
2189 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
2190 xlargeScreen = -1;
2191 } else {
2192 xlargeScreen = 0;
2193 }
2194 }
2195
2196 // Determine default values for any unspecified screen sizes,
2197 // based on the target SDK of the package. As of 4 (donut)
2198 // the screen size support was introduced, so all default to
2199 // enabled.
2200 if (smallScreen > 0) {
2201 smallScreen = targetSdk >= 4 ? -1 : 0;
2202 }
2203 if (normalScreen > 0) {
2204 normalScreen = -1;
2205 }
2206 if (largeScreen > 0) {
2207 largeScreen = targetSdk >= 4 ? -1 : 0;
2208 }
2209 if (xlargeScreen > 0) {
2210 // Introduced in Gingerbread.
2211 xlargeScreen = targetSdk >= 9 ? -1 : 0;
2212 }
2213 if (anyDensity > 0) {
2214 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
2215 || compatibleWidthLimitDp > 0) ? -1 : 0;
2216 }
2217 printf("supports-screens:");
2218 if (smallScreen != 0) {
2219 printf(" 'small'");
2220 }
2221 if (normalScreen != 0) {
2222 printf(" 'normal'");
2223 }
2224 if (largeScreen != 0) {
2225 printf(" 'large'");
2226 }
2227 if (xlargeScreen != 0) {
2228 printf(" 'xlarge'");
2229 }
2230 printf("\n");
2231 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
2232 if (requiresSmallestWidthDp > 0) {
2233 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
2234 }
2235 if (compatibleWidthLimitDp > 0) {
2236 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
2237 }
2238 if (largestWidthLimitDp > 0) {
2239 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
2240 }
2241
2242 printf("locales:");
2243 const size_t NL = locales.size();
2244 for (size_t i=0; i<NL; i++) {
2245 const char* localeStr = locales[i].string();
2246 if (localeStr == NULL || strlen(localeStr) == 0) {
2247 localeStr = "--_--";
2248 }
2249 printf(" '%s'", localeStr);
2250 }
2251 printf("\n");
2252
2253 printf("densities:");
2254 const size_t ND = densities.size();
2255 for (size_t i=0; i<ND; i++) {
2256 printf(" '%d'", densities[i]);
2257 }
2258 printf("\n");
2259
2260 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
2261 if (dir != NULL) {
2262 if (dir->getFileCount() > 0) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002263 SortedVector<String8> architectures;
Adam Lesinski282e1812014-01-23 18:17:42 -08002264 for (size_t i=0; i<dir->getFileCount(); i++) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002265 architectures.add(ResTable::normalizeForOutput(
2266 dir->getFileName(i).string()));
Adam Lesinski282e1812014-01-23 18:17:42 -08002267 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07002268
2269 bool outputAltNativeCode = false;
2270 // A multiArch package is one that contains 64-bit and
2271 // 32-bit versions of native code and expects 3rd-party
2272 // apps to load these native code libraries. Since most
2273 // 64-bit systems also support 32-bit apps, the apps
2274 // loading this multiArch package's code may be either
2275 // 32-bit or 64-bit.
2276 if (hasMultiArch) {
2277 // If this is a multiArch package, report the 64-bit
2278 // version only. Then as a separate entry, report the
2279 // rest.
2280 //
2281 // If we report the 32-bit architecture, this APK will
2282 // be installed on a 32-bit device, causing a large waste
2283 // of bandwidth and disk space. This assumes that
2284 // the developer of the multiArch package has also
2285 // made a version that is 32-bit only.
2286 String8 intel64("x86_64");
2287 String8 arm64("arm64-v8a");
2288 ssize_t index = architectures.indexOf(intel64);
2289 if (index < 0) {
2290 index = architectures.indexOf(arm64);
2291 }
2292
2293 if (index >= 0) {
2294 printf("native-code: '%s'\n", architectures[index].string());
2295 architectures.removeAt(index);
2296 outputAltNativeCode = true;
2297 }
2298 }
2299
2300 const size_t archCount = architectures.size();
2301 if (archCount > 0) {
2302 if (outputAltNativeCode) {
2303 printf("alt-");
2304 }
2305 printf("native-code:");
2306 for (size_t i = 0; i < archCount; i++) {
2307 printf(" '%s'", architectures[i].string());
2308 }
2309 printf("\n");
2310 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002311 }
2312 delete dir;
2313 }
2314 } else if (strcmp("badger", option) == 0) {
2315 printf("%s", CONSOLE_DATA);
2316 } else if (strcmp("configurations", option) == 0) {
2317 Vector<ResTable_config> configs;
2318 res.getConfigurations(&configs);
2319 const size_t N = configs.size();
2320 for (size_t i=0; i<N; i++) {
2321 printf("%s\n", configs[i].toString().string());
2322 }
2323 } else {
2324 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2325 goto bail;
2326 }
2327 }
2328
2329 result = NO_ERROR;
2330
2331bail:
Adam Lesinski10de3af12016-07-13 10:14:03 -07002332 if (SourcePos::hasErrors()) {
2333 SourcePos::printErrors(stderr);
2334 }
2335
Adam Lesinski282e1812014-01-23 18:17:42 -08002336 if (asset) {
2337 delete asset;
2338 }
2339 return (result != NO_ERROR);
2340}
2341
2342
2343/*
2344 * Handle the "add" command, which wants to add files to a new or
2345 * pre-existing archive.
2346 */
2347int doAdd(Bundle* bundle)
2348{
2349 ZipFile* zip = NULL;
2350 status_t result = UNKNOWN_ERROR;
2351 const char* zipFileName;
2352
2353 if (bundle->getUpdate()) {
2354 /* avoid confusion */
2355 fprintf(stderr, "ERROR: can't use '-u' with add\n");
2356 goto bail;
2357 }
2358
2359 if (bundle->getFileSpecCount() < 1) {
2360 fprintf(stderr, "ERROR: must specify zip file name\n");
2361 goto bail;
2362 }
2363 zipFileName = bundle->getFileSpecEntry(0);
2364
2365 if (bundle->getFileSpecCount() < 2) {
2366 fprintf(stderr, "NOTE: nothing to do\n");
2367 goto bail;
2368 }
2369
2370 zip = openReadWrite(zipFileName, true);
2371 if (zip == NULL) {
2372 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2373 goto bail;
2374 }
2375
2376 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2377 const char* fileName = bundle->getFileSpecEntry(i);
2378
2379 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
2380 printf(" '%s'... (from gzip)\n", fileName);
2381 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
2382 } else {
2383 if (bundle->getJunkPath()) {
2384 String8 storageName = String8(fileName).getPathLeaf();
Maurice Chu2675f762013-10-22 17:33:11 -07002385 printf(" '%s' as '%s'...\n", fileName,
2386 ResTable::normalizeForOutput(storageName.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08002387 result = zip->add(fileName, storageName.string(),
2388 bundle->getCompressionMethod(), NULL);
2389 } else {
2390 printf(" '%s'...\n", fileName);
2391 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2392 }
2393 }
2394 if (result != NO_ERROR) {
2395 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2396 if (result == NAME_NOT_FOUND) {
2397 fprintf(stderr, ": file not found\n");
2398 } else if (result == ALREADY_EXISTS) {
2399 fprintf(stderr, ": already exists in archive\n");
2400 } else {
2401 fprintf(stderr, "\n");
2402 }
2403 goto bail;
2404 }
2405 }
2406
2407 result = NO_ERROR;
2408
2409bail:
2410 delete zip;
2411 return (result != NO_ERROR);
2412}
2413
2414
2415/*
2416 * Delete files from an existing archive.
2417 */
2418int doRemove(Bundle* bundle)
2419{
2420 ZipFile* zip = NULL;
2421 status_t result = UNKNOWN_ERROR;
2422 const char* zipFileName;
2423
2424 if (bundle->getFileSpecCount() < 1) {
2425 fprintf(stderr, "ERROR: must specify zip file name\n");
2426 goto bail;
2427 }
2428 zipFileName = bundle->getFileSpecEntry(0);
2429
2430 if (bundle->getFileSpecCount() < 2) {
2431 fprintf(stderr, "NOTE: nothing to do\n");
2432 goto bail;
2433 }
2434
2435 zip = openReadWrite(zipFileName, false);
2436 if (zip == NULL) {
2437 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2438 zipFileName);
2439 goto bail;
2440 }
2441
2442 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2443 const char* fileName = bundle->getFileSpecEntry(i);
2444 ZipEntry* entry;
2445
2446 entry = zip->getEntryByName(fileName);
2447 if (entry == NULL) {
2448 printf(" '%s' NOT FOUND\n", fileName);
2449 continue;
2450 }
2451
2452 result = zip->remove(entry);
2453
2454 if (result != NO_ERROR) {
2455 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2456 bundle->getFileSpecEntry(i), zipFileName);
2457 goto bail;
2458 }
2459 }
2460
2461 /* update the archive */
2462 zip->flush();
2463
2464bail:
2465 delete zip;
2466 return (result != NO_ERROR);
2467}
2468
Adam Lesinski3921e872014-05-13 10:56:25 -07002469static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002470 const size_t numDirs = dir->getDirs().size();
2471 for (size_t i = 0; i < numDirs; i++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002472 bool ignore = ignoreConfig;
2473 const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2474 const char* dirStr = subDir->getLeaf().string();
2475 if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2476 ignore = true;
2477 }
2478 status_t err = addResourcesToBuilder(subDir, builder, ignore);
Adam Lesinskifab50872014-04-16 14:40:42 -07002479 if (err != NO_ERROR) {
2480 return err;
2481 }
2482 }
2483
2484 const size_t numFiles = dir->getFiles().size();
2485 for (size_t i = 0; i < numFiles; i++) {
2486 sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2487 const size_t numConfigs = gp->getFiles().size();
2488 for (size_t j = 0; j < numConfigs; j++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002489 status_t err = NO_ERROR;
2490 if (ignoreConfig) {
2491 err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2492 } else {
2493 err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2494 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002495 if (err != NO_ERROR) {
2496 fprintf(stderr, "Failed to add %s (%s) to builder.\n",
2497 gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
2498 return err;
2499 }
2500 }
2501 }
2502 return NO_ERROR;
2503}
2504
2505static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2506 if (split->isBase()) {
2507 return original;
2508 }
2509
2510 String8 ext(original.getPathExtension());
2511 if (ext == String8(".apk")) {
2512 return String8::format("%s_%s%s",
2513 original.getBasePath().string(),
2514 split->getDirectorySafeName().string(),
2515 ext.string());
2516 }
2517
2518 return String8::format("%s_%s", original.string(),
2519 split->getDirectorySafeName().string());
2520}
Adam Lesinski282e1812014-01-23 18:17:42 -08002521
2522/*
2523 * Package up an asset directory and associated application files.
2524 */
2525int doPackage(Bundle* bundle)
2526{
2527 const char* outputAPKFile;
2528 int retVal = 1;
2529 status_t err;
2530 sp<AaptAssets> assets;
2531 int N;
2532 FILE* fp;
2533 String8 dependencyFile;
Adam Lesinskifab50872014-04-16 14:40:42 -07002534 sp<ApkBuilder> builder;
Adam Lesinski282e1812014-01-23 18:17:42 -08002535
Anton Krumina2ef5c02014-03-12 14:46:44 -07002536 // -c en_XA or/and ar_XB means do pseudolocalization
Adam Lesinskifab50872014-04-16 14:40:42 -07002537 sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2538 err = configFilter->parse(bundle->getConfigurations());
Adam Lesinski282e1812014-01-23 18:17:42 -08002539 if (err != NO_ERROR) {
2540 goto bail;
2541 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002542 if (configFilter->containsPseudo()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002543 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2544 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002545 if (configFilter->containsPseudoBidi()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002546 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
Adam Lesinski282e1812014-01-23 18:17:42 -08002547 }
2548
2549 N = bundle->getFileSpecCount();
2550 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
Adam Lesinski09384302014-01-22 16:07:42 -08002551 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002552 fprintf(stderr, "ERROR: no input files\n");
2553 goto bail;
2554 }
2555
2556 outputAPKFile = bundle->getOutputAPKFile();
2557
2558 // Make sure the filenames provided exist and are of the appropriate type.
2559 if (outputAPKFile) {
2560 FileType type;
2561 type = getFileType(outputAPKFile);
2562 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2563 fprintf(stderr,
2564 "ERROR: output file '%s' exists but is not regular file\n",
2565 outputAPKFile);
2566 goto bail;
2567 }
2568 }
2569
2570 // Load the assets.
2571 assets = new AaptAssets();
2572
2573 // Set up the resource gathering in assets if we're going to generate
2574 // dependency files. Every time we encounter a resource while slurping
2575 // the tree, we'll add it to these stores so we have full resource paths
2576 // to write to a dependency file.
2577 if (bundle->getGenDependencies()) {
2578 sp<FilePathStore> resPathStore = new FilePathStore;
2579 assets->setFullResPaths(resPathStore);
2580 sp<FilePathStore> assetPathStore = new FilePathStore;
2581 assets->setFullAssetPaths(assetPathStore);
2582 }
2583
2584 err = assets->slurpFromArgs(bundle);
2585 if (err < 0) {
2586 goto bail;
2587 }
2588
2589 if (bundle->getVerbose()) {
2590 assets->print(String8());
2591 }
2592
Adam Lesinskifab50872014-04-16 14:40:42 -07002593 // Create the ApkBuilder, which will collect the compiled files
2594 // to write to the final APK (or sets of APKs if we are building
2595 // a Split APK.
2596 builder = new ApkBuilder(configFilter);
2597
2598 // If we are generating a Split APK, find out which configurations to split on.
2599 if (bundle->getSplitConfigurations().size() > 0) {
2600 const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2601 const size_t numSplits = splitStrs.size();
2602 for (size_t i = 0; i < numSplits; i++) {
2603 std::set<ConfigDescription> configs;
2604 if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2605 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2606 goto bail;
2607 }
2608
2609 err = builder->createSplitForConfigs(configs);
2610 if (err != NO_ERROR) {
2611 goto bail;
2612 }
2613 }
2614 }
2615
Adam Lesinski282e1812014-01-23 18:17:42 -08002616 // If they asked for any fileAs that need to be compiled, do so.
2617 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002618 err = buildResources(bundle, assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002619 if (err != 0) {
2620 goto bail;
2621 }
2622 }
2623
2624 // At this point we've read everything and processed everything. From here
2625 // on out it's just writing output files.
2626 if (SourcePos::hasErrors()) {
2627 goto bail;
2628 }
2629
2630 // Update symbols with information about which ones are needed as Java symbols.
2631 assets->applyJavaSymbols();
2632 if (SourcePos::hasErrors()) {
2633 goto bail;
2634 }
2635
2636 // If we've been asked to generate a dependency file, do that here
2637 if (bundle->getGenDependencies()) {
2638 // If this is the packaging step, generate the dependency file next to
2639 // the output apk (e.g. bin/resources.ap_.d)
2640 if (outputAPKFile) {
2641 dependencyFile = String8(outputAPKFile);
2642 // Add the .d extension to the dependency file.
2643 dependencyFile.append(".d");
2644 } else {
2645 // Else if this is the R.java dependency generation step,
2646 // generate the dependency file in the R.java package subdirectory
2647 // e.g. gen/com/foo/app/R.java.d
2648 dependencyFile = String8(bundle->getRClassDir());
2649 dependencyFile.appendPath("R.java.d");
2650 }
2651 // Make sure we have a clean dependency file to start with
2652 fp = fopen(dependencyFile, "w");
2653 fclose(fp);
2654 }
2655
2656 // Write out R.java constants
2657 if (!assets->havePrivateSymbols()) {
2658 if (bundle->getCustomPackage() == NULL) {
2659 // Write the R.java file into the appropriate class directory
2660 // e.g. gen/com/foo/app/R.java
Adam Lesinski1e4663852014-08-15 14:47:28 -07002661 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002662 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002663 } else {
2664 const String8 customPkg(bundle->getCustomPackage());
Adam Lesinski1e4663852014-08-15 14:47:28 -07002665 err = writeResourceSymbols(bundle, assets, customPkg, true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002666 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002667 }
2668 if (err < 0) {
2669 goto bail;
2670 }
2671 // If we have library files, we're going to write our R.java file into
2672 // the appropriate class directory for those libraries as well.
2673 // e.g. gen/com/foo/app/lib/R.java
2674 if (bundle->getExtraPackages() != NULL) {
2675 // Split on colon
2676 String8 libs(bundle->getExtraPackages());
2677 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2678 while (packageString != NULL) {
2679 // Write the R.java file out with the correct package name
Marcin Kosiba0f3a5a62014-09-11 13:48:48 +01002680 err = writeResourceSymbols(bundle, assets, String8(packageString), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002681 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002682 if (err < 0) {
2683 goto bail;
2684 }
2685 packageString = strtok(NULL, ":");
2686 }
2687 libs.unlockBuffer();
2688 }
2689 } else {
Adam Lesinski1e4663852014-08-15 14:47:28 -07002690 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002691 if (err < 0) {
2692 goto bail;
2693 }
Adam Lesinski1e4663852014-08-15 14:47:28 -07002694 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002695 if (err < 0) {
2696 goto bail;
2697 }
2698 }
2699
2700 // Write out the ProGuard file
2701 err = writeProguardFile(bundle, assets);
2702 if (err < 0) {
2703 goto bail;
2704 }
2705
Rohit Agrawal86229cb2016-04-21 16:29:58 -07002706 // Write out the Main Dex ProGuard file
2707 err = writeMainDexProguardFile(bundle, assets);
2708 if (err < 0) {
2709 goto bail;
2710 }
2711
Adam Lesinski282e1812014-01-23 18:17:42 -08002712 // Write the apk
2713 if (outputAPKFile) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002714 // Gather all resources and add them to the APK Builder. The builder will then
2715 // figure out which Split they belong in.
2716 err = addResourcesToBuilder(assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002717 if (err != NO_ERROR) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002718 goto bail;
2719 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002720
2721 const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2722 const size_t numSplits = splits.size();
2723 for (size_t i = 0; i < numSplits; i++) {
2724 const sp<ApkSplit>& split = splits[i];
2725 String8 outputPath = buildApkName(String8(outputAPKFile), split);
2726 err = writeAPK(bundle, outputPath, split);
2727 if (err != NO_ERROR) {
2728 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2729 goto bail;
2730 }
2731 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002732 }
2733
2734 // If we've been asked to generate a dependency file, we need to finish up here.
2735 // the writeResourceSymbols and writeAPK functions have already written the target
2736 // half of the dependency file, now we need to write the prerequisites. (files that
2737 // the R.java file or .ap_ file depend on)
2738 if (bundle->getGenDependencies()) {
2739 // Now that writeResourceSymbols or writeAPK has taken care of writing
2740 // the targets to our dependency file, we'll write the prereqs
2741 fp = fopen(dependencyFile, "a+");
2742 fprintf(fp, " : ");
2743 bool includeRaw = (outputAPKFile != NULL);
2744 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2745 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2746 // and therefore was not added to our pathstores during slurping
2747 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2748 fclose(fp);
2749 }
2750
2751 retVal = 0;
2752bail:
2753 if (SourcePos::hasErrors()) {
2754 SourcePos::printErrors(stderr);
2755 }
2756 return retVal;
2757}
2758
2759/*
2760 * Do PNG Crunching
2761 * PRECONDITIONS
2762 * -S flag points to a source directory containing drawable* folders
2763 * -C flag points to destination directory. The folder structure in the
2764 * source directory will be mirrored to the destination (cache) directory
2765 *
2766 * POSTCONDITIONS
2767 * Destination directory will be updated to match the PNG files in
Maurice Chu2675f762013-10-22 17:33:11 -07002768 * the source directory.
Adam Lesinski282e1812014-01-23 18:17:42 -08002769 */
2770int doCrunch(Bundle* bundle)
2771{
2772 fprintf(stdout, "Crunching PNG Files in ");
2773 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2774 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2775
2776 updatePreProcessedCache(bundle);
2777
2778 return NO_ERROR;
2779}
2780
2781/*
2782 * Do PNG Crunching on a single flag
2783 * -i points to a single png file
2784 * -o points to a single png output file
2785 */
2786int doSingleCrunch(Bundle* bundle)
2787{
2788 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2789 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2790
2791 String8 input(bundle->getSingleCrunchInputFile());
2792 String8 output(bundle->getSingleCrunchOutputFile());
2793
2794 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2795 // we can't return the status_t as it gets truncate to the lower 8 bits.
2796 return 42;
2797 }
2798
2799 return NO_ERROR;
2800}
2801
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002802int runInDaemonMode(Bundle* bundle) {
2803 std::cout << "Ready" << std::endl;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002804 for (std::string cmd; std::getline(std::cin, cmd);) {
2805 if (cmd == "quit") {
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002806 return NO_ERROR;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002807 } else if (cmd == "s") {
2808 // Two argument crunch
2809 std::string inputFile, outputFile;
2810 std::getline(std::cin, inputFile);
2811 std::getline(std::cin, outputFile);
2812 bundle->setSingleCrunchInputFile(inputFile.c_str());
2813 bundle->setSingleCrunchOutputFile(outputFile.c_str());
2814 std::cout << "Crunching " << inputFile << std::endl;
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002815 if (doSingleCrunch(bundle) != NO_ERROR) {
2816 std::cout << "Error" << std::endl;
2817 }
2818 std::cout << "Done" << std::endl;
2819 } else {
2820 // in case of invalid command, just bail out.
2821 std::cerr << "Unknown command" << std::endl;
2822 return -1;
2823 }
2824 }
2825 return -1;
2826}
2827
Adam Lesinski282e1812014-01-23 18:17:42 -08002828char CONSOLE_DATA[2925] = {
2829 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2830 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2831 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2832 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2833 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2834 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2835 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2836 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2837 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2838 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2839 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2840 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2841 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2842 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2843 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2844 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2845 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2846 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2847 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2848 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2849 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2850 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2851 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2852 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2853 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2854 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2855 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2856 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2857 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2858 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2859 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2860 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2861 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2862 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2863 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2864 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2865 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2866 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2867 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2868 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2869 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2870 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2871 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2872 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2873 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2874 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2875 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2876 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2877 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2878 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2879 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2880 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2881 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2882 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2883 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2884 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2885 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2886 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2887 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2888 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2889 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2890 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2891 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2892 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2893 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2894 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2895 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2896 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2897 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2898 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2899 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2900 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2901 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2902 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2903 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2904 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2905 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2906 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2907 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2908 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2909 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2910 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2911 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2912 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2913 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2914 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2915 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2916 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2917 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2918 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2919 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2920 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2921 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2922 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2923 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2924 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2925 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2926 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2927 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2928 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2929 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2930 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2931 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2932 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2933 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2934 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2935 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2936 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2937 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2938 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2939 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2940 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2941 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2942 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2943 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2944 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2945 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2946 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2947 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2948 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2949 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2950 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2951 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2952 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2953 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2954 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2955 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2956 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2957 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2958 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2959 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2960 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2961 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2962 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2963 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2964 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2965 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2966 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2967 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2968 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2969 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2970 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2971 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2972 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2973 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2974 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2975 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2976 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2977 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2978 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2979 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2980 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2981 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2982 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2983 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2984 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2985 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2986 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2987 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2988 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2989 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2990 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2991 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2992 };