blob: 0bb88a7b3284da0e77f5b5db0f455689123ab3c1 [file] [log] [blame]
Adam Lesinski282e1812014-01-23 18:17:42 -08001//
2// Copyright 2006 The Android Open Source Project
3//
4// Android Asset Packaging Tool main entry point.
5//
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07006#include "AaptXml.h"
Adam Lesinskifab50872014-04-16 14:40:42 -07007#include "ApkBuilder.h"
Adam Lesinski282e1812014-01-23 18:17:42 -08008#include "Bundle.h"
Adam Lesinski2c72b682014-06-24 09:56:01 -07009#include "Images.h"
10#include "Main.h"
Adam Lesinski282e1812014-01-23 18:17:42 -080011#include "ResourceFilter.h"
12#include "ResourceTable.h"
Adam Lesinski282e1812014-01-23 18:17:42 -080013#include "XMLNode.h"
14
Adam Lesinski282e1812014-01-23 18:17:42 -080015#include <utils/Errors.h>
Adam Lesinski2c72b682014-06-24 09:56:01 -070016#include <utils/KeyedVector.h>
17#include <utils/List.h>
18#include <utils/Log.h>
19#include <utils/SortedVector.h>
20#include <utils/threads.h>
21#include <utils/Vector.h>
Adam Lesinski282e1812014-01-23 18:17:42 -080022
Adam Lesinski282e1812014-01-23 18:17:42 -080023#include <errno.h>
Adam Lesinski2c72b682014-06-24 09:56:01 -070024#include <fcntl.h>
Adam Lesinski282e1812014-01-23 18:17:42 -080025
Jerome Dochez6f1280c2014-09-26 10:21:21 -070026#include <iostream>
27#include <string>
28#include <sstream>
29
Adam Lesinski282e1812014-01-23 18:17:42 -080030using namespace android;
31
Adam Lesinskiad751222014-08-18 14:06:38 -070032#ifndef AAPT_VERSION
33 #define AAPT_VERSION ""
34#endif
35
Adam Lesinski282e1812014-01-23 18:17:42 -080036/*
37 * Show version info. All the cool kids do it.
38 */
39int doVersion(Bundle* bundle)
40{
41 if (bundle->getFileSpecCount() != 0) {
42 printf("(ignoring extra arguments)\n");
43 }
Adam Lesinskiad751222014-08-18 14:06:38 -070044 printf("Android Asset Packaging Tool, v0.2-" AAPT_VERSION "\n");
Adam Lesinski282e1812014-01-23 18:17:42 -080045
46 return 0;
47}
48
49
50/*
51 * Open the file read only. The call fails if the file doesn't exist.
52 *
53 * Returns NULL on failure.
54 */
55ZipFile* openReadOnly(const char* fileName)
56{
57 ZipFile* zip;
58 status_t result;
59
60 zip = new ZipFile;
61 result = zip->open(fileName, ZipFile::kOpenReadOnly);
62 if (result != NO_ERROR) {
63 if (result == NAME_NOT_FOUND) {
64 fprintf(stderr, "ERROR: '%s' not found\n", fileName);
65 } else if (result == PERMISSION_DENIED) {
66 fprintf(stderr, "ERROR: '%s' access denied\n", fileName);
67 } else {
68 fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n",
69 fileName);
70 }
71 delete zip;
72 return NULL;
73 }
74
75 return zip;
76}
77
78/*
79 * Open the file read-write. The file will be created if it doesn't
80 * already exist and "okayToCreate" is set.
81 *
82 * Returns NULL on failure.
83 */
84ZipFile* openReadWrite(const char* fileName, bool okayToCreate)
85{
86 ZipFile* zip = NULL;
87 status_t result;
88 int flags;
89
90 flags = ZipFile::kOpenReadWrite;
91 if (okayToCreate) {
92 flags |= ZipFile::kOpenCreate;
93 }
94
95 zip = new ZipFile;
96 result = zip->open(fileName, flags);
97 if (result != NO_ERROR) {
98 delete zip;
99 zip = NULL;
100 goto bail;
101 }
102
103bail:
104 return zip;
105}
106
107
108/*
109 * Return a short string describing the compression method.
110 */
111const char* compressionName(int method)
112{
113 if (method == ZipEntry::kCompressStored) {
114 return "Stored";
115 } else if (method == ZipEntry::kCompressDeflated) {
116 return "Deflated";
117 } else {
118 return "Unknown";
119 }
120}
121
122/*
123 * Return the percent reduction in size (0% == no compression).
124 */
125int calcPercent(long uncompressedLen, long compressedLen)
126{
127 if (!uncompressedLen) {
128 return 0;
129 } else {
130 return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5);
131 }
132}
133
134/*
135 * Handle the "list" command, which can be a simple file dump or
136 * a verbose listing.
137 *
138 * The verbose listing closely matches the output of the Info-ZIP "unzip"
139 * command.
140 */
141int doList(Bundle* bundle)
142{
143 int result = 1;
144 ZipFile* zip = NULL;
145 const ZipEntry* entry;
146 long totalUncLen, totalCompLen;
147 const char* zipFileName;
148
149 if (bundle->getFileSpecCount() != 1) {
150 fprintf(stderr, "ERROR: specify zip file name (only)\n");
151 goto bail;
152 }
153 zipFileName = bundle->getFileSpecEntry(0);
154
155 zip = openReadOnly(zipFileName);
156 if (zip == NULL) {
157 goto bail;
158 }
159
160 int count, i;
161
162 if (bundle->getVerbose()) {
163 printf("Archive: %s\n", zipFileName);
164 printf(
165 " Length Method Size Ratio Offset Date Time CRC-32 Name\n");
166 printf(
167 "-------- ------ ------- ----- ------- ---- ---- ------ ----\n");
168 }
169
170 totalUncLen = totalCompLen = 0;
171
172 count = zip->getNumEntries();
173 for (i = 0; i < count; i++) {
174 entry = zip->getEntryByIndex(i);
175 if (bundle->getVerbose()) {
176 char dateBuf[32];
177 time_t when;
178
179 when = entry->getModWhen();
180 strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M",
181 localtime(&when));
182
183 printf("%8ld %-7.7s %7ld %3d%% %8zd %s %08lx %s\n",
184 (long) entry->getUncompressedLen(),
185 compressionName(entry->getCompressionMethod()),
186 (long) entry->getCompressedLen(),
187 calcPercent(entry->getUncompressedLen(),
188 entry->getCompressedLen()),
189 (size_t) entry->getLFHOffset(),
190 dateBuf,
191 entry->getCRC32(),
192 entry->getFileName());
193 } else {
194 printf("%s\n", entry->getFileName());
195 }
196
197 totalUncLen += entry->getUncompressedLen();
198 totalCompLen += entry->getCompressedLen();
199 }
200
201 if (bundle->getVerbose()) {
202 printf(
203 "-------- ------- --- -------\n");
204 printf("%8ld %7ld %2d%% %d files\n",
205 totalUncLen,
206 totalCompLen,
207 calcPercent(totalUncLen, totalCompLen),
208 zip->getNumEntries());
209 }
210
211 if (bundle->getAndroidList()) {
212 AssetManager assets;
213 if (!assets.addAssetPath(String8(zipFileName), NULL)) {
214 fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n");
215 goto bail;
216 }
217
Elliott Hughesba3fe562015-08-12 14:49:53 -0700218#ifdef __ANDROID__
Andreas Gampeb8dc7bc2014-10-01 19:07:51 -0700219 static const bool kHaveAndroidOs = true;
220#else
221 static const bool kHaveAndroidOs = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800222#endif
Andreas Gampeb8dc7bc2014-10-01 19:07:51 -0700223 const ResTable& res = assets.getResources(false);
224 if (!kHaveAndroidOs) {
225 printf("\nResource table:\n");
226 res.print(false);
227 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800228
229 Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
230 Asset::ACCESS_BUFFER);
231 if (manifestAsset == NULL) {
232 printf("\nNo AndroidManifest.xml found.\n");
233 } else {
234 printf("\nAndroid manifest:\n");
235 ResXMLTree tree;
236 tree.setTo(manifestAsset->getBuffer(true),
237 manifestAsset->getLength());
238 printXMLBlock(&tree);
239 }
240 delete manifestAsset;
241 }
242
243 result = 0;
244
245bail:
246 delete zip;
247 return result;
248}
249
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700250static void printResolvedResourceAttribute(const ResTable& resTable, const ResXMLTree& tree,
Maurice Chu76327312013-10-16 18:28:46 -0700251 uint32_t attrRes, String8 attrLabel, String8* outError)
252{
253 Res_value value;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700254 AaptXml::getResolvedResourceAttribute(resTable, tree, attrRes, &value, outError);
Maurice Chu76327312013-10-16 18:28:46 -0700255 if (*outError != "") {
256 *outError = "error print resolved resource attribute";
257 return;
258 }
259 if (value.dataType == Res_value::TYPE_STRING) {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700260 String8 result = AaptXml::getResolvedAttribute(resTable, tree, attrRes, outError);
Maurice Chu2675f762013-10-22 17:33:11 -0700261 printf("%s='%s'", attrLabel.string(),
262 ResTable::normalizeForOutput(result.string()).string());
Maurice Chu76327312013-10-16 18:28:46 -0700263 } else if (Res_value::TYPE_FIRST_INT <= value.dataType &&
264 value.dataType <= Res_value::TYPE_LAST_INT) {
265 printf("%s='%d'", attrLabel.string(), value.data);
266 } else {
267 printf("%s='0x%x'", attrLabel.string(), (int)value.data);
268 }
269}
270
Adam Lesinski282e1812014-01-23 18:17:42 -0800271// These are attribute resource constants for the platform, as found
272// in android.R.attr
273enum {
274 LABEL_ATTR = 0x01010001,
275 ICON_ATTR = 0x01010002,
276 NAME_ATTR = 0x01010003,
Adam Lesinskia5018c92013-09-30 16:23:15 -0700277 PERMISSION_ATTR = 0x01010006,
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700278 EXPORTED_ATTR = 0x01010010,
279 GRANT_URI_PERMISSIONS_ATTR = 0x0101001b,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700280 RESOURCE_ATTR = 0x01010025,
Adam Lesinski282e1812014-01-23 18:17:42 -0800281 DEBUGGABLE_ATTR = 0x0101000f,
282 VALUE_ATTR = 0x01010024,
283 VERSION_CODE_ATTR = 0x0101021b,
284 VERSION_NAME_ATTR = 0x0101021c,
285 SCREEN_ORIENTATION_ATTR = 0x0101001e,
286 MIN_SDK_VERSION_ATTR = 0x0101020c,
287 MAX_SDK_VERSION_ATTR = 0x01010271,
288 REQ_TOUCH_SCREEN_ATTR = 0x01010227,
289 REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
290 REQ_HARD_KEYBOARD_ATTR = 0x01010229,
291 REQ_NAVIGATION_ATTR = 0x0101022a,
292 REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
293 TARGET_SDK_VERSION_ATTR = 0x01010270,
294 TEST_ONLY_ATTR = 0x01010272,
295 ANY_DENSITY_ATTR = 0x0101026c,
296 GL_ES_VERSION_ATTR = 0x01010281,
297 SMALL_SCREEN_ATTR = 0x01010284,
298 NORMAL_SCREEN_ATTR = 0x01010285,
299 LARGE_SCREEN_ATTR = 0x01010286,
300 XLARGE_SCREEN_ATTR = 0x010102bf,
301 REQUIRED_ATTR = 0x0101028e,
Adam Lesinskicaf797c2014-08-22 12:56:26 -0700302 INSTALL_LOCATION_ATTR = 0x010102b7,
Adam Lesinski282e1812014-01-23 18:17:42 -0800303 SCREEN_SIZE_ATTR = 0x010102ca,
304 SCREEN_DENSITY_ATTR = 0x010102cb,
305 REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
306 COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
307 LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
308 PUBLIC_KEY_ATTR = 0x010103a6,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700309 CATEGORY_ATTR = 0x010103e8,
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800310 BANNER_ATTR = 0x10103f2,
Tim Kilbournd9b1cad2014-10-24 12:43:41 -0700311 ISGAME_ATTR = 0x10103f4,
Adam Lesinski282e1812014-01-23 18:17:42 -0800312};
313
Maurice Chu2675f762013-10-22 17:33:11 -0700314String8 getComponentName(String8 &pkgName, String8 &componentName) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800315 ssize_t idx = componentName.find(".");
316 String8 retStr(pkgName);
317 if (idx == 0) {
318 retStr += componentName;
319 } else if (idx < 0) {
320 retStr += ".";
321 retStr += componentName;
322 } else {
Maurice Chu2675f762013-10-22 17:33:11 -0700323 return componentName;
Adam Lesinski282e1812014-01-23 18:17:42 -0800324 }
Maurice Chu2675f762013-10-22 17:33:11 -0700325 return retStr;
Adam Lesinski282e1812014-01-23 18:17:42 -0800326}
327
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700328static void printCompatibleScreens(ResXMLTree& tree, String8* outError) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800329 size_t len;
330 ResXMLTree::event_code_t code;
331 int depth = 0;
332 bool first = true;
333 printf("compatible-screens:");
334 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
335 if (code == ResXMLTree::END_TAG) {
336 depth--;
337 if (depth < 0) {
338 break;
339 }
340 continue;
341 }
342 if (code != ResXMLTree::START_TAG) {
343 continue;
344 }
345 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700346 const char16_t* ctag16 = tree.getElementName(&len);
347 if (ctag16 == NULL) {
348 *outError = "failed to get XML element name (bad string pool)";
349 return;
350 }
351 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800352 if (tag == "screen") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700353 int32_t screenSize = AaptXml::getIntegerAttribute(tree,
354 SCREEN_SIZE_ATTR);
355 int32_t screenDensity = AaptXml::getIntegerAttribute(tree,
356 SCREEN_DENSITY_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -0800357 if (screenSize > 0 && screenDensity > 0) {
358 if (!first) {
359 printf(",");
360 }
361 first = false;
362 printf("'%d/%d'", screenSize, screenDensity);
363 }
364 }
365 }
366 printf("\n");
367}
368
Adam Lesinski58f1f362013-11-12 12:59:08 -0800369static void printUsesPermission(const String8& name, bool optional=false, int maxSdkVersion=-1) {
370 printf("uses-permission: name='%s'", ResTable::normalizeForOutput(name.string()).string());
371 if (maxSdkVersion != -1) {
372 printf(" maxSdkVersion='%d'", maxSdkVersion);
373 }
374 printf("\n");
375
376 if (optional) {
377 printf("optional-permission: name='%s'",
378 ResTable::normalizeForOutput(name.string()).string());
379 if (maxSdkVersion != -1) {
380 printf(" maxSdkVersion='%d'", maxSdkVersion);
381 }
382 printf("\n");
383 }
384}
385
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800386static void printUsesPermissionSdk23(const String8& name, int maxSdkVersion=-1) {
387 printf("uses-permission-sdk-23: ");
388
389 printf("name='%s'", ResTable::normalizeForOutput(name.string()).string());
390 if (maxSdkVersion != -1) {
391 printf(" maxSdkVersion='%d'", maxSdkVersion);
392 }
393 printf("\n");
394}
395
Adam Lesinski58f1f362013-11-12 12:59:08 -0800396static void printUsesImpliedPermission(const String8& name, const String8& reason) {
397 printf("uses-implied-permission: name='%s' reason='%s'\n",
398 ResTable::normalizeForOutput(name.string()).string(),
399 ResTable::normalizeForOutput(reason.string()).string());
400}
401
Adam Lesinski94fc9122013-09-30 17:16:09 -0700402Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool offHost,
403 String8 *outError = NULL)
404{
405 Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER);
406 if (aidAsset == NULL) {
407 if (outError != NULL) *outError = "xml resource does not exist";
408 return Vector<String8>();
409 }
410
411 const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service");
412
413 bool withinApduService = false;
414 Vector<String8> categories;
415
416 String8 error;
417 ResXMLTree tree;
418 tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength());
419
420 size_t len;
421 int depth = 0;
422 ResXMLTree::event_code_t code;
423 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
424 if (code == ResXMLTree::END_TAG) {
425 depth--;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700426 const char16_t* ctag16 = tree.getElementName(&len);
427 if (ctag16 == NULL) {
428 *outError = "failed to get XML element name (bad string pool)";
429 return Vector<String8>();
430 }
431 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700432
433 if (depth == 0 && tag == serviceTagName) {
434 withinApduService = false;
435 }
436
437 } else if (code == ResXMLTree::START_TAG) {
438 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700439 const char16_t* ctag16 = tree.getElementName(&len);
440 if (ctag16 == NULL) {
441 *outError = "failed to get XML element name (bad string pool)";
442 return Vector<String8>();
443 }
444 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700445
446 if (depth == 1) {
447 if (tag == serviceTagName) {
448 withinApduService = true;
449 }
450 } else if (depth == 2 && withinApduService) {
451 if (tag == "aid-group") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700452 String8 category = AaptXml::getAttribute(tree, CATEGORY_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700453 if (error != "") {
454 if (outError != NULL) *outError = error;
455 return Vector<String8>();
456 }
457
458 categories.add(category);
459 }
460 }
461 }
462 }
463 aidAsset->close();
464 return categories;
465}
466
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700467static void printComponentPresence(const char* componentName) {
468 printf("provides-component:'%s'\n", componentName);
469}
470
Adam Lesinski2c72b682014-06-24 09:56:01 -0700471/**
472 * Represents a feature that has been automatically added due to
473 * a pre-requisite or some other reason.
474 */
475struct ImpliedFeature {
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800476 ImpliedFeature() : impliedBySdk23(false) {}
477 ImpliedFeature(const String8& n, bool sdk23) : name(n), impliedBySdk23(sdk23) {}
478
Adam Lesinski2c72b682014-06-24 09:56:01 -0700479 /**
480 * Name of the implied feature.
481 */
482 String8 name;
483
484 /**
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800485 * Was this implied by a permission from SDK 23 (<uses-permission-sdk-23 />)?
486 */
487 bool impliedBySdk23;
488
489 /**
Adam Lesinski2c72b682014-06-24 09:56:01 -0700490 * List of human-readable reasons for why this feature was implied.
491 */
492 SortedVector<String8> reasons;
493};
494
495/**
496 * Represents a <feature-group> tag in the AndroidManifest.xml
497 */
498struct FeatureGroup {
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700499 FeatureGroup() : openGLESVersion(-1) {}
500
Adam Lesinski2c72b682014-06-24 09:56:01 -0700501 /**
502 * Human readable label
503 */
504 String8 label;
505
506 /**
507 * Explicit features defined in the group
508 */
509 KeyedVector<String8, bool> features;
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700510
511 /**
512 * OpenGL ES version required
513 */
514 int openGLESVersion;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700515};
516
517static void addImpliedFeature(KeyedVector<String8, ImpliedFeature>* impliedFeatures,
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800518 const char* name, const char* reason, bool sdk23) {
Adam Lesinski2c72b682014-06-24 09:56:01 -0700519 String8 name8(name);
520 ssize_t idx = impliedFeatures->indexOfKey(name8);
521 if (idx < 0) {
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800522 idx = impliedFeatures->add(name8, ImpliedFeature(name8, sdk23));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700523 }
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800524
525 ImpliedFeature* feature = &impliedFeatures->editValueAt(idx);
526
527 // A non-sdk 23 implied feature takes precedence.
528 if (feature->impliedBySdk23 && !sdk23) {
529 feature->impliedBySdk23 = false;
530 }
531 feature->reasons.add(String8(reason));
Adam Lesinski2c72b682014-06-24 09:56:01 -0700532}
533
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800534static void printFeatureGroupImpl(const FeatureGroup& grp,
535 const KeyedVector<String8, ImpliedFeature>* impliedFeatures) {
Adam Lesinski2c72b682014-06-24 09:56:01 -0700536 printf("feature-group: label='%s'\n", grp.label.string());
537
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700538 if (grp.openGLESVersion > 0) {
539 printf(" uses-gl-es: '0x%x'\n", grp.openGLESVersion);
540 }
541
Adam Lesinski2c72b682014-06-24 09:56:01 -0700542 const size_t numFeatures = grp.features.size();
543 for (size_t i = 0; i < numFeatures; i++) {
Adam Lesinski73a05112014-12-08 12:53:17 -0800544 const bool required = grp.features[i];
Adam Lesinski2c72b682014-06-24 09:56:01 -0700545
546 const String8& featureName = grp.features.keyAt(i);
Adam Lesinski73a05112014-12-08 12:53:17 -0800547 printf(" uses-feature%s: name='%s'\n", (required ? "" : "-not-required"),
Adam Lesinski2c72b682014-06-24 09:56:01 -0700548 ResTable::normalizeForOutput(featureName.string()).string());
549 }
550
551 const size_t numImpliedFeatures =
552 (impliedFeatures != NULL) ? impliedFeatures->size() : 0;
553 for (size_t i = 0; i < numImpliedFeatures; i++) {
554 const ImpliedFeature& impliedFeature = impliedFeatures->valueAt(i);
555 if (grp.features.indexOfKey(impliedFeature.name) >= 0) {
556 // The feature is explicitly set, no need to use implied
557 // definition.
558 continue;
559 }
560
561 String8 printableFeatureName(ResTable::normalizeForOutput(
562 impliedFeature.name.string()));
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800563 const char* sdk23Suffix = impliedFeature.impliedBySdk23 ? "-sdk-23" : "";
564
565 printf(" uses-feature%s: name='%s'\n", sdk23Suffix, printableFeatureName.string());
566 printf(" uses-implied-feature%s: name='%s' reason='", sdk23Suffix,
567 printableFeatureName.string());
Adam Lesinski2c72b682014-06-24 09:56:01 -0700568 const size_t numReasons = impliedFeature.reasons.size();
569 for (size_t j = 0; j < numReasons; j++) {
570 printf("%s", impliedFeature.reasons[j].string());
571 if (j + 2 < numReasons) {
572 printf(", ");
573 } else if (j + 1 < numReasons) {
574 printf(", and ");
575 }
576 }
577 printf("'\n");
578 }
579}
580
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800581static void printFeatureGroup(const FeatureGroup& grp) {
582 printFeatureGroupImpl(grp, NULL);
583}
584
585static void printDefaultFeatureGroup(const FeatureGroup& grp,
586 const KeyedVector<String8, ImpliedFeature>& impliedFeatures) {
587 printFeatureGroupImpl(grp, &impliedFeatures);
588}
589
Adam Lesinski2c72b682014-06-24 09:56:01 -0700590static void addParentFeatures(FeatureGroup* grp, const String8& name) {
591 if (name == "android.hardware.camera.autofocus" ||
592 name == "android.hardware.camera.flash") {
593 grp->features.add(String8("android.hardware.camera"), true);
594 } else if (name == "android.hardware.location.gps" ||
595 name == "android.hardware.location.network") {
596 grp->features.add(String8("android.hardware.location"), true);
597 } else if (name == "android.hardware.touchscreen.multitouch") {
598 grp->features.add(String8("android.hardware.touchscreen"), true);
599 } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
600 grp->features.add(String8("android.hardware.touchscreen.multitouch"), true);
601 grp->features.add(String8("android.hardware.touchscreen"), true);
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700602 } else if (name == "android.hardware.opengles.aep") {
603 const int openGLESVersion31 = 0x00030001;
604 if (openGLESVersion31 > grp->openGLESVersion) {
605 grp->openGLESVersion = openGLESVersion31;
606 }
Adam Lesinski2c72b682014-06-24 09:56:01 -0700607 }
608}
609
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800610static void addImpliedFeaturesForPermission(const int targetSdk, const String8& name,
611 KeyedVector<String8, ImpliedFeature>* impliedFeatures,
612 bool impliedBySdk23Permission) {
613 if (name == "android.permission.CAMERA") {
614 addImpliedFeature(impliedFeatures, "android.hardware.camera",
615 String8::format("requested %s permission", name.string())
616 .string(), impliedBySdk23Permission);
617 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
618 addImpliedFeature(impliedFeatures, "android.hardware.location.gps",
619 String8::format("requested %s permission", name.string())
620 .string(), impliedBySdk23Permission);
621 addImpliedFeature(impliedFeatures, "android.hardware.location",
622 String8::format("requested %s permission", name.string())
623 .string(), impliedBySdk23Permission);
624 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
625 addImpliedFeature(impliedFeatures, "android.hardware.location",
626 String8::format("requested %s permission", name.string())
627 .string(), impliedBySdk23Permission);
628 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
629 addImpliedFeature(impliedFeatures, "android.hardware.location.network",
630 String8::format("requested %s permission", name.string())
631 .string(), impliedBySdk23Permission);
632 addImpliedFeature(impliedFeatures, "android.hardware.location",
633 String8::format("requested %s permission", name.string())
634 .string(), impliedBySdk23Permission);
635 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
636 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
637 addImpliedFeature(impliedFeatures, "android.hardware.location",
638 String8::format("requested %s permission", name.string())
639 .string(), impliedBySdk23Permission);
640 } else if (name == "android.permission.BLUETOOTH" ||
641 name == "android.permission.BLUETOOTH_ADMIN") {
642 if (targetSdk > 4) {
643 addImpliedFeature(impliedFeatures, "android.hardware.bluetooth",
644 String8::format("requested %s permission", name.string())
645 .string(), impliedBySdk23Permission);
646 addImpliedFeature(impliedFeatures, "android.hardware.bluetooth",
647 "targetSdkVersion > 4", impliedBySdk23Permission);
648 }
649 } else if (name == "android.permission.RECORD_AUDIO") {
650 addImpliedFeature(impliedFeatures, "android.hardware.microphone",
651 String8::format("requested %s permission", name.string())
652 .string(), impliedBySdk23Permission);
653 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
654 name == "android.permission.CHANGE_WIFI_STATE" ||
655 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
656 addImpliedFeature(impliedFeatures, "android.hardware.wifi",
657 String8::format("requested %s permission", name.string())
658 .string(), impliedBySdk23Permission);
659 } else if (name == "android.permission.CALL_PHONE" ||
660 name == "android.permission.CALL_PRIVILEGED" ||
661 name == "android.permission.MODIFY_PHONE_STATE" ||
662 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
663 name == "android.permission.READ_SMS" ||
664 name == "android.permission.RECEIVE_SMS" ||
665 name == "android.permission.RECEIVE_MMS" ||
666 name == "android.permission.RECEIVE_WAP_PUSH" ||
667 name == "android.permission.SEND_SMS" ||
668 name == "android.permission.WRITE_APN_SETTINGS" ||
669 name == "android.permission.WRITE_SMS") {
670 addImpliedFeature(impliedFeatures, "android.hardware.telephony",
671 String8("requested a telephony permission").string(),
672 impliedBySdk23Permission);
673 }
674}
675
Adam Lesinski282e1812014-01-23 18:17:42 -0800676/*
677 * Handle the "dump" command, to extract select data from an archive.
678 */
679extern char CONSOLE_DATA[2925]; // see EOF
680int doDump(Bundle* bundle)
681{
682 status_t result = UNKNOWN_ERROR;
Adam Lesinski282e1812014-01-23 18:17:42 -0800683
684 if (bundle->getFileSpecCount() < 1) {
685 fprintf(stderr, "ERROR: no dump option specified\n");
686 return 1;
687 }
688
689 if (bundle->getFileSpecCount() < 2) {
690 fprintf(stderr, "ERROR: no dump file specified\n");
691 return 1;
692 }
693
694 const char* option = bundle->getFileSpecEntry(0);
695 const char* filename = bundle->getFileSpecEntry(1);
696
697 AssetManager assets;
Narayan Kamathf85e41f2014-01-24 14:14:30 +0000698 int32_t assetsCookie;
Adam Lesinski282e1812014-01-23 18:17:42 -0800699 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
700 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
701 return 1;
702 }
703
704 // Make a dummy config for retrieving resources... we need to supply
705 // non-default values for some configs so that we can retrieve resources
706 // in the app that don't have a default. The most important of these is
707 // the API version because key resources like icons will have an implicit
708 // version if they are using newer config types like density.
709 ResTable_config config;
Narayan Kamath91447d82014-01-21 15:32:36 +0000710 memset(&config, 0, sizeof(ResTable_config));
Adam Lesinski282e1812014-01-23 18:17:42 -0800711 config.language[0] = 'e';
712 config.language[1] = 'n';
713 config.country[0] = 'U';
714 config.country[1] = 'S';
715 config.orientation = ResTable_config::ORIENTATION_PORT;
716 config.density = ResTable_config::DENSITY_MEDIUM;
717 config.sdkVersion = 10000; // Very high.
718 config.screenWidthDp = 320;
719 config.screenHeightDp = 480;
720 config.smallestScreenWidthDp = 320;
Adam Lesinskic2dea8d2014-08-04 16:40:41 -0700721 config.screenLayout |= ResTable_config::SCREENSIZE_NORMAL;
Adam Lesinski282e1812014-01-23 18:17:42 -0800722 assets.setConfiguration(config);
723
724 const ResTable& res = assets.getResources(false);
Dan Albert68001652014-09-09 09:51:01 -0700725 if (res.getError() != NO_ERROR) {
Adam Lesinski25e9d552014-05-19 15:01:43 -0700726 fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
Adam Lesinski63e646e2014-07-30 11:40:39 -0700727 return 1;
Adam Lesinski282e1812014-01-23 18:17:42 -0800728 }
729
Adam Lesinski2cb761e2014-08-15 13:59:02 -0700730 // The dynamicRefTable can be null if there are no resources for this asset cookie.
731 // This fine.
Adam Lesinski63e646e2014-07-30 11:40:39 -0700732 const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700733
734 Asset* asset = NULL;
735
Adam Lesinski282e1812014-01-23 18:17:42 -0800736 if (strcmp("resources", option) == 0) {
Elliott Hughesba3fe562015-08-12 14:49:53 -0700737#ifndef __ANDROID__
Adam Lesinski282e1812014-01-23 18:17:42 -0800738 res.print(bundle->getValues());
739#endif
740
741 } else if (strcmp("strings", option) == 0) {
742 const ResStringPool* pool = res.getTableStringBlock(0);
743 printStringPool(pool);
744
745 } else if (strcmp("xmltree", option) == 0) {
746 if (bundle->getFileSpecCount() < 3) {
747 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
748 goto bail;
749 }
750
751 for (int i=2; i<bundle->getFileSpecCount(); i++) {
752 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700753 ResXMLTree tree(dynamicRefTable);
754 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800755 if (asset == NULL) {
756 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
757 goto bail;
758 }
759
760 if (tree.setTo(asset->getBuffer(true),
761 asset->getLength()) != NO_ERROR) {
762 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
763 goto bail;
764 }
765 tree.restart();
766 printXMLBlock(&tree);
767 tree.uninit();
768 delete asset;
769 asset = NULL;
770 }
771
772 } else if (strcmp("xmlstrings", option) == 0) {
773 if (bundle->getFileSpecCount() < 3) {
774 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
775 goto bail;
776 }
777
778 for (int i=2; i<bundle->getFileSpecCount(); i++) {
779 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700780 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800781 if (asset == NULL) {
782 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
783 goto bail;
784 }
785
Adam Lesinski63e646e2014-07-30 11:40:39 -0700786 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800787 if (tree.setTo(asset->getBuffer(true),
788 asset->getLength()) != NO_ERROR) {
789 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
790 goto bail;
791 }
792 printStringPool(&tree.getStrings());
793 delete asset;
794 asset = NULL;
795 }
796
797 } else {
Adam Lesinski63e646e2014-07-30 11:40:39 -0700798 asset = assets.openNonAsset(assetsCookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800799 if (asset == NULL) {
800 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
801 goto bail;
802 }
803
Adam Lesinski63e646e2014-07-30 11:40:39 -0700804 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800805 if (tree.setTo(asset->getBuffer(true),
806 asset->getLength()) != NO_ERROR) {
807 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
808 goto bail;
809 }
810 tree.restart();
811
812 if (strcmp("permissions", option) == 0) {
813 size_t len;
814 ResXMLTree::event_code_t code;
815 int depth = 0;
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800816 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
817 code != ResXMLTree::BAD_DOCUMENT) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800818 if (code == ResXMLTree::END_TAG) {
819 depth--;
820 continue;
821 }
822 if (code != ResXMLTree::START_TAG) {
823 continue;
824 }
825 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700826 const char16_t* ctag16 = tree.getElementName(&len);
827 if (ctag16 == NULL) {
828 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
829 goto bail;
830 }
831 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800832 //printf("Depth %d tag %s\n", depth, tag.string());
833 if (depth == 1) {
834 if (tag != "manifest") {
835 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
836 goto bail;
837 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700838 String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -0700839 printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -0800840 } else if (depth == 2) {
841 if (tag == "permission") {
842 String8 error;
843 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
844 if (error != "") {
845 fprintf(stderr, "ERROR: %s\n", error.string());
846 goto bail;
847 }
848
849 if (name == "") {
850 fprintf(stderr, "ERROR: missing 'android:name' for permission\n");
851 goto bail;
852 }
853 printf("permission: %s\n",
854 ResTable::normalizeForOutput(name.string()).string());
855 } else if (tag == "uses-permission") {
856 String8 error;
857 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
858 if (error != "") {
859 fprintf(stderr, "ERROR: %s\n", error.string());
860 goto bail;
861 }
862
863 if (name == "") {
864 fprintf(stderr, "ERROR: missing 'android:name' for uses-permission\n");
865 goto bail;
866 }
867 printUsesPermission(name,
868 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
869 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
870 } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
871 String8 error;
872 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
873 if (error != "") {
874 fprintf(stderr, "ERROR: %s\n", error.string());
875 goto bail;
876 }
877
878 if (name == "") {
879 fprintf(stderr, "ERROR: missing 'android:name' for "
880 "uses-permission-sdk-23\n");
881 goto bail;
882 }
883 printUsesPermissionSdk23(
884 name,
885 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
Adam Lesinski282e1812014-01-23 18:17:42 -0800886 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800887 }
888 }
889 } else if (strcmp("badging", option) == 0) {
890 Vector<String8> locales;
891 res.getLocales(&locales);
892
893 Vector<ResTable_config> configs;
894 res.getConfigurations(&configs);
895 SortedVector<int> densities;
896 const size_t NC = configs.size();
897 for (size_t i=0; i<NC; i++) {
898 int dens = configs[i].density;
899 if (dens == 0) {
900 dens = 160;
901 }
902 densities.add(dens);
903 }
904
905 size_t len;
906 ResXMLTree::event_code_t code;
907 int depth = 0;
908 String8 error;
909 bool withinActivity = false;
910 bool isMainActivity = false;
911 bool isLauncherActivity = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800912 bool isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800913 bool isSearchable = false;
914 bool withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700915 bool withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700916 bool withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800917 bool withinReceiver = false;
918 bool withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700919 bool withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800920 bool withinIntentFilter = false;
921 bool hasMainActivity = false;
922 bool hasOtherActivities = false;
923 bool hasOtherReceivers = false;
924 bool hasOtherServices = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700925 bool hasIntentFilter = false;
926
Adam Lesinski282e1812014-01-23 18:17:42 -0800927 bool hasWallpaperService = false;
928 bool hasImeService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700929 bool hasAccessibilityService = false;
930 bool hasPrintService = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800931 bool hasWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700932 bool hasDeviceAdminReceiver = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700933 bool hasPaymentService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700934 bool hasDocumentsProvider = false;
935 bool hasCameraActivity = false;
936 bool hasCameraSecureActivity = false;
937 bool hasLauncher = false;
938 bool hasNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400939 bool hasDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700940
Adam Lesinski282e1812014-01-23 18:17:42 -0800941 bool actMainActivity = false;
942 bool actWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700943 bool actDeviceAdminEnabled = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800944 bool actImeService = false;
945 bool actWallpaperService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700946 bool actAccessibilityService = false;
947 bool actPrintService = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700948 bool actHostApduService = false;
949 bool actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700950 bool actDocumentsProvider = false;
951 bool actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400952 bool actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700953 bool actCamera = false;
954 bool actCameraSecure = false;
955 bool catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700956 bool hasMetaHostPaymentCategory = false;
957 bool hasMetaOffHostPaymentCategory = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700958
959 // These permissions are required by services implementing services
960 // the system binds to (IME, Accessibility, PrintServices, etc.)
961 bool hasBindDeviceAdminPermission = false;
962 bool hasBindInputMethodPermission = false;
963 bool hasBindAccessibilityServicePermission = false;
964 bool hasBindPrintServicePermission = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700965 bool hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700966 bool hasRequiredSafAttributes = false;
967 bool hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400968 bool hasBindDreamServicePermission = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800969
970 // These two implement the implicit permissions that are granted
971 // to pre-1.6 applications.
972 bool hasWriteExternalStoragePermission = false;
973 bool hasReadPhoneStatePermission = false;
974
975 // If an app requests write storage, they will also get read storage.
976 bool hasReadExternalStoragePermission = false;
977
978 // Implement transition to read and write call log.
979 bool hasReadContactsPermission = false;
980 bool hasWriteContactsPermission = false;
981 bool hasReadCallLogPermission = false;
982 bool hasWriteCallLogPermission = false;
983
Adam Lesinskie47fd122014-08-15 22:25:36 -0700984 // If an app declares itself as multiArch, we report the
985 // native libraries differently.
986 bool hasMultiArch = false;
987
Adam Lesinski282e1812014-01-23 18:17:42 -0800988 // This next group of variables is used to implement a group of
989 // backward-compatibility heuristics necessitated by the addition of
990 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
991 // heuristic is "if an app requests a permission but doesn't explicitly
992 // request the corresponding <uses-feature>, presume it's there anyway".
Adam Lesinski2c72b682014-06-24 09:56:01 -0700993
Adam Lesinski282e1812014-01-23 18:17:42 -0800994 // 2.2 also added some other features that apps can request, but that
995 // have no corresponding permission, so we cannot implement any
996 // back-compatibility heuristic for them. The below are thus unnecessary
997 // (but are retained here for documentary purposes.)
998 //bool specCompassFeature = false;
999 //bool specAccelerometerFeature = false;
1000 //bool specProximityFeature = false;
1001 //bool specAmbientLightFeature = false;
1002 //bool specLiveWallpaperFeature = false;
1003
1004 int targetSdk = 0;
1005 int smallScreen = 1;
1006 int normalScreen = 1;
1007 int largeScreen = 1;
1008 int xlargeScreen = 1;
1009 int anyDensity = 1;
1010 int requiresSmallestWidthDp = 0;
1011 int compatibleWidthLimitDp = 0;
1012 int largestWidthLimitDp = 0;
1013 String8 pkg;
1014 String8 activityName;
1015 String8 activityLabel;
1016 String8 activityIcon;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001017 String8 activityBanner;
Adam Lesinski282e1812014-01-23 18:17:42 -08001018 String8 receiverName;
1019 String8 serviceName;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001020 Vector<String8> supportedInput;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001021
1022 FeatureGroup commonFeatures;
1023 Vector<FeatureGroup> featureGroups;
1024 KeyedVector<String8, ImpliedFeature> impliedFeatures;
1025
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001026 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
1027 code != ResXMLTree::BAD_DOCUMENT) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001028 if (code == ResXMLTree::END_TAG) {
1029 depth--;
1030 if (depth < 2) {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001031 if (withinSupportsInput && !supportedInput.isEmpty()) {
1032 printf("supports-input: '");
1033 const size_t N = supportedInput.size();
1034 for (size_t i=0; i<N; i++) {
Maurice Chu2675f762013-10-22 17:33:11 -07001035 printf("%s", ResTable::normalizeForOutput(
1036 supportedInput[i].string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001037 if (i != N - 1) {
1038 printf("' '");
1039 } else {
1040 printf("'\n");
1041 }
1042 }
1043 supportedInput.clear();
1044 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001045 withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001046 withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001047 withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001048 } else if (depth < 3) {
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001049 if (withinActivity && isMainActivity) {
Maurice Chu2675f762013-10-22 17:33:11 -07001050 String8 aName(getComponentName(pkg, activityName));
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001051 if (isLauncherActivity) {
1052 printf("launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001053 if (aName.length() > 0) {
1054 printf(" name='%s' ",
1055 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001056 }
1057 printf(" label='%s' icon='%s'\n",
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001058 ResTable::normalizeForOutput(activityLabel.string())
1059 .string(),
1060 ResTable::normalizeForOutput(activityIcon.string())
1061 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001062 }
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001063 if (isLeanbackLauncherActivity) {
1064 printf("leanback-launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001065 if (aName.length() > 0) {
1066 printf(" name='%s' ",
1067 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001068 }
1069 printf(" label='%s' icon='%s' banner='%s'\n",
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001070 ResTable::normalizeForOutput(activityLabel.string())
1071 .string(),
1072 ResTable::normalizeForOutput(activityIcon.string())
1073 .string(),
1074 ResTable::normalizeForOutput(activityBanner.string())
1075 .string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001076 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001077 }
1078 if (!hasIntentFilter) {
1079 hasOtherActivities |= withinActivity;
1080 hasOtherReceivers |= withinReceiver;
1081 hasOtherServices |= withinService;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001082 } else {
1083 if (withinService) {
1084 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
1085 hasBindNfcServicePermission);
1086 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
1087 hasBindNfcServicePermission);
1088 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001089 }
1090 withinActivity = false;
1091 withinService = false;
1092 withinReceiver = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001093 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001094 hasIntentFilter = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001095 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001096 } else if (depth < 4) {
1097 if (withinIntentFilter) {
1098 if (withinActivity) {
1099 hasMainActivity |= actMainActivity;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001100 hasLauncher |= catLauncher;
1101 hasCameraActivity |= actCamera;
1102 hasCameraSecureActivity |= actCameraSecure;
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001103 hasOtherActivities |=
1104 !actMainActivity && !actCamera && !actCameraSecure;
Adam Lesinski282e1812014-01-23 18:17:42 -08001105 } else if (withinReceiver) {
1106 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001107 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
1108 hasBindDeviceAdminPermission);
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001109 hasOtherReceivers |=
1110 (!actWidgetReceivers && !actDeviceAdminEnabled);
Adam Lesinski282e1812014-01-23 18:17:42 -08001111 } else if (withinService) {
1112 hasImeService |= actImeService;
1113 hasWallpaperService |= actWallpaperService;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001114 hasAccessibilityService |= (actAccessibilityService &&
1115 hasBindAccessibilityServicePermission);
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001116 hasPrintService |=
1117 (actPrintService && hasBindPrintServicePermission);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001118 hasNotificationListenerService |= actNotificationListenerService &&
1119 hasBindNotificationListenerServicePermission;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001120 hasDreamService |= actDreamService && hasBindDreamServicePermission;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001121 hasOtherServices |= (!actImeService && !actWallpaperService &&
Adam Lesinski94fc9122013-09-30 17:16:09 -07001122 !actAccessibilityService && !actPrintService &&
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001123 !actHostApduService && !actOffHostApduService &&
1124 !actNotificationListenerService);
1125 } else if (withinProvider) {
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001126 hasDocumentsProvider |=
1127 actDocumentsProvider && hasRequiredSafAttributes;
Adam Lesinski282e1812014-01-23 18:17:42 -08001128 }
1129 }
1130 withinIntentFilter = false;
1131 }
1132 continue;
1133 }
1134 if (code != ResXMLTree::START_TAG) {
1135 continue;
1136 }
1137 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001138
1139 const char16_t* ctag16 = tree.getElementName(&len);
1140 if (ctag16 == NULL) {
1141 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
1142 goto bail;
1143 }
1144 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -08001145 //printf("Depth %d, %s\n", depth, tag.string());
1146 if (depth == 1) {
1147 if (tag != "manifest") {
1148 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
1149 goto bail;
1150 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001151 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -07001152 printf("package: name='%s' ",
1153 ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001154 int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR,
1155 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001156 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001157 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n",
1158 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001159 goto bail;
1160 }
1161 if (versionCode > 0) {
1162 printf("versionCode='%d' ", versionCode);
1163 } else {
1164 printf("versionCode='' ");
1165 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001166 String8 versionName = AaptXml::getResolvedAttribute(res, tree,
1167 VERSION_NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001168 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001169 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n",
1170 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001171 goto bail;
1172 }
Adam Lesinski25d35a92014-08-11 09:41:56 -07001173 printf("versionName='%s'",
Maurice Chu2675f762013-10-22 17:33:11 -07001174 ResTable::normalizeForOutput(versionName.string()).string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001175
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001176 String8 splitName = AaptXml::getAttribute(tree, NULL, "split");
Adam Lesinski25d35a92014-08-11 09:41:56 -07001177 if (!splitName.isEmpty()) {
1178 printf(" split='%s'", ResTable::normalizeForOutput(
1179 splitName.string()).string());
1180 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001181
Adam Lesinski5283fab2014-08-29 11:23:55 -07001182 String8 platformVersionName = AaptXml::getAttribute(tree, NULL,
1183 "platformBuildVersionName");
1184 printf(" platformBuildVersionName='%s'", platformVersionName.string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001185 printf("\n");
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001186
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001187 int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
1188 INSTALL_LOCATION_ATTR, &error);
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001189 if (error != "") {
1190 fprintf(stderr, "ERROR getting 'android:installLocation' attribute: %s\n",
1191 error.string());
1192 goto bail;
1193 }
1194
1195 if (installLocation >= 0) {
1196 printf("install-location:'");
1197 switch (installLocation) {
1198 case 0:
1199 printf("auto");
1200 break;
1201 case 1:
1202 printf("internalOnly");
1203 break;
1204 case 2:
1205 printf("preferExternal");
1206 break;
1207 default:
1208 fprintf(stderr, "Invalid installLocation %d\n", installLocation);
1209 goto bail;
1210 }
1211 printf("'\n");
1212 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001213 } else if (depth == 2) {
1214 withinApplication = false;
1215 if (tag == "application") {
1216 withinApplication = true;
1217
1218 String8 label;
1219 const size_t NL = locales.size();
1220 for (size_t i=0; i<NL; i++) {
1221 const char* localeStr = locales[i].string();
1222 assets.setLocale(localeStr != NULL ? localeStr : "");
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001223 String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1224 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001225 if (llabel != "") {
1226 if (localeStr == NULL || strlen(localeStr) == 0) {
1227 label = llabel;
Maurice Chu2675f762013-10-22 17:33:11 -07001228 printf("application-label:'%s'\n",
1229 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001230 } else {
1231 if (label == "") {
1232 label = llabel;
1233 }
1234 printf("application-label-%s:'%s'\n", localeStr,
Maurice Chu2675f762013-10-22 17:33:11 -07001235 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001236 }
1237 }
1238 }
1239
1240 ResTable_config tmpConfig = config;
1241 const size_t ND = densities.size();
1242 for (size_t i=0; i<ND; i++) {
1243 tmpConfig.density = densities[i];
1244 assets.setConfiguration(tmpConfig);
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001245 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1246 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001247 if (icon != "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001248 printf("application-icon-%d:'%s'\n", densities[i],
1249 ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001250 }
1251 }
1252 assets.setConfiguration(config);
1253
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001254 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001255 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001256 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1257 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001258 goto bail;
1259 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001260 int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0,
1261 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001262 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001263 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n",
1264 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001265 goto bail;
1266 }
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001267
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001268 String8 banner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1269 &error);
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001270 if (error != "") {
1271 fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
1272 error.string());
1273 goto bail;
1274 }
Maurice Chu2675f762013-10-22 17:33:11 -07001275 printf("application: label='%s' ",
1276 ResTable::normalizeForOutput(label.string()).string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001277 printf("icon='%s'", ResTable::normalizeForOutput(icon.string()).string());
1278 if (banner != "") {
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001279 printf(" banner='%s'",
1280 ResTable::normalizeForOutput(banner.string()).string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001281 }
1282 printf("\n");
Adam Lesinski282e1812014-01-23 18:17:42 -08001283 if (testOnly != 0) {
1284 printf("testOnly='%d'\n", testOnly);
1285 }
1286
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001287 int32_t isGame = AaptXml::getResolvedIntegerAttribute(res, tree,
1288 ISGAME_ATTR, 0, &error);
1289 if (error != "") {
1290 fprintf(stderr, "ERROR getting 'android:isGame' attribute: %s\n",
1291 error.string());
1292 goto bail;
1293 }
1294 if (isGame != 0) {
1295 printf("application-isGame\n");
1296 }
1297
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001298 int32_t debuggable = AaptXml::getResolvedIntegerAttribute(res, tree,
1299 DEBUGGABLE_ATTR, 0, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001300 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001301 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n",
1302 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001303 goto bail;
1304 }
1305 if (debuggable != 0) {
1306 printf("application-debuggable\n");
1307 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07001308
1309 // We must search by name because the multiArch flag hasn't been API
1310 // frozen yet.
1311 int32_t multiArchIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
1312 "multiArch");
1313 if (multiArchIndex >= 0) {
1314 Res_value value;
1315 if (tree.getAttributeValue(multiArchIndex, &value) != NO_ERROR) {
1316 if (value.dataType >= Res_value::TYPE_FIRST_INT &&
1317 value.dataType <= Res_value::TYPE_LAST_INT) {
1318 hasMultiArch = value.data;
1319 }
1320 }
1321 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001322 } else if (tag == "uses-sdk") {
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001323 int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR,
1324 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001325 if (error != "") {
1326 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001327 String8 name = AaptXml::getResolvedAttribute(res, tree,
1328 MIN_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001329 if (error != "") {
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001330 fprintf(stderr,
1331 "ERROR getting 'android:minSdkVersion' attribute: %s\n",
Adam Lesinski282e1812014-01-23 18:17:42 -08001332 error.string());
1333 goto bail;
1334 }
1335 if (name == "Donut") targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001336 printf("sdkVersion:'%s'\n",
1337 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001338 } else if (code != -1) {
1339 targetSdk = code;
1340 printf("sdkVersion:'%d'\n", code);
1341 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001342 code = AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -08001343 if (code != -1) {
1344 printf("maxSdkVersion:'%d'\n", code);
1345 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001346 code = AaptXml::getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001347 if (error != "") {
1348 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001349 String8 name = AaptXml::getResolvedAttribute(res, tree,
1350 TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001351 if (error != "") {
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001352 fprintf(stderr,
1353 "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
Adam Lesinski282e1812014-01-23 18:17:42 -08001354 error.string());
1355 goto bail;
1356 }
1357 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001358 printf("targetSdkVersion:'%s'\n",
1359 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001360 } else if (code != -1) {
1361 if (targetSdk < code) {
1362 targetSdk = code;
1363 }
1364 printf("targetSdkVersion:'%d'\n", code);
1365 }
1366 } else if (tag == "uses-configuration") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001367 int32_t reqTouchScreen = AaptXml::getIntegerAttribute(tree,
1368 REQ_TOUCH_SCREEN_ATTR, 0);
1369 int32_t reqKeyboardType = AaptXml::getIntegerAttribute(tree,
1370 REQ_KEYBOARD_TYPE_ATTR, 0);
1371 int32_t reqHardKeyboard = AaptXml::getIntegerAttribute(tree,
1372 REQ_HARD_KEYBOARD_ATTR, 0);
1373 int32_t reqNavigation = AaptXml::getIntegerAttribute(tree,
1374 REQ_NAVIGATION_ATTR, 0);
1375 int32_t reqFiveWayNav = AaptXml::getIntegerAttribute(tree,
1376 REQ_FIVE_WAY_NAV_ATTR, 0);
Adam Lesinski282e1812014-01-23 18:17:42 -08001377 printf("uses-configuration:");
1378 if (reqTouchScreen != 0) {
1379 printf(" reqTouchScreen='%d'", reqTouchScreen);
1380 }
1381 if (reqKeyboardType != 0) {
1382 printf(" reqKeyboardType='%d'", reqKeyboardType);
1383 }
1384 if (reqHardKeyboard != 0) {
1385 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1386 }
1387 if (reqNavigation != 0) {
1388 printf(" reqNavigation='%d'", reqNavigation);
1389 }
1390 if (reqFiveWayNav != 0) {
1391 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1392 }
1393 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001394 } else if (tag == "supports-input") {
1395 withinSupportsInput = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001396 } else if (tag == "supports-screens") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001397 smallScreen = AaptXml::getIntegerAttribute(tree,
1398 SMALL_SCREEN_ATTR, 1);
1399 normalScreen = AaptXml::getIntegerAttribute(tree,
1400 NORMAL_SCREEN_ATTR, 1);
1401 largeScreen = AaptXml::getIntegerAttribute(tree,
1402 LARGE_SCREEN_ATTR, 1);
1403 xlargeScreen = AaptXml::getIntegerAttribute(tree,
1404 XLARGE_SCREEN_ATTR, 1);
1405 anyDensity = AaptXml::getIntegerAttribute(tree,
1406 ANY_DENSITY_ATTR, 1);
1407 requiresSmallestWidthDp = AaptXml::getIntegerAttribute(tree,
1408 REQUIRES_SMALLEST_WIDTH_DP_ATTR, 0);
1409 compatibleWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1410 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, 0);
1411 largestWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1412 LARGEST_WIDTH_LIMIT_DP_ATTR, 0);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001413 } else if (tag == "feature-group") {
1414 withinFeatureGroup = true;
1415 FeatureGroup group;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001416 group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001417 if (error != "") {
1418 fprintf(stderr, "ERROR getting 'android:label' attribute:"
1419 " %s\n", error.string());
1420 goto bail;
1421 }
1422 featureGroups.add(group);
1423
Adam Lesinski282e1812014-01-23 18:17:42 -08001424 } else if (tag == "uses-feature") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001425 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001426 if (name != "" && error == "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001427 int req = AaptXml::getIntegerAttribute(tree,
1428 REQUIRED_ATTR, 1);
Adam Lesinski282e1812014-01-23 18:17:42 -08001429
Adam Lesinski2c72b682014-06-24 09:56:01 -07001430 commonFeatures.features.add(name, req);
1431 if (req) {
1432 addParentFeatures(&commonFeatures, name);
Adam Lesinski282e1812014-01-23 18:17:42 -08001433 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001434 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001435 int vers = AaptXml::getIntegerAttribute(tree,
Adam Lesinski282e1812014-01-23 18:17:42 -08001436 GL_ES_VERSION_ATTR, &error);
1437 if (error == "") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001438 if (vers > commonFeatures.openGLESVersion) {
1439 commonFeatures.openGLESVersion = vers;
1440 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001441 }
1442 }
1443 } else if (tag == "uses-permission") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001444 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001445 if (error != "") {
Adam Lesinski282e1812014-01-23 18:17:42 -08001446 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1447 error.string());
1448 goto bail;
1449 }
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001450
1451 if (name == "") {
1452 fprintf(stderr, "ERROR: missing 'android:name' for uses-permission\n");
1453 goto bail;
1454 }
1455
1456 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, false);
1457
1458 if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1459 hasWriteExternalStoragePermission = true;
1460 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1461 hasReadExternalStoragePermission = true;
1462 } else if (name == "android.permission.READ_PHONE_STATE") {
1463 hasReadPhoneStatePermission = true;
1464 } else if (name == "android.permission.READ_CONTACTS") {
1465 hasReadContactsPermission = true;
1466 } else if (name == "android.permission.WRITE_CONTACTS") {
1467 hasWriteContactsPermission = true;
1468 } else if (name == "android.permission.READ_CALL_LOG") {
1469 hasReadCallLogPermission = true;
1470 } else if (name == "android.permission.WRITE_CALL_LOG") {
1471 hasWriteCallLogPermission = true;
1472 }
1473
1474 printUsesPermission(name,
1475 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
1476 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
1477
1478 } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
1479 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1480 if (error != "") {
1481 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1482 error.string());
1483 goto bail;
1484 }
1485
1486 if (name == "") {
1487 fprintf(stderr, "ERROR: missing 'android:name' for "
1488 "uses-permission-sdk-23\n");
1489 goto bail;
1490 }
1491
1492 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, true);
1493
1494 printUsesPermissionSdk23(
1495 name, AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
1496
Adam Lesinski282e1812014-01-23 18:17:42 -08001497 } else if (tag == "uses-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001498 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001499 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001500 printf("uses-package:'%s'\n",
1501 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001502 } else {
1503 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1504 error.string());
1505 goto bail;
1506 }
1507 } else if (tag == "original-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001508 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001509 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001510 printf("original-package:'%s'\n",
1511 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001512 } else {
1513 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1514 error.string());
1515 goto bail;
1516 }
1517 } else if (tag == "supports-gl-texture") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001518 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001519 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001520 printf("supports-gl-texture:'%s'\n",
1521 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001522 } else {
1523 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1524 error.string());
1525 goto bail;
1526 }
1527 } else if (tag == "compatible-screens") {
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001528 printCompatibleScreens(tree, &error);
1529 if (error != "") {
1530 fprintf(stderr, "ERROR getting compatible screens: %s\n",
1531 error.string());
1532 goto bail;
1533 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001534 depth--;
1535 } else if (tag == "package-verifier") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001536 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001537 if (name != "" && error == "") {
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001538 String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR,
1539 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001540 if (publicKey != "" && error == "") {
1541 printf("package-verifier: name='%s' publicKey='%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001542 ResTable::normalizeForOutput(name.string()).string(),
1543 ResTable::normalizeForOutput(publicKey.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001544 }
1545 }
1546 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001547 } else if (depth == 3) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001548 withinActivity = false;
1549 withinReceiver = false;
1550 withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001551 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001552 hasIntentFilter = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001553 hasMetaHostPaymentCategory = false;
1554 hasMetaOffHostPaymentCategory = false;
1555 hasBindDeviceAdminPermission = false;
1556 hasBindInputMethodPermission = false;
1557 hasBindAccessibilityServicePermission = false;
1558 hasBindPrintServicePermission = false;
1559 hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001560 hasRequiredSafAttributes = false;
1561 hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001562 hasBindDreamServicePermission = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001563 if (withinApplication) {
1564 if(tag == "activity") {
1565 withinActivity = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001566 activityName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001567 if (error != "") {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001568 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1569 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001570 goto bail;
1571 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001572
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001573 activityLabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1574 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001575 if (error != "") {
1576 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
1577 error.string());
1578 goto bail;
1579 }
1580
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001581 activityIcon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1582 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001583 if (error != "") {
1584 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1585 error.string());
1586 goto bail;
1587 }
1588
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001589 activityBanner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1590 &error);
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001591 if (error != "") {
1592 fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
1593 error.string());
1594 goto bail;
1595 }
1596
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001597 int32_t orien = AaptXml::getResolvedIntegerAttribute(res, tree,
Michael Wrightec4fdec2013-09-06 16:50:52 -07001598 SCREEN_ORIENTATION_ATTR, &error);
1599 if (error == "") {
1600 if (orien == 0 || orien == 6 || orien == 8) {
1601 // Requests landscape, sensorLandscape, or reverseLandscape.
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001602 addImpliedFeature(&impliedFeatures,
1603 "android.hardware.screen.landscape",
1604 "one or more activities have specified a "
1605 "landscape orientation",
1606 false);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001607 } else if (orien == 1 || orien == 7 || orien == 9) {
1608 // Requests portrait, sensorPortrait, or reversePortrait.
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001609 addImpliedFeature(&impliedFeatures,
1610 "android.hardware.screen.portrait",
1611 "one or more activities have specified a "
1612 "portrait orientation",
1613 false);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001614 }
1615 }
1616 } else if (tag == "uses-library") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001617 String8 libraryName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001618 if (error != "") {
1619 fprintf(stderr,
1620 "ERROR getting 'android:name' attribute for uses-library"
1621 " %s\n", error.string());
1622 goto bail;
1623 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001624 int req = AaptXml::getIntegerAttribute(tree,
1625 REQUIRED_ATTR, 1);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001626 printf("uses-library%s:'%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001627 req ? "" : "-not-required", ResTable::normalizeForOutput(
1628 libraryName.string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001629 } else if (tag == "receiver") {
1630 withinReceiver = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001631 receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001632
1633 if (error != "") {
1634 fprintf(stderr,
1635 "ERROR getting 'android:name' attribute for receiver:"
1636 " %s\n", error.string());
1637 goto bail;
1638 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001639
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001640 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1641 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001642 if (error == "") {
1643 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1644 hasBindDeviceAdminPermission = true;
1645 }
1646 } else {
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001647 fprintf(stderr,
1648 "ERROR getting 'android:permission' attribute for"
1649 " receiver '%s': %s\n",
1650 receiverName.string(), error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001651 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001652 } else if (tag == "service") {
1653 withinService = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001654 serviceName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001655
1656 if (error != "") {
1657 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1658 "service:%s\n", error.string());
1659 goto bail;
1660 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001661
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001662 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1663 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001664 if (error == "") {
1665 if (permission == "android.permission.BIND_INPUT_METHOD") {
1666 hasBindInputMethodPermission = true;
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001667 } else if (permission ==
1668 "android.permission.BIND_ACCESSIBILITY_SERVICE") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001669 hasBindAccessibilityServicePermission = true;
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001670 } else if (permission ==
1671 "android.permission.BIND_PRINT_SERVICE") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001672 hasBindPrintServicePermission = true;
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001673 } else if (permission ==
1674 "android.permission.BIND_NFC_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07001675 hasBindNfcServicePermission = true;
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001676 } else if (permission ==
1677 "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001678 hasBindNotificationListenerServicePermission = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001679 } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
1680 hasBindDreamServicePermission = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001681 }
1682 } else {
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001683 fprintf(stderr, "ERROR getting 'android:permission' attribute for "
1684 "service '%s': %s\n", serviceName.string(), error.string());
Adam Lesinskia5018c92013-09-30 16:23:15 -07001685 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001686 } else if (tag == "provider") {
1687 withinProvider = true;
1688
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001689 bool exported = AaptXml::getResolvedIntegerAttribute(res, tree,
1690 EXPORTED_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001691 if (error != "") {
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001692 fprintf(stderr,
1693 "ERROR getting 'android:exported' attribute for provider:"
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001694 " %s\n", error.string());
1695 goto bail;
1696 }
1697
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001698 bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute(
1699 res, tree, GRANT_URI_PERMISSIONS_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001700 if (error != "") {
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001701 fprintf(stderr,
1702 "ERROR getting 'android:grantUriPermissions' attribute for "
1703 "provider: %s\n", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001704 goto bail;
1705 }
1706
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001707 String8 permission = AaptXml::getResolvedAttribute(res, tree,
1708 PERMISSION_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001709 if (error != "") {
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001710 fprintf(stderr, "ERROR getting 'android:permission' attribute for "
1711 "provider: %s\n", error.string());
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001712 goto bail;
1713 }
1714
1715 hasRequiredSafAttributes |= exported && grantUriPermissions &&
1716 permission == "android.permission.MANAGE_DOCUMENTS";
1717
Michael Wrightec4fdec2013-09-06 16:50:52 -07001718 } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001719 String8 metaDataName = AaptXml::getResolvedAttribute(res, tree,
1720 NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001721 if (error != "") {
1722 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1723 "meta-data:%s\n", error.string());
1724 goto bail;
1725 }
Maurice Chu2675f762013-10-22 17:33:11 -07001726 printf("meta-data: name='%s' ",
1727 ResTable::normalizeForOutput(metaDataName.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001728 printResolvedResourceAttribute(res, tree, VALUE_ATTR, String8("value"),
Maurice Chu76327312013-10-16 18:28:46 -07001729 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001730 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001731 // Try looking for a RESOURCE_ATTR
1732 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001733 printResolvedResourceAttribute(res, tree, RESOURCE_ATTR,
Maurice Chu76327312013-10-16 18:28:46 -07001734 String8("resource"), &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001735 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001736 fprintf(stderr, "ERROR getting 'android:value' or "
1737 "'android:resource' attribute for "
1738 "meta-data:%s\n", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001739 goto bail;
1740 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001741 }
Maurice Chu76327312013-10-16 18:28:46 -07001742 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001743 } else if (withinSupportsInput && tag == "input-type") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001744 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001745 if (name != "" && error == "") {
1746 supportedInput.add(name);
1747 } else {
1748 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1749 error.string());
1750 goto bail;
1751 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001752 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001753 } else if (withinFeatureGroup && tag == "uses-feature") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001754 FeatureGroup& top = featureGroups.editTop();
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001755
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001756 String8 name = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001757 if (name != "" && error == "") {
Adam Lesinskid3edfde2014-08-08 17:32:44 -07001758 top.features.add(name, true);
1759 addParentFeatures(&top, name);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001760 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001761 int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR,
1762 &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001763 if (error == "") {
1764 if (vers > top.openGLESVersion) {
1765 top.openGLESVersion = vers;
1766 }
1767 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001768 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001769 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001770 } else if (depth == 4) {
1771 if (tag == "intent-filter") {
1772 hasIntentFilter = true;
1773 withinIntentFilter = true;
1774 actMainActivity = false;
1775 actWidgetReceivers = false;
1776 actImeService = false;
1777 actWallpaperService = false;
1778 actAccessibilityService = false;
1779 actPrintService = false;
1780 actDeviceAdminEnabled = false;
1781 actHostApduService = false;
1782 actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001783 actDocumentsProvider = false;
1784 actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001785 actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001786 actCamera = false;
1787 actCameraSecure = false;
1788 catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001789 } else if (withinService && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001790 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001791 if (error != "") {
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001792 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1793 "meta-data tag in service '%s': %s\n", serviceName.string(),
1794 error.string());
Adam Lesinski94fc9122013-09-30 17:16:09 -07001795 goto bail;
1796 }
1797
1798 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1799 name == "android.nfc.cardemulation.off_host_apdu_service") {
1800 bool offHost = true;
1801 if (name == "android.nfc.cardemulation.host_apdu_service") {
1802 offHost = false;
1803 }
1804
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001805 String8 xmlPath = AaptXml::getResolvedAttribute(res, tree,
1806 RESOURCE_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001807 if (error != "") {
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001808 fprintf(stderr, "ERROR getting 'android:resource' attribute for "
1809 "meta-data tag in service '%s': %s\n",
1810 serviceName.string(), error.string());
Adam Lesinski94fc9122013-09-30 17:16:09 -07001811 goto bail;
1812 }
1813
1814 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1815 offHost, &error);
1816 if (error != "") {
1817 fprintf(stderr, "ERROR getting AID category for service '%s'\n",
1818 serviceName.string());
1819 goto bail;
1820 }
1821
1822 const size_t catLen = categories.size();
1823 for (size_t i = 0; i < catLen; i++) {
1824 bool paymentCategory = (categories[i] == "payment");
1825 if (offHost) {
1826 hasMetaOffHostPaymentCategory |= paymentCategory;
1827 } else {
1828 hasMetaHostPaymentCategory |= paymentCategory;
1829 }
1830 }
1831 }
1832 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001833 } else if ((depth == 5) && withinIntentFilter) {
1834 String8 action;
1835 if (tag == "action") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001836 action = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001837 if (error != "") {
1838 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1839 error.string());
1840 goto bail;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001841 }
1842
Adam Lesinskia5018c92013-09-30 16:23:15 -07001843 if (withinActivity) {
1844 if (action == "android.intent.action.MAIN") {
1845 isMainActivity = true;
1846 actMainActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001847 } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
1848 action == "android.media.action.VIDEO_CAMERA") {
1849 actCamera = true;
1850 } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
1851 actCameraSecure = true;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001852 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001853 } else if (withinReceiver) {
1854 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1855 actWidgetReceivers = true;
1856 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1857 actDeviceAdminEnabled = true;
1858 }
1859 } else if (withinService) {
1860 if (action == "android.view.InputMethod") {
1861 actImeService = true;
1862 } else if (action == "android.service.wallpaper.WallpaperService") {
1863 actWallpaperService = true;
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001864 } else if (action ==
1865 "android.accessibilityservice.AccessibilityService") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001866 actAccessibilityService = true;
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001867 } else if (action =="android.printservice.PrintService") {
Adam Lesinskia5018c92013-09-30 16:23:15 -07001868 actPrintService = true;
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001869 } else if (action ==
1870 "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07001871 actHostApduService = true;
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001872 } else if (action ==
1873 "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
Adam Lesinski94fc9122013-09-30 17:16:09 -07001874 actOffHostApduService = true;
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001875 } else if (action ==
1876 "android.service.notification.NotificationListenerService") {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001877 actNotificationListenerService = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001878 } else if (action == "android.service.dreams.DreamService") {
1879 actDreamService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001880 }
1881 } else if (withinProvider) {
1882 if (action == "android.content.action.DOCUMENTS_PROVIDER") {
1883 actDocumentsProvider = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001884 }
1885 }
1886 if (action == "android.intent.action.SEARCH") {
1887 isSearchable = true;
1888 }
1889 }
1890
1891 if (tag == "category") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001892 String8 category = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001893 if (error != "") {
1894 fprintf(stderr, "ERROR getting 'name' attribute: %s\n",
1895 error.string());
1896 goto bail;
1897 }
1898 if (withinActivity) {
1899 if (category == "android.intent.category.LAUNCHER") {
1900 isLauncherActivity = true;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001901 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
1902 isLeanbackLauncherActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001903 } else if (category == "android.intent.category.HOME") {
1904 catLauncher = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001905 }
1906 }
1907 }
1908 }
1909 }
1910
1911 // Pre-1.6 implicitly granted permission compatibility logic
1912 if (targetSdk < 4) {
1913 if (!hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001914 printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
1915 printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
1916 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001917 hasWriteExternalStoragePermission = true;
1918 }
1919 if (!hasReadPhoneStatePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001920 printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
1921 printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
1922 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001923 }
1924 }
1925
1926 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1927 // force them to always take READ_EXTERNAL_STORAGE as well. We always
1928 // do this (regardless of target API version) because we can't have
1929 // an app with write permission but not read permission.
1930 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001931 printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"));
1932 printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
1933 String8("requested WRITE_EXTERNAL_STORAGE"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001934 }
1935
1936 // Pre-JellyBean call log permission compatibility.
1937 if (targetSdk < 16) {
1938 if (!hasReadCallLogPermission && hasReadContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001939 printUsesPermission(String8("android.permission.READ_CALL_LOG"));
1940 printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
1941 String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001942 }
1943 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001944 printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
1945 printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
1946 String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001947 }
1948 }
1949
Adam Lesinski2c72b682014-06-24 09:56:01 -07001950 addImpliedFeature(&impliedFeatures, "android.hardware.touchscreen",
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001951 "default feature for all apps", false);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001952
1953 const size_t numFeatureGroups = featureGroups.size();
1954 if (numFeatureGroups == 0) {
1955 // If no <feature-group> tags were defined, apply auto-implied features.
Adam Lesinski5f3b2ecb2015-12-02 15:40:19 -08001956 printDefaultFeatureGroup(commonFeatures, impliedFeatures);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001957
1958 } else {
1959 // <feature-group> tags are defined, so we ignore implied features and
1960 for (size_t i = 0; i < numFeatureGroups; i++) {
1961 FeatureGroup& grp = featureGroups.editItemAt(i);
1962
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001963 if (commonFeatures.openGLESVersion > grp.openGLESVersion) {
1964 grp.openGLESVersion = commonFeatures.openGLESVersion;
1965 }
1966
Adam Lesinski2c72b682014-06-24 09:56:01 -07001967 // Merge the features defined in the top level (not inside a <feature-group>)
1968 // with this feature group.
1969 const size_t numCommonFeatures = commonFeatures.features.size();
1970 for (size_t j = 0; j < numCommonFeatures; j++) {
1971 if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001972 grp.features.add(commonFeatures.features.keyAt(j),
1973 commonFeatures.features[j]);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001974 }
1975 }
1976
Adam Lesinski73a05112014-12-08 12:53:17 -08001977 if (!grp.features.isEmpty()) {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001978 printFeatureGroup(grp);
Adam Lesinski282e1812014-01-23 18:17:42 -08001979 }
1980 }
1981 }
1982
Adam Lesinski282e1812014-01-23 18:17:42 -08001983
Adam Lesinski282e1812014-01-23 18:17:42 -08001984 if (hasWidgetReceivers) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001985 printComponentPresence("app-widget");
Adam Lesinski282e1812014-01-23 18:17:42 -08001986 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001987 if (hasDeviceAdminReceiver) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001988 printComponentPresence("device-admin");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001989 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001990 if (hasImeService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001991 printComponentPresence("ime");
Adam Lesinski282e1812014-01-23 18:17:42 -08001992 }
1993 if (hasWallpaperService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001994 printComponentPresence("wallpaper");
Adam Lesinski282e1812014-01-23 18:17:42 -08001995 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001996 if (hasAccessibilityService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001997 printComponentPresence("accessibility");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001998 }
1999 if (hasPrintService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002000 printComponentPresence("print-service");
Adam Lesinskia5018c92013-09-30 16:23:15 -07002001 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07002002 if (hasPaymentService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002003 printComponentPresence("payment");
2004 }
2005 if (isSearchable) {
2006 printComponentPresence("search");
2007 }
2008 if (hasDocumentsProvider) {
2009 printComponentPresence("document-provider");
2010 }
2011 if (hasLauncher) {
2012 printComponentPresence("launcher");
2013 }
2014 if (hasNotificationListenerService) {
2015 printComponentPresence("notification-listener");
2016 }
John Spurlockeb8d1be2014-06-25 17:46:15 -04002017 if (hasDreamService) {
2018 printComponentPresence("dream");
2019 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002020 if (hasCameraActivity) {
2021 printComponentPresence("camera");
2022 }
2023 if (hasCameraSecureActivity) {
2024 printComponentPresence("camera-secure");
2025 }
2026
2027 if (hasMainActivity) {
2028 printf("main\n");
Adam Lesinski94fc9122013-09-30 17:16:09 -07002029 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002030 if (hasOtherActivities) {
2031 printf("other-activities\n");
2032 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07002033 if (hasOtherReceivers) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002034 printf("other-receivers\n");
2035 }
2036 if (hasOtherServices) {
2037 printf("other-services\n");
2038 }
2039
2040 // For modern apps, if screen size buckets haven't been specified
2041 // but the new width ranges have, then infer the buckets from them.
2042 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
2043 && requiresSmallestWidthDp > 0) {
2044 int compatWidth = compatibleWidthLimitDp;
2045 if (compatWidth <= 0) {
2046 compatWidth = requiresSmallestWidthDp;
2047 }
2048 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
2049 smallScreen = -1;
2050 } else {
2051 smallScreen = 0;
2052 }
2053 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
2054 normalScreen = -1;
2055 } else {
2056 normalScreen = 0;
2057 }
2058 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
2059 largeScreen = -1;
2060 } else {
2061 largeScreen = 0;
2062 }
2063 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
2064 xlargeScreen = -1;
2065 } else {
2066 xlargeScreen = 0;
2067 }
2068 }
2069
2070 // Determine default values for any unspecified screen sizes,
2071 // based on the target SDK of the package. As of 4 (donut)
2072 // the screen size support was introduced, so all default to
2073 // enabled.
2074 if (smallScreen > 0) {
2075 smallScreen = targetSdk >= 4 ? -1 : 0;
2076 }
2077 if (normalScreen > 0) {
2078 normalScreen = -1;
2079 }
2080 if (largeScreen > 0) {
2081 largeScreen = targetSdk >= 4 ? -1 : 0;
2082 }
2083 if (xlargeScreen > 0) {
2084 // Introduced in Gingerbread.
2085 xlargeScreen = targetSdk >= 9 ? -1 : 0;
2086 }
2087 if (anyDensity > 0) {
2088 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
2089 || compatibleWidthLimitDp > 0) ? -1 : 0;
2090 }
2091 printf("supports-screens:");
2092 if (smallScreen != 0) {
2093 printf(" 'small'");
2094 }
2095 if (normalScreen != 0) {
2096 printf(" 'normal'");
2097 }
2098 if (largeScreen != 0) {
2099 printf(" 'large'");
2100 }
2101 if (xlargeScreen != 0) {
2102 printf(" 'xlarge'");
2103 }
2104 printf("\n");
2105 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
2106 if (requiresSmallestWidthDp > 0) {
2107 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
2108 }
2109 if (compatibleWidthLimitDp > 0) {
2110 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
2111 }
2112 if (largestWidthLimitDp > 0) {
2113 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
2114 }
2115
2116 printf("locales:");
2117 const size_t NL = locales.size();
2118 for (size_t i=0; i<NL; i++) {
2119 const char* localeStr = locales[i].string();
2120 if (localeStr == NULL || strlen(localeStr) == 0) {
2121 localeStr = "--_--";
2122 }
2123 printf(" '%s'", localeStr);
2124 }
2125 printf("\n");
2126
2127 printf("densities:");
2128 const size_t ND = densities.size();
2129 for (size_t i=0; i<ND; i++) {
2130 printf(" '%d'", densities[i]);
2131 }
2132 printf("\n");
2133
2134 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
2135 if (dir != NULL) {
2136 if (dir->getFileCount() > 0) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002137 SortedVector<String8> architectures;
Adam Lesinski282e1812014-01-23 18:17:42 -08002138 for (size_t i=0; i<dir->getFileCount(); i++) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002139 architectures.add(ResTable::normalizeForOutput(
2140 dir->getFileName(i).string()));
Adam Lesinski282e1812014-01-23 18:17:42 -08002141 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07002142
2143 bool outputAltNativeCode = false;
2144 // A multiArch package is one that contains 64-bit and
2145 // 32-bit versions of native code and expects 3rd-party
2146 // apps to load these native code libraries. Since most
2147 // 64-bit systems also support 32-bit apps, the apps
2148 // loading this multiArch package's code may be either
2149 // 32-bit or 64-bit.
2150 if (hasMultiArch) {
2151 // If this is a multiArch package, report the 64-bit
2152 // version only. Then as a separate entry, report the
2153 // rest.
2154 //
2155 // If we report the 32-bit architecture, this APK will
2156 // be installed on a 32-bit device, causing a large waste
2157 // of bandwidth and disk space. This assumes that
2158 // the developer of the multiArch package has also
2159 // made a version that is 32-bit only.
2160 String8 intel64("x86_64");
2161 String8 arm64("arm64-v8a");
2162 ssize_t index = architectures.indexOf(intel64);
2163 if (index < 0) {
2164 index = architectures.indexOf(arm64);
2165 }
2166
2167 if (index >= 0) {
2168 printf("native-code: '%s'\n", architectures[index].string());
2169 architectures.removeAt(index);
2170 outputAltNativeCode = true;
2171 }
2172 }
2173
2174 const size_t archCount = architectures.size();
2175 if (archCount > 0) {
2176 if (outputAltNativeCode) {
2177 printf("alt-");
2178 }
2179 printf("native-code:");
2180 for (size_t i = 0; i < archCount; i++) {
2181 printf(" '%s'", architectures[i].string());
2182 }
2183 printf("\n");
2184 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002185 }
2186 delete dir;
2187 }
2188 } else if (strcmp("badger", option) == 0) {
2189 printf("%s", CONSOLE_DATA);
2190 } else if (strcmp("configurations", option) == 0) {
2191 Vector<ResTable_config> configs;
2192 res.getConfigurations(&configs);
2193 const size_t N = configs.size();
2194 for (size_t i=0; i<N; i++) {
2195 printf("%s\n", configs[i].toString().string());
2196 }
2197 } else {
2198 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2199 goto bail;
2200 }
2201 }
2202
2203 result = NO_ERROR;
2204
2205bail:
2206 if (asset) {
2207 delete asset;
2208 }
2209 return (result != NO_ERROR);
2210}
2211
2212
2213/*
2214 * Handle the "add" command, which wants to add files to a new or
2215 * pre-existing archive.
2216 */
2217int doAdd(Bundle* bundle)
2218{
2219 ZipFile* zip = NULL;
2220 status_t result = UNKNOWN_ERROR;
2221 const char* zipFileName;
2222
2223 if (bundle->getUpdate()) {
2224 /* avoid confusion */
2225 fprintf(stderr, "ERROR: can't use '-u' with add\n");
2226 goto bail;
2227 }
2228
2229 if (bundle->getFileSpecCount() < 1) {
2230 fprintf(stderr, "ERROR: must specify zip file name\n");
2231 goto bail;
2232 }
2233 zipFileName = bundle->getFileSpecEntry(0);
2234
2235 if (bundle->getFileSpecCount() < 2) {
2236 fprintf(stderr, "NOTE: nothing to do\n");
2237 goto bail;
2238 }
2239
2240 zip = openReadWrite(zipFileName, true);
2241 if (zip == NULL) {
2242 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2243 goto bail;
2244 }
2245
2246 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2247 const char* fileName = bundle->getFileSpecEntry(i);
2248
2249 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
2250 printf(" '%s'... (from gzip)\n", fileName);
2251 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
2252 } else {
2253 if (bundle->getJunkPath()) {
2254 String8 storageName = String8(fileName).getPathLeaf();
Maurice Chu2675f762013-10-22 17:33:11 -07002255 printf(" '%s' as '%s'...\n", fileName,
2256 ResTable::normalizeForOutput(storageName.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08002257 result = zip->add(fileName, storageName.string(),
2258 bundle->getCompressionMethod(), NULL);
2259 } else {
2260 printf(" '%s'...\n", fileName);
2261 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2262 }
2263 }
2264 if (result != NO_ERROR) {
2265 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2266 if (result == NAME_NOT_FOUND) {
2267 fprintf(stderr, ": file not found\n");
2268 } else if (result == ALREADY_EXISTS) {
2269 fprintf(stderr, ": already exists in archive\n");
2270 } else {
2271 fprintf(stderr, "\n");
2272 }
2273 goto bail;
2274 }
2275 }
2276
2277 result = NO_ERROR;
2278
2279bail:
2280 delete zip;
2281 return (result != NO_ERROR);
2282}
2283
2284
2285/*
2286 * Delete files from an existing archive.
2287 */
2288int doRemove(Bundle* bundle)
2289{
2290 ZipFile* zip = NULL;
2291 status_t result = UNKNOWN_ERROR;
2292 const char* zipFileName;
2293
2294 if (bundle->getFileSpecCount() < 1) {
2295 fprintf(stderr, "ERROR: must specify zip file name\n");
2296 goto bail;
2297 }
2298 zipFileName = bundle->getFileSpecEntry(0);
2299
2300 if (bundle->getFileSpecCount() < 2) {
2301 fprintf(stderr, "NOTE: nothing to do\n");
2302 goto bail;
2303 }
2304
2305 zip = openReadWrite(zipFileName, false);
2306 if (zip == NULL) {
2307 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2308 zipFileName);
2309 goto bail;
2310 }
2311
2312 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2313 const char* fileName = bundle->getFileSpecEntry(i);
2314 ZipEntry* entry;
2315
2316 entry = zip->getEntryByName(fileName);
2317 if (entry == NULL) {
2318 printf(" '%s' NOT FOUND\n", fileName);
2319 continue;
2320 }
2321
2322 result = zip->remove(entry);
2323
2324 if (result != NO_ERROR) {
2325 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2326 bundle->getFileSpecEntry(i), zipFileName);
2327 goto bail;
2328 }
2329 }
2330
2331 /* update the archive */
2332 zip->flush();
2333
2334bail:
2335 delete zip;
2336 return (result != NO_ERROR);
2337}
2338
Adam Lesinski3921e872014-05-13 10:56:25 -07002339static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002340 const size_t numDirs = dir->getDirs().size();
2341 for (size_t i = 0; i < numDirs; i++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002342 bool ignore = ignoreConfig;
2343 const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2344 const char* dirStr = subDir->getLeaf().string();
2345 if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2346 ignore = true;
2347 }
2348 status_t err = addResourcesToBuilder(subDir, builder, ignore);
Adam Lesinskifab50872014-04-16 14:40:42 -07002349 if (err != NO_ERROR) {
2350 return err;
2351 }
2352 }
2353
2354 const size_t numFiles = dir->getFiles().size();
2355 for (size_t i = 0; i < numFiles; i++) {
2356 sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2357 const size_t numConfigs = gp->getFiles().size();
2358 for (size_t j = 0; j < numConfigs; j++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002359 status_t err = NO_ERROR;
2360 if (ignoreConfig) {
2361 err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2362 } else {
2363 err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2364 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002365 if (err != NO_ERROR) {
2366 fprintf(stderr, "Failed to add %s (%s) to builder.\n",
2367 gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
2368 return err;
2369 }
2370 }
2371 }
2372 return NO_ERROR;
2373}
2374
2375static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2376 if (split->isBase()) {
2377 return original;
2378 }
2379
2380 String8 ext(original.getPathExtension());
2381 if (ext == String8(".apk")) {
2382 return String8::format("%s_%s%s",
2383 original.getBasePath().string(),
2384 split->getDirectorySafeName().string(),
2385 ext.string());
2386 }
2387
2388 return String8::format("%s_%s", original.string(),
2389 split->getDirectorySafeName().string());
2390}
Adam Lesinski282e1812014-01-23 18:17:42 -08002391
2392/*
2393 * Package up an asset directory and associated application files.
2394 */
2395int doPackage(Bundle* bundle)
2396{
2397 const char* outputAPKFile;
2398 int retVal = 1;
2399 status_t err;
2400 sp<AaptAssets> assets;
2401 int N;
2402 FILE* fp;
2403 String8 dependencyFile;
Adam Lesinskifab50872014-04-16 14:40:42 -07002404 sp<ApkBuilder> builder;
Adam Lesinski282e1812014-01-23 18:17:42 -08002405
Anton Krumina2ef5c02014-03-12 14:46:44 -07002406 // -c en_XA or/and ar_XB means do pseudolocalization
Adam Lesinskifab50872014-04-16 14:40:42 -07002407 sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2408 err = configFilter->parse(bundle->getConfigurations());
Adam Lesinski282e1812014-01-23 18:17:42 -08002409 if (err != NO_ERROR) {
2410 goto bail;
2411 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002412 if (configFilter->containsPseudo()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002413 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2414 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002415 if (configFilter->containsPseudoBidi()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002416 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
Adam Lesinski282e1812014-01-23 18:17:42 -08002417 }
2418
2419 N = bundle->getFileSpecCount();
2420 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
Adam Lesinski09384302014-01-22 16:07:42 -08002421 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002422 fprintf(stderr, "ERROR: no input files\n");
2423 goto bail;
2424 }
2425
2426 outputAPKFile = bundle->getOutputAPKFile();
2427
2428 // Make sure the filenames provided exist and are of the appropriate type.
2429 if (outputAPKFile) {
2430 FileType type;
2431 type = getFileType(outputAPKFile);
2432 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2433 fprintf(stderr,
2434 "ERROR: output file '%s' exists but is not regular file\n",
2435 outputAPKFile);
2436 goto bail;
2437 }
2438 }
2439
2440 // Load the assets.
2441 assets = new AaptAssets();
2442
2443 // Set up the resource gathering in assets if we're going to generate
2444 // dependency files. Every time we encounter a resource while slurping
2445 // the tree, we'll add it to these stores so we have full resource paths
2446 // to write to a dependency file.
2447 if (bundle->getGenDependencies()) {
2448 sp<FilePathStore> resPathStore = new FilePathStore;
2449 assets->setFullResPaths(resPathStore);
2450 sp<FilePathStore> assetPathStore = new FilePathStore;
2451 assets->setFullAssetPaths(assetPathStore);
2452 }
2453
2454 err = assets->slurpFromArgs(bundle);
2455 if (err < 0) {
2456 goto bail;
2457 }
2458
2459 if (bundle->getVerbose()) {
2460 assets->print(String8());
2461 }
2462
Adam Lesinskifab50872014-04-16 14:40:42 -07002463 // Create the ApkBuilder, which will collect the compiled files
2464 // to write to the final APK (or sets of APKs if we are building
2465 // a Split APK.
2466 builder = new ApkBuilder(configFilter);
2467
2468 // If we are generating a Split APK, find out which configurations to split on.
2469 if (bundle->getSplitConfigurations().size() > 0) {
2470 const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2471 const size_t numSplits = splitStrs.size();
2472 for (size_t i = 0; i < numSplits; i++) {
2473 std::set<ConfigDescription> configs;
2474 if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2475 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2476 goto bail;
2477 }
2478
2479 err = builder->createSplitForConfigs(configs);
2480 if (err != NO_ERROR) {
2481 goto bail;
2482 }
2483 }
2484 }
2485
Adam Lesinski282e1812014-01-23 18:17:42 -08002486 // If they asked for any fileAs that need to be compiled, do so.
2487 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002488 err = buildResources(bundle, assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002489 if (err != 0) {
2490 goto bail;
2491 }
2492 }
2493
2494 // At this point we've read everything and processed everything. From here
2495 // on out it's just writing output files.
2496 if (SourcePos::hasErrors()) {
2497 goto bail;
2498 }
2499
2500 // Update symbols with information about which ones are needed as Java symbols.
2501 assets->applyJavaSymbols();
2502 if (SourcePos::hasErrors()) {
2503 goto bail;
2504 }
2505
2506 // If we've been asked to generate a dependency file, do that here
2507 if (bundle->getGenDependencies()) {
2508 // If this is the packaging step, generate the dependency file next to
2509 // the output apk (e.g. bin/resources.ap_.d)
2510 if (outputAPKFile) {
2511 dependencyFile = String8(outputAPKFile);
2512 // Add the .d extension to the dependency file.
2513 dependencyFile.append(".d");
2514 } else {
2515 // Else if this is the R.java dependency generation step,
2516 // generate the dependency file in the R.java package subdirectory
2517 // e.g. gen/com/foo/app/R.java.d
2518 dependencyFile = String8(bundle->getRClassDir());
2519 dependencyFile.appendPath("R.java.d");
2520 }
2521 // Make sure we have a clean dependency file to start with
2522 fp = fopen(dependencyFile, "w");
2523 fclose(fp);
2524 }
2525
2526 // Write out R.java constants
2527 if (!assets->havePrivateSymbols()) {
2528 if (bundle->getCustomPackage() == NULL) {
2529 // Write the R.java file into the appropriate class directory
2530 // e.g. gen/com/foo/app/R.java
Adam Lesinski1e4663852014-08-15 14:47:28 -07002531 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002532 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002533 } else {
2534 const String8 customPkg(bundle->getCustomPackage());
Adam Lesinski1e4663852014-08-15 14:47:28 -07002535 err = writeResourceSymbols(bundle, assets, customPkg, true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002536 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002537 }
2538 if (err < 0) {
2539 goto bail;
2540 }
2541 // If we have library files, we're going to write our R.java file into
2542 // the appropriate class directory for those libraries as well.
2543 // e.g. gen/com/foo/app/lib/R.java
2544 if (bundle->getExtraPackages() != NULL) {
2545 // Split on colon
2546 String8 libs(bundle->getExtraPackages());
2547 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2548 while (packageString != NULL) {
2549 // Write the R.java file out with the correct package name
Marcin Kosiba0f3a5a62014-09-11 13:48:48 +01002550 err = writeResourceSymbols(bundle, assets, String8(packageString), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002551 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002552 if (err < 0) {
2553 goto bail;
2554 }
2555 packageString = strtok(NULL, ":");
2556 }
2557 libs.unlockBuffer();
2558 }
2559 } else {
Adam Lesinski1e4663852014-08-15 14:47:28 -07002560 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002561 if (err < 0) {
2562 goto bail;
2563 }
Adam Lesinski1e4663852014-08-15 14:47:28 -07002564 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002565 if (err < 0) {
2566 goto bail;
2567 }
2568 }
2569
2570 // Write out the ProGuard file
2571 err = writeProguardFile(bundle, assets);
2572 if (err < 0) {
2573 goto bail;
2574 }
2575
2576 // Write the apk
2577 if (outputAPKFile) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002578 // Gather all resources and add them to the APK Builder. The builder will then
2579 // figure out which Split they belong in.
2580 err = addResourcesToBuilder(assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002581 if (err != NO_ERROR) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002582 goto bail;
2583 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002584
2585 const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2586 const size_t numSplits = splits.size();
2587 for (size_t i = 0; i < numSplits; i++) {
2588 const sp<ApkSplit>& split = splits[i];
2589 String8 outputPath = buildApkName(String8(outputAPKFile), split);
2590 err = writeAPK(bundle, outputPath, split);
2591 if (err != NO_ERROR) {
2592 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2593 goto bail;
2594 }
2595 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002596 }
2597
2598 // If we've been asked to generate a dependency file, we need to finish up here.
2599 // the writeResourceSymbols and writeAPK functions have already written the target
2600 // half of the dependency file, now we need to write the prerequisites. (files that
2601 // the R.java file or .ap_ file depend on)
2602 if (bundle->getGenDependencies()) {
2603 // Now that writeResourceSymbols or writeAPK has taken care of writing
2604 // the targets to our dependency file, we'll write the prereqs
2605 fp = fopen(dependencyFile, "a+");
2606 fprintf(fp, " : ");
2607 bool includeRaw = (outputAPKFile != NULL);
2608 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2609 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2610 // and therefore was not added to our pathstores during slurping
2611 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2612 fclose(fp);
2613 }
2614
2615 retVal = 0;
2616bail:
2617 if (SourcePos::hasErrors()) {
2618 SourcePos::printErrors(stderr);
2619 }
2620 return retVal;
2621}
2622
2623/*
2624 * Do PNG Crunching
2625 * PRECONDITIONS
2626 * -S flag points to a source directory containing drawable* folders
2627 * -C flag points to destination directory. The folder structure in the
2628 * source directory will be mirrored to the destination (cache) directory
2629 *
2630 * POSTCONDITIONS
2631 * Destination directory will be updated to match the PNG files in
Maurice Chu2675f762013-10-22 17:33:11 -07002632 * the source directory.
Adam Lesinski282e1812014-01-23 18:17:42 -08002633 */
2634int doCrunch(Bundle* bundle)
2635{
2636 fprintf(stdout, "Crunching PNG Files in ");
2637 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2638 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2639
2640 updatePreProcessedCache(bundle);
2641
2642 return NO_ERROR;
2643}
2644
2645/*
2646 * Do PNG Crunching on a single flag
2647 * -i points to a single png file
2648 * -o points to a single png output file
2649 */
2650int doSingleCrunch(Bundle* bundle)
2651{
2652 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2653 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2654
2655 String8 input(bundle->getSingleCrunchInputFile());
2656 String8 output(bundle->getSingleCrunchOutputFile());
2657
2658 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2659 // we can't return the status_t as it gets truncate to the lower 8 bits.
2660 return 42;
2661 }
2662
2663 return NO_ERROR;
2664}
2665
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002666int runInDaemonMode(Bundle* bundle) {
2667 std::cout << "Ready" << std::endl;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002668 for (std::string cmd; std::getline(std::cin, cmd);) {
2669 if (cmd == "quit") {
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002670 return NO_ERROR;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002671 } else if (cmd == "s") {
2672 // Two argument crunch
2673 std::string inputFile, outputFile;
2674 std::getline(std::cin, inputFile);
2675 std::getline(std::cin, outputFile);
2676 bundle->setSingleCrunchInputFile(inputFile.c_str());
2677 bundle->setSingleCrunchOutputFile(outputFile.c_str());
2678 std::cout << "Crunching " << inputFile << std::endl;
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002679 if (doSingleCrunch(bundle) != NO_ERROR) {
2680 std::cout << "Error" << std::endl;
2681 }
2682 std::cout << "Done" << std::endl;
2683 } else {
2684 // in case of invalid command, just bail out.
2685 std::cerr << "Unknown command" << std::endl;
2686 return -1;
2687 }
2688 }
2689 return -1;
2690}
2691
Adam Lesinski282e1812014-01-23 18:17:42 -08002692char CONSOLE_DATA[2925] = {
2693 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2694 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2695 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2696 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2697 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2698 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2699 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2700 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2701 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2702 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2703 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2704 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2705 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2706 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2707 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2708 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2709 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2710 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2711 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2712 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2713 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2714 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2715 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2716 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2717 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2718 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2719 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2720 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2721 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2722 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2723 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2724 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2725 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2726 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2727 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2728 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2729 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2730 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2731 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2732 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2733 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2734 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2735 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2736 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2737 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2738 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2739 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2740 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2741 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2742 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2743 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2744 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2745 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2746 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2747 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2748 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2749 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2750 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2751 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2752 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2753 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2754 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2755 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2756 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2757 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2758 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2759 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2760 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2761 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2762 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2763 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2764 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2765 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2766 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2767 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2768 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2769 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2770 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2771 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2772 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2773 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2774 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2775 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2776 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2777 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2778 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2779 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2780 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2781 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2782 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2783 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2784 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2785 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2786 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2787 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2788 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2789 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2790 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2791 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2792 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2793 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2794 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2795 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2796 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2797 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2798 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2799 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2800 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2801 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2802 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2803 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2804 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2805 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2806 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2807 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2808 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2809 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2810 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2811 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2812 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2813 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2814 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2815 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2816 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2817 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2818 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2819 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2820 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2821 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2822 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2823 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2824 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2825 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2826 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2827 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2828 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2829 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2830 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2831 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2832 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2833 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2834 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2835 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2836 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2837 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2838 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2839 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2840 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2841 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2842 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2843 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2844 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2845 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2846 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2847 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2848 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2849 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2850 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2851 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2852 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2853 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2854 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2855 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2856 };