blob: 1e9e3e20ddf83f25e6ab3ea6a882f80835afec6b [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
218 const ResTable& res = assets.getResources(false);
219 if (&res == NULL) {
220 printf("\nNo resource table found.\n");
221 } else {
222#ifndef HAVE_ANDROID_OS
223 printf("\nResource table:\n");
224 res.print(false);
225#endif
226 }
227
228 Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
229 Asset::ACCESS_BUFFER);
230 if (manifestAsset == NULL) {
231 printf("\nNo AndroidManifest.xml found.\n");
232 } else {
233 printf("\nAndroid manifest:\n");
234 ResXMLTree tree;
235 tree.setTo(manifestAsset->getBuffer(true),
236 manifestAsset->getLength());
237 printXMLBlock(&tree);
238 }
239 delete manifestAsset;
240 }
241
242 result = 0;
243
244bail:
245 delete zip;
246 return result;
247}
248
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700249static void printResolvedResourceAttribute(const ResTable& resTable, const ResXMLTree& tree,
Maurice Chu76327312013-10-16 18:28:46 -0700250 uint32_t attrRes, String8 attrLabel, String8* outError)
251{
252 Res_value value;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700253 AaptXml::getResolvedResourceAttribute(resTable, tree, attrRes, &value, outError);
Maurice Chu76327312013-10-16 18:28:46 -0700254 if (*outError != "") {
255 *outError = "error print resolved resource attribute";
256 return;
257 }
258 if (value.dataType == Res_value::TYPE_STRING) {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700259 String8 result = AaptXml::getResolvedAttribute(resTable, tree, attrRes, outError);
Maurice Chu2675f762013-10-22 17:33:11 -0700260 printf("%s='%s'", attrLabel.string(),
261 ResTable::normalizeForOutput(result.string()).string());
Maurice Chu76327312013-10-16 18:28:46 -0700262 } else if (Res_value::TYPE_FIRST_INT <= value.dataType &&
263 value.dataType <= Res_value::TYPE_LAST_INT) {
264 printf("%s='%d'", attrLabel.string(), value.data);
265 } else {
266 printf("%s='0x%x'", attrLabel.string(), (int)value.data);
267 }
268}
269
Adam Lesinski282e1812014-01-23 18:17:42 -0800270// These are attribute resource constants for the platform, as found
271// in android.R.attr
272enum {
273 LABEL_ATTR = 0x01010001,
274 ICON_ATTR = 0x01010002,
275 NAME_ATTR = 0x01010003,
Adam Lesinskia5018c92013-09-30 16:23:15 -0700276 PERMISSION_ATTR = 0x01010006,
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700277 EXPORTED_ATTR = 0x01010010,
278 GRANT_URI_PERMISSIONS_ATTR = 0x0101001b,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700279 RESOURCE_ATTR = 0x01010025,
Adam Lesinski282e1812014-01-23 18:17:42 -0800280 DEBUGGABLE_ATTR = 0x0101000f,
281 VALUE_ATTR = 0x01010024,
282 VERSION_CODE_ATTR = 0x0101021b,
283 VERSION_NAME_ATTR = 0x0101021c,
284 SCREEN_ORIENTATION_ATTR = 0x0101001e,
285 MIN_SDK_VERSION_ATTR = 0x0101020c,
286 MAX_SDK_VERSION_ATTR = 0x01010271,
287 REQ_TOUCH_SCREEN_ATTR = 0x01010227,
288 REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
289 REQ_HARD_KEYBOARD_ATTR = 0x01010229,
290 REQ_NAVIGATION_ATTR = 0x0101022a,
291 REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
292 TARGET_SDK_VERSION_ATTR = 0x01010270,
293 TEST_ONLY_ATTR = 0x01010272,
294 ANY_DENSITY_ATTR = 0x0101026c,
295 GL_ES_VERSION_ATTR = 0x01010281,
296 SMALL_SCREEN_ATTR = 0x01010284,
297 NORMAL_SCREEN_ATTR = 0x01010285,
298 LARGE_SCREEN_ATTR = 0x01010286,
299 XLARGE_SCREEN_ATTR = 0x010102bf,
300 REQUIRED_ATTR = 0x0101028e,
Adam Lesinskicaf797c2014-08-22 12:56:26 -0700301 INSTALL_LOCATION_ATTR = 0x010102b7,
Adam Lesinski282e1812014-01-23 18:17:42 -0800302 SCREEN_SIZE_ATTR = 0x010102ca,
303 SCREEN_DENSITY_ATTR = 0x010102cb,
304 REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
305 COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
306 LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
307 PUBLIC_KEY_ATTR = 0x010103a6,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700308 CATEGORY_ATTR = 0x010103e8,
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800309 BANNER_ATTR = 0x10103f2,
Adam Lesinski282e1812014-01-23 18:17:42 -0800310};
311
Maurice Chu2675f762013-10-22 17:33:11 -0700312String8 getComponentName(String8 &pkgName, String8 &componentName) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800313 ssize_t idx = componentName.find(".");
314 String8 retStr(pkgName);
315 if (idx == 0) {
316 retStr += componentName;
317 } else if (idx < 0) {
318 retStr += ".";
319 retStr += componentName;
320 } else {
Maurice Chu2675f762013-10-22 17:33:11 -0700321 return componentName;
Adam Lesinski282e1812014-01-23 18:17:42 -0800322 }
Maurice Chu2675f762013-10-22 17:33:11 -0700323 return retStr;
Adam Lesinski282e1812014-01-23 18:17:42 -0800324}
325
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700326static void printCompatibleScreens(ResXMLTree& tree, String8* outError) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800327 size_t len;
328 ResXMLTree::event_code_t code;
329 int depth = 0;
330 bool first = true;
331 printf("compatible-screens:");
332 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
333 if (code == ResXMLTree::END_TAG) {
334 depth--;
335 if (depth < 0) {
336 break;
337 }
338 continue;
339 }
340 if (code != ResXMLTree::START_TAG) {
341 continue;
342 }
343 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700344 const char16_t* ctag16 = tree.getElementName(&len);
345 if (ctag16 == NULL) {
346 *outError = "failed to get XML element name (bad string pool)";
347 return;
348 }
349 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800350 if (tag == "screen") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700351 int32_t screenSize = AaptXml::getIntegerAttribute(tree,
352 SCREEN_SIZE_ATTR);
353 int32_t screenDensity = AaptXml::getIntegerAttribute(tree,
354 SCREEN_DENSITY_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -0800355 if (screenSize > 0 && screenDensity > 0) {
356 if (!first) {
357 printf(",");
358 }
359 first = false;
360 printf("'%d/%d'", screenSize, screenDensity);
361 }
362 }
363 }
364 printf("\n");
365}
366
Adam Lesinski58f1f362013-11-12 12:59:08 -0800367static void printUsesPermission(const String8& name, bool optional=false, int maxSdkVersion=-1) {
368 printf("uses-permission: name='%s'", ResTable::normalizeForOutput(name.string()).string());
369 if (maxSdkVersion != -1) {
370 printf(" maxSdkVersion='%d'", maxSdkVersion);
371 }
372 printf("\n");
373
374 if (optional) {
375 printf("optional-permission: name='%s'",
376 ResTable::normalizeForOutput(name.string()).string());
377 if (maxSdkVersion != -1) {
378 printf(" maxSdkVersion='%d'", maxSdkVersion);
379 }
380 printf("\n");
381 }
382}
383
384static void printUsesImpliedPermission(const String8& name, const String8& reason) {
385 printf("uses-implied-permission: name='%s' reason='%s'\n",
386 ResTable::normalizeForOutput(name.string()).string(),
387 ResTable::normalizeForOutput(reason.string()).string());
388}
389
Adam Lesinski94fc9122013-09-30 17:16:09 -0700390Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool offHost,
391 String8 *outError = NULL)
392{
393 Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER);
394 if (aidAsset == NULL) {
395 if (outError != NULL) *outError = "xml resource does not exist";
396 return Vector<String8>();
397 }
398
399 const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service");
400
401 bool withinApduService = false;
402 Vector<String8> categories;
403
404 String8 error;
405 ResXMLTree tree;
406 tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength());
407
408 size_t len;
409 int depth = 0;
410 ResXMLTree::event_code_t code;
411 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
412 if (code == ResXMLTree::END_TAG) {
413 depth--;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700414 const char16_t* ctag16 = tree.getElementName(&len);
415 if (ctag16 == NULL) {
416 *outError = "failed to get XML element name (bad string pool)";
417 return Vector<String8>();
418 }
419 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700420
421 if (depth == 0 && tag == serviceTagName) {
422 withinApduService = false;
423 }
424
425 } else if (code == ResXMLTree::START_TAG) {
426 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700427 const char16_t* ctag16 = tree.getElementName(&len);
428 if (ctag16 == NULL) {
429 *outError = "failed to get XML element name (bad string pool)";
430 return Vector<String8>();
431 }
432 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700433
434 if (depth == 1) {
435 if (tag == serviceTagName) {
436 withinApduService = true;
437 }
438 } else if (depth == 2 && withinApduService) {
439 if (tag == "aid-group") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700440 String8 category = AaptXml::getAttribute(tree, CATEGORY_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700441 if (error != "") {
442 if (outError != NULL) *outError = error;
443 return Vector<String8>();
444 }
445
446 categories.add(category);
447 }
448 }
449 }
450 }
451 aidAsset->close();
452 return categories;
453}
454
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700455static void printComponentPresence(const char* componentName) {
456 printf("provides-component:'%s'\n", componentName);
457}
458
Adam Lesinski2c72b682014-06-24 09:56:01 -0700459/**
460 * Represents a feature that has been automatically added due to
461 * a pre-requisite or some other reason.
462 */
463struct ImpliedFeature {
464 /**
465 * Name of the implied feature.
466 */
467 String8 name;
468
469 /**
470 * List of human-readable reasons for why this feature was implied.
471 */
472 SortedVector<String8> reasons;
473};
474
475/**
476 * Represents a <feature-group> tag in the AndroidManifest.xml
477 */
478struct FeatureGroup {
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700479 FeatureGroup() : openGLESVersion(-1) {}
480
Adam Lesinski2c72b682014-06-24 09:56:01 -0700481 /**
482 * Human readable label
483 */
484 String8 label;
485
486 /**
487 * Explicit features defined in the group
488 */
489 KeyedVector<String8, bool> features;
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700490
491 /**
492 * OpenGL ES version required
493 */
494 int openGLESVersion;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700495};
496
497static void addImpliedFeature(KeyedVector<String8, ImpliedFeature>* impliedFeatures,
498 const char* name, const char* reason) {
499 String8 name8(name);
500 ssize_t idx = impliedFeatures->indexOfKey(name8);
501 if (idx < 0) {
502 idx = impliedFeatures->add(name8, ImpliedFeature());
503 impliedFeatures->editValueAt(idx).name = name8;
504 }
505 impliedFeatures->editValueAt(idx).reasons.add(String8(reason));
506}
507
508static void printFeatureGroup(const FeatureGroup& grp,
509 const KeyedVector<String8, ImpliedFeature>* impliedFeatures = NULL) {
510 printf("feature-group: label='%s'\n", grp.label.string());
511
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700512 if (grp.openGLESVersion > 0) {
513 printf(" uses-gl-es: '0x%x'\n", grp.openGLESVersion);
514 }
515
Adam Lesinski2c72b682014-06-24 09:56:01 -0700516 const size_t numFeatures = grp.features.size();
517 for (size_t i = 0; i < numFeatures; i++) {
518 if (!grp.features[i]) {
519 continue;
520 }
521
522 const String8& featureName = grp.features.keyAt(i);
523 printf(" uses-feature: name='%s'\n",
524 ResTable::normalizeForOutput(featureName.string()).string());
525 }
526
527 const size_t numImpliedFeatures =
528 (impliedFeatures != NULL) ? impliedFeatures->size() : 0;
529 for (size_t i = 0; i < numImpliedFeatures; i++) {
530 const ImpliedFeature& impliedFeature = impliedFeatures->valueAt(i);
531 if (grp.features.indexOfKey(impliedFeature.name) >= 0) {
532 // The feature is explicitly set, no need to use implied
533 // definition.
534 continue;
535 }
536
537 String8 printableFeatureName(ResTable::normalizeForOutput(
538 impliedFeature.name.string()));
539 printf(" uses-feature: name='%s'\n", printableFeatureName.string());
540 printf(" uses-implied-feature: name='%s' reason='",
541 printableFeatureName.string());
542 const size_t numReasons = impliedFeature.reasons.size();
543 for (size_t j = 0; j < numReasons; j++) {
544 printf("%s", impliedFeature.reasons[j].string());
545 if (j + 2 < numReasons) {
546 printf(", ");
547 } else if (j + 1 < numReasons) {
548 printf(", and ");
549 }
550 }
551 printf("'\n");
552 }
553}
554
555static void addParentFeatures(FeatureGroup* grp, const String8& name) {
556 if (name == "android.hardware.camera.autofocus" ||
557 name == "android.hardware.camera.flash") {
558 grp->features.add(String8("android.hardware.camera"), true);
559 } else if (name == "android.hardware.location.gps" ||
560 name == "android.hardware.location.network") {
561 grp->features.add(String8("android.hardware.location"), true);
562 } else if (name == "android.hardware.touchscreen.multitouch") {
563 grp->features.add(String8("android.hardware.touchscreen"), true);
564 } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
565 grp->features.add(String8("android.hardware.touchscreen.multitouch"), true);
566 grp->features.add(String8("android.hardware.touchscreen"), true);
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700567 } else if (name == "android.hardware.opengles.aep") {
568 const int openGLESVersion31 = 0x00030001;
569 if (openGLESVersion31 > grp->openGLESVersion) {
570 grp->openGLESVersion = openGLESVersion31;
571 }
Adam Lesinski2c72b682014-06-24 09:56:01 -0700572 }
573}
574
Adam Lesinski282e1812014-01-23 18:17:42 -0800575/*
576 * Handle the "dump" command, to extract select data from an archive.
577 */
578extern char CONSOLE_DATA[2925]; // see EOF
579int doDump(Bundle* bundle)
580{
581 status_t result = UNKNOWN_ERROR;
Adam Lesinski282e1812014-01-23 18:17:42 -0800582
583 if (bundle->getFileSpecCount() < 1) {
584 fprintf(stderr, "ERROR: no dump option specified\n");
585 return 1;
586 }
587
588 if (bundle->getFileSpecCount() < 2) {
589 fprintf(stderr, "ERROR: no dump file specified\n");
590 return 1;
591 }
592
593 const char* option = bundle->getFileSpecEntry(0);
594 const char* filename = bundle->getFileSpecEntry(1);
595
596 AssetManager assets;
Narayan Kamathf85e41f2014-01-24 14:14:30 +0000597 int32_t assetsCookie;
Adam Lesinski282e1812014-01-23 18:17:42 -0800598 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
599 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
600 return 1;
601 }
602
603 // Make a dummy config for retrieving resources... we need to supply
604 // non-default values for some configs so that we can retrieve resources
605 // in the app that don't have a default. The most important of these is
606 // the API version because key resources like icons will have an implicit
607 // version if they are using newer config types like density.
608 ResTable_config config;
Narayan Kamath91447d82014-01-21 15:32:36 +0000609 memset(&config, 0, sizeof(ResTable_config));
Adam Lesinski282e1812014-01-23 18:17:42 -0800610 config.language[0] = 'e';
611 config.language[1] = 'n';
612 config.country[0] = 'U';
613 config.country[1] = 'S';
614 config.orientation = ResTable_config::ORIENTATION_PORT;
615 config.density = ResTable_config::DENSITY_MEDIUM;
616 config.sdkVersion = 10000; // Very high.
617 config.screenWidthDp = 320;
618 config.screenHeightDp = 480;
619 config.smallestScreenWidthDp = 320;
Adam Lesinskic2dea8d2014-08-04 16:40:41 -0700620 config.screenLayout |= ResTable_config::SCREENSIZE_NORMAL;
Adam Lesinski282e1812014-01-23 18:17:42 -0800621 assets.setConfiguration(config);
622
623 const ResTable& res = assets.getResources(false);
624 if (&res == NULL) {
625 fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
Adam Lesinski63e646e2014-07-30 11:40:39 -0700626 return 1;
Adam Lesinski25e9d552014-05-19 15:01:43 -0700627 } else if (res.getError() != NO_ERROR) {
628 fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
Adam Lesinski63e646e2014-07-30 11:40:39 -0700629 return 1;
Adam Lesinski282e1812014-01-23 18:17:42 -0800630 }
631
Adam Lesinski2cb761e2014-08-15 13:59:02 -0700632 // The dynamicRefTable can be null if there are no resources for this asset cookie.
633 // This fine.
Adam Lesinski63e646e2014-07-30 11:40:39 -0700634 const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700635
636 Asset* asset = NULL;
637
Adam Lesinski282e1812014-01-23 18:17:42 -0800638 if (strcmp("resources", option) == 0) {
639#ifndef HAVE_ANDROID_OS
640 res.print(bundle->getValues());
641#endif
642
643 } else if (strcmp("strings", option) == 0) {
644 const ResStringPool* pool = res.getTableStringBlock(0);
645 printStringPool(pool);
646
647 } else if (strcmp("xmltree", option) == 0) {
648 if (bundle->getFileSpecCount() < 3) {
649 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
650 goto bail;
651 }
652
653 for (int i=2; i<bundle->getFileSpecCount(); i++) {
654 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700655 ResXMLTree tree(dynamicRefTable);
656 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800657 if (asset == NULL) {
658 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
659 goto bail;
660 }
661
662 if (tree.setTo(asset->getBuffer(true),
663 asset->getLength()) != NO_ERROR) {
664 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
665 goto bail;
666 }
667 tree.restart();
668 printXMLBlock(&tree);
669 tree.uninit();
670 delete asset;
671 asset = NULL;
672 }
673
674 } else if (strcmp("xmlstrings", option) == 0) {
675 if (bundle->getFileSpecCount() < 3) {
676 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
677 goto bail;
678 }
679
680 for (int i=2; i<bundle->getFileSpecCount(); i++) {
681 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700682 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800683 if (asset == NULL) {
684 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
685 goto bail;
686 }
687
Adam Lesinski63e646e2014-07-30 11:40:39 -0700688 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800689 if (tree.setTo(asset->getBuffer(true),
690 asset->getLength()) != NO_ERROR) {
691 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
692 goto bail;
693 }
694 printStringPool(&tree.getStrings());
695 delete asset;
696 asset = NULL;
697 }
698
699 } else {
Adam Lesinski63e646e2014-07-30 11:40:39 -0700700 asset = assets.openNonAsset(assetsCookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800701 if (asset == NULL) {
702 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
703 goto bail;
704 }
705
Adam Lesinski63e646e2014-07-30 11:40:39 -0700706 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800707 if (tree.setTo(asset->getBuffer(true),
708 asset->getLength()) != NO_ERROR) {
709 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
710 goto bail;
711 }
712 tree.restart();
713
714 if (strcmp("permissions", option) == 0) {
715 size_t len;
716 ResXMLTree::event_code_t code;
717 int depth = 0;
718 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
719 if (code == ResXMLTree::END_TAG) {
720 depth--;
721 continue;
722 }
723 if (code != ResXMLTree::START_TAG) {
724 continue;
725 }
726 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700727 const char16_t* ctag16 = tree.getElementName(&len);
728 if (ctag16 == NULL) {
729 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
730 goto bail;
731 }
732 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800733 //printf("Depth %d tag %s\n", depth, tag.string());
734 if (depth == 1) {
735 if (tag != "manifest") {
736 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
737 goto bail;
738 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700739 String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -0700740 printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800741 } else if (depth == 2 && tag == "permission") {
742 String8 error;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700743 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -0800744 if (error != "") {
745 fprintf(stderr, "ERROR: %s\n", error.string());
746 goto bail;
747 }
Maurice Chu2675f762013-10-22 17:33:11 -0700748 printf("permission: %s\n",
749 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800750 } else if (depth == 2 && tag == "uses-permission") {
751 String8 error;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700752 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -0800753 if (error != "") {
754 fprintf(stderr, "ERROR: %s\n", error.string());
755 goto bail;
756 }
Adam Lesinski58f1f362013-11-12 12:59:08 -0800757 printUsesPermission(name,
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700758 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
759 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
Adam Lesinski282e1812014-01-23 18:17:42 -0800760 }
761 }
762 } else if (strcmp("badging", option) == 0) {
763 Vector<String8> locales;
764 res.getLocales(&locales);
765
766 Vector<ResTable_config> configs;
767 res.getConfigurations(&configs);
768 SortedVector<int> densities;
769 const size_t NC = configs.size();
770 for (size_t i=0; i<NC; i++) {
771 int dens = configs[i].density;
772 if (dens == 0) {
773 dens = 160;
774 }
775 densities.add(dens);
776 }
777
778 size_t len;
779 ResXMLTree::event_code_t code;
780 int depth = 0;
781 String8 error;
782 bool withinActivity = false;
783 bool isMainActivity = false;
784 bool isLauncherActivity = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800785 bool isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800786 bool isSearchable = false;
787 bool withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700788 bool withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700789 bool withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800790 bool withinReceiver = false;
791 bool withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700792 bool withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800793 bool withinIntentFilter = false;
794 bool hasMainActivity = false;
795 bool hasOtherActivities = false;
796 bool hasOtherReceivers = false;
797 bool hasOtherServices = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700798 bool hasIntentFilter = false;
799
Adam Lesinski282e1812014-01-23 18:17:42 -0800800 bool hasWallpaperService = false;
801 bool hasImeService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700802 bool hasAccessibilityService = false;
803 bool hasPrintService = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800804 bool hasWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700805 bool hasDeviceAdminReceiver = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700806 bool hasPaymentService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700807 bool hasDocumentsProvider = false;
808 bool hasCameraActivity = false;
809 bool hasCameraSecureActivity = false;
810 bool hasLauncher = false;
811 bool hasNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400812 bool hasDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700813
Adam Lesinski282e1812014-01-23 18:17:42 -0800814 bool actMainActivity = false;
815 bool actWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700816 bool actDeviceAdminEnabled = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800817 bool actImeService = false;
818 bool actWallpaperService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700819 bool actAccessibilityService = false;
820 bool actPrintService = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700821 bool actHostApduService = false;
822 bool actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700823 bool actDocumentsProvider = false;
824 bool actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400825 bool actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700826 bool actCamera = false;
827 bool actCameraSecure = false;
828 bool catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700829 bool hasMetaHostPaymentCategory = false;
830 bool hasMetaOffHostPaymentCategory = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700831
832 // These permissions are required by services implementing services
833 // the system binds to (IME, Accessibility, PrintServices, etc.)
834 bool hasBindDeviceAdminPermission = false;
835 bool hasBindInputMethodPermission = false;
836 bool hasBindAccessibilityServicePermission = false;
837 bool hasBindPrintServicePermission = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700838 bool hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700839 bool hasRequiredSafAttributes = false;
840 bool hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400841 bool hasBindDreamServicePermission = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800842
843 // These two implement the implicit permissions that are granted
844 // to pre-1.6 applications.
845 bool hasWriteExternalStoragePermission = false;
846 bool hasReadPhoneStatePermission = false;
847
848 // If an app requests write storage, they will also get read storage.
849 bool hasReadExternalStoragePermission = false;
850
851 // Implement transition to read and write call log.
852 bool hasReadContactsPermission = false;
853 bool hasWriteContactsPermission = false;
854 bool hasReadCallLogPermission = false;
855 bool hasWriteCallLogPermission = false;
856
Adam Lesinskie47fd122014-08-15 22:25:36 -0700857 // If an app declares itself as multiArch, we report the
858 // native libraries differently.
859 bool hasMultiArch = false;
860
Adam Lesinski282e1812014-01-23 18:17:42 -0800861 // This next group of variables is used to implement a group of
862 // backward-compatibility heuristics necessitated by the addition of
863 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
864 // heuristic is "if an app requests a permission but doesn't explicitly
865 // request the corresponding <uses-feature>, presume it's there anyway".
Adam Lesinski2c72b682014-06-24 09:56:01 -0700866
Adam Lesinski282e1812014-01-23 18:17:42 -0800867 // 2.2 also added some other features that apps can request, but that
868 // have no corresponding permission, so we cannot implement any
869 // back-compatibility heuristic for them. The below are thus unnecessary
870 // (but are retained here for documentary purposes.)
871 //bool specCompassFeature = false;
872 //bool specAccelerometerFeature = false;
873 //bool specProximityFeature = false;
874 //bool specAmbientLightFeature = false;
875 //bool specLiveWallpaperFeature = false;
876
877 int targetSdk = 0;
878 int smallScreen = 1;
879 int normalScreen = 1;
880 int largeScreen = 1;
881 int xlargeScreen = 1;
882 int anyDensity = 1;
883 int requiresSmallestWidthDp = 0;
884 int compatibleWidthLimitDp = 0;
885 int largestWidthLimitDp = 0;
886 String8 pkg;
887 String8 activityName;
888 String8 activityLabel;
889 String8 activityIcon;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800890 String8 activityBanner;
Adam Lesinski282e1812014-01-23 18:17:42 -0800891 String8 receiverName;
892 String8 serviceName;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700893 Vector<String8> supportedInput;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700894
895 FeatureGroup commonFeatures;
896 Vector<FeatureGroup> featureGroups;
897 KeyedVector<String8, ImpliedFeature> impliedFeatures;
898
Adam Lesinski282e1812014-01-23 18:17:42 -0800899 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
900 if (code == ResXMLTree::END_TAG) {
901 depth--;
902 if (depth < 2) {
Michael Wrightec4fdec2013-09-06 16:50:52 -0700903 if (withinSupportsInput && !supportedInput.isEmpty()) {
904 printf("supports-input: '");
905 const size_t N = supportedInput.size();
906 for (size_t i=0; i<N; i++) {
Maurice Chu2675f762013-10-22 17:33:11 -0700907 printf("%s", ResTable::normalizeForOutput(
908 supportedInput[i].string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -0700909 if (i != N - 1) {
910 printf("' '");
911 } else {
912 printf("'\n");
913 }
914 }
915 supportedInput.clear();
916 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800917 withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700918 withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700919 withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800920 } else if (depth < 3) {
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800921 if (withinActivity && isMainActivity) {
Maurice Chu2675f762013-10-22 17:33:11 -0700922 String8 aName(getComponentName(pkg, activityName));
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800923 if (isLauncherActivity) {
924 printf("launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800925 if (aName.length() > 0) {
926 printf(" name='%s' ",
927 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800928 }
929 printf(" label='%s' icon='%s'\n",
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800930 ResTable::normalizeForOutput(activityLabel.string()).string(),
931 ResTable::normalizeForOutput(activityIcon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800932 }
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800933 if (isLeanbackLauncherActivity) {
934 printf("leanback-launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800935 if (aName.length() > 0) {
936 printf(" name='%s' ",
937 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800938 }
939 printf(" label='%s' icon='%s' banner='%s'\n",
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800940 ResTable::normalizeForOutput(activityLabel.string()).string(),
941 ResTable::normalizeForOutput(activityIcon.string()).string(),
942 ResTable::normalizeForOutput(activityBanner.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800943 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800944 }
945 if (!hasIntentFilter) {
946 hasOtherActivities |= withinActivity;
947 hasOtherReceivers |= withinReceiver;
948 hasOtherServices |= withinService;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700949 } else {
950 if (withinService) {
951 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
952 hasBindNfcServicePermission);
953 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
954 hasBindNfcServicePermission);
955 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800956 }
957 withinActivity = false;
958 withinService = false;
959 withinReceiver = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700960 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800961 hasIntentFilter = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800962 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800963 } else if (depth < 4) {
964 if (withinIntentFilter) {
965 if (withinActivity) {
966 hasMainActivity |= actMainActivity;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700967 hasLauncher |= catLauncher;
968 hasCameraActivity |= actCamera;
969 hasCameraSecureActivity |= actCameraSecure;
970 hasOtherActivities |= !actMainActivity && !actCamera && !actCameraSecure;
Adam Lesinski282e1812014-01-23 18:17:42 -0800971 } else if (withinReceiver) {
972 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700973 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
974 hasBindDeviceAdminPermission);
975 hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled);
Adam Lesinski282e1812014-01-23 18:17:42 -0800976 } else if (withinService) {
977 hasImeService |= actImeService;
978 hasWallpaperService |= actWallpaperService;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700979 hasAccessibilityService |= (actAccessibilityService &&
980 hasBindAccessibilityServicePermission);
981 hasPrintService |= (actPrintService && hasBindPrintServicePermission);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700982 hasNotificationListenerService |= actNotificationListenerService &&
983 hasBindNotificationListenerServicePermission;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400984 hasDreamService |= actDreamService && hasBindDreamServicePermission;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700985 hasOtherServices |= (!actImeService && !actWallpaperService &&
Adam Lesinski94fc9122013-09-30 17:16:09 -0700986 !actAccessibilityService && !actPrintService &&
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700987 !actHostApduService && !actOffHostApduService &&
988 !actNotificationListenerService);
989 } else if (withinProvider) {
990 hasDocumentsProvider |= actDocumentsProvider && hasRequiredSafAttributes;
Adam Lesinski282e1812014-01-23 18:17:42 -0800991 }
992 }
993 withinIntentFilter = false;
994 }
995 continue;
996 }
997 if (code != ResXMLTree::START_TAG) {
998 continue;
999 }
1000 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001001
1002 const char16_t* ctag16 = tree.getElementName(&len);
1003 if (ctag16 == NULL) {
1004 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
1005 goto bail;
1006 }
1007 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -08001008 //printf("Depth %d, %s\n", depth, tag.string());
1009 if (depth == 1) {
1010 if (tag != "manifest") {
1011 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
1012 goto bail;
1013 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001014 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -07001015 printf("package: name='%s' ",
1016 ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001017 int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR,
1018 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001019 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001020 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n",
1021 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001022 goto bail;
1023 }
1024 if (versionCode > 0) {
1025 printf("versionCode='%d' ", versionCode);
1026 } else {
1027 printf("versionCode='' ");
1028 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001029 String8 versionName = AaptXml::getResolvedAttribute(res, tree,
1030 VERSION_NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001031 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001032 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n",
1033 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001034 goto bail;
1035 }
Adam Lesinski25d35a92014-08-11 09:41:56 -07001036 printf("versionName='%s'",
Maurice Chu2675f762013-10-22 17:33:11 -07001037 ResTable::normalizeForOutput(versionName.string()).string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001038
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001039 String8 splitName = AaptXml::getAttribute(tree, NULL, "split");
Adam Lesinski25d35a92014-08-11 09:41:56 -07001040 if (!splitName.isEmpty()) {
1041 printf(" split='%s'", ResTable::normalizeForOutput(
1042 splitName.string()).string());
1043 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001044
Adam Lesinski5283fab2014-08-29 11:23:55 -07001045 String8 platformVersionName = AaptXml::getAttribute(tree, NULL,
1046 "platformBuildVersionName");
1047 printf(" platformBuildVersionName='%s'", platformVersionName.string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001048 printf("\n");
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001049
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001050 int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
1051 INSTALL_LOCATION_ATTR, &error);
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001052 if (error != "") {
1053 fprintf(stderr, "ERROR getting 'android:installLocation' attribute: %s\n",
1054 error.string());
1055 goto bail;
1056 }
1057
1058 if (installLocation >= 0) {
1059 printf("install-location:'");
1060 switch (installLocation) {
1061 case 0:
1062 printf("auto");
1063 break;
1064 case 1:
1065 printf("internalOnly");
1066 break;
1067 case 2:
1068 printf("preferExternal");
1069 break;
1070 default:
1071 fprintf(stderr, "Invalid installLocation %d\n", installLocation);
1072 goto bail;
1073 }
1074 printf("'\n");
1075 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001076 } else if (depth == 2) {
1077 withinApplication = false;
1078 if (tag == "application") {
1079 withinApplication = true;
1080
1081 String8 label;
1082 const size_t NL = locales.size();
1083 for (size_t i=0; i<NL; i++) {
1084 const char* localeStr = locales[i].string();
1085 assets.setLocale(localeStr != NULL ? localeStr : "");
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001086 String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1087 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001088 if (llabel != "") {
1089 if (localeStr == NULL || strlen(localeStr) == 0) {
1090 label = llabel;
Maurice Chu2675f762013-10-22 17:33:11 -07001091 printf("application-label:'%s'\n",
1092 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001093 } else {
1094 if (label == "") {
1095 label = llabel;
1096 }
1097 printf("application-label-%s:'%s'\n", localeStr,
Maurice Chu2675f762013-10-22 17:33:11 -07001098 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001099 }
1100 }
1101 }
1102
1103 ResTable_config tmpConfig = config;
1104 const size_t ND = densities.size();
1105 for (size_t i=0; i<ND; i++) {
1106 tmpConfig.density = densities[i];
1107 assets.setConfiguration(tmpConfig);
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001108 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1109 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001110 if (icon != "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001111 printf("application-icon-%d:'%s'\n", densities[i],
1112 ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001113 }
1114 }
1115 assets.setConfiguration(config);
1116
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001117 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001118 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001119 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1120 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001121 goto bail;
1122 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001123 int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0,
1124 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001125 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001126 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n",
1127 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001128 goto bail;
1129 }
Maurice Chu2675f762013-10-22 17:33:11 -07001130 printf("application: label='%s' ",
1131 ResTable::normalizeForOutput(label.string()).string());
1132 printf("icon='%s'\n", ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001133 if (testOnly != 0) {
1134 printf("testOnly='%d'\n", testOnly);
1135 }
1136
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001137 int32_t debuggable = AaptXml::getResolvedIntegerAttribute(res, tree,
1138 DEBUGGABLE_ATTR, 0, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001139 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001140 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n",
1141 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001142 goto bail;
1143 }
1144 if (debuggable != 0) {
1145 printf("application-debuggable\n");
1146 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07001147
1148 // We must search by name because the multiArch flag hasn't been API
1149 // frozen yet.
1150 int32_t multiArchIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
1151 "multiArch");
1152 if (multiArchIndex >= 0) {
1153 Res_value value;
1154 if (tree.getAttributeValue(multiArchIndex, &value) != NO_ERROR) {
1155 if (value.dataType >= Res_value::TYPE_FIRST_INT &&
1156 value.dataType <= Res_value::TYPE_LAST_INT) {
1157 hasMultiArch = value.data;
1158 }
1159 }
1160 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001161 } else if (tag == "uses-sdk") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001162 int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001163 if (error != "") {
1164 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001165 String8 name = AaptXml::getResolvedAttribute(res, tree,
1166 MIN_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001167 if (error != "") {
1168 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
1169 error.string());
1170 goto bail;
1171 }
1172 if (name == "Donut") targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001173 printf("sdkVersion:'%s'\n",
1174 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001175 } else if (code != -1) {
1176 targetSdk = code;
1177 printf("sdkVersion:'%d'\n", code);
1178 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001179 code = AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -08001180 if (code != -1) {
1181 printf("maxSdkVersion:'%d'\n", code);
1182 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001183 code = AaptXml::getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001184 if (error != "") {
1185 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001186 String8 name = AaptXml::getResolvedAttribute(res, tree,
1187 TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001188 if (error != "") {
1189 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
1190 error.string());
1191 goto bail;
1192 }
1193 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001194 printf("targetSdkVersion:'%s'\n",
1195 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001196 } else if (code != -1) {
1197 if (targetSdk < code) {
1198 targetSdk = code;
1199 }
1200 printf("targetSdkVersion:'%d'\n", code);
1201 }
1202 } else if (tag == "uses-configuration") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001203 int32_t reqTouchScreen = AaptXml::getIntegerAttribute(tree,
1204 REQ_TOUCH_SCREEN_ATTR, 0);
1205 int32_t reqKeyboardType = AaptXml::getIntegerAttribute(tree,
1206 REQ_KEYBOARD_TYPE_ATTR, 0);
1207 int32_t reqHardKeyboard = AaptXml::getIntegerAttribute(tree,
1208 REQ_HARD_KEYBOARD_ATTR, 0);
1209 int32_t reqNavigation = AaptXml::getIntegerAttribute(tree,
1210 REQ_NAVIGATION_ATTR, 0);
1211 int32_t reqFiveWayNav = AaptXml::getIntegerAttribute(tree,
1212 REQ_FIVE_WAY_NAV_ATTR, 0);
Adam Lesinski282e1812014-01-23 18:17:42 -08001213 printf("uses-configuration:");
1214 if (reqTouchScreen != 0) {
1215 printf(" reqTouchScreen='%d'", reqTouchScreen);
1216 }
1217 if (reqKeyboardType != 0) {
1218 printf(" reqKeyboardType='%d'", reqKeyboardType);
1219 }
1220 if (reqHardKeyboard != 0) {
1221 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1222 }
1223 if (reqNavigation != 0) {
1224 printf(" reqNavigation='%d'", reqNavigation);
1225 }
1226 if (reqFiveWayNav != 0) {
1227 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1228 }
1229 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001230 } else if (tag == "supports-input") {
1231 withinSupportsInput = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001232 } else if (tag == "supports-screens") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001233 smallScreen = AaptXml::getIntegerAttribute(tree,
1234 SMALL_SCREEN_ATTR, 1);
1235 normalScreen = AaptXml::getIntegerAttribute(tree,
1236 NORMAL_SCREEN_ATTR, 1);
1237 largeScreen = AaptXml::getIntegerAttribute(tree,
1238 LARGE_SCREEN_ATTR, 1);
1239 xlargeScreen = AaptXml::getIntegerAttribute(tree,
1240 XLARGE_SCREEN_ATTR, 1);
1241 anyDensity = AaptXml::getIntegerAttribute(tree,
1242 ANY_DENSITY_ATTR, 1);
1243 requiresSmallestWidthDp = AaptXml::getIntegerAttribute(tree,
1244 REQUIRES_SMALLEST_WIDTH_DP_ATTR, 0);
1245 compatibleWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1246 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, 0);
1247 largestWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1248 LARGEST_WIDTH_LIMIT_DP_ATTR, 0);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001249 } else if (tag == "feature-group") {
1250 withinFeatureGroup = true;
1251 FeatureGroup group;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001252 group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001253 if (error != "") {
1254 fprintf(stderr, "ERROR getting 'android:label' attribute:"
1255 " %s\n", error.string());
1256 goto bail;
1257 }
1258 featureGroups.add(group);
1259
Adam Lesinski282e1812014-01-23 18:17:42 -08001260 } else if (tag == "uses-feature") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001261 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001262 if (name != "" && error == "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001263 int req = AaptXml::getIntegerAttribute(tree,
1264 REQUIRED_ATTR, 1);
Adam Lesinski282e1812014-01-23 18:17:42 -08001265
Adam Lesinski2c72b682014-06-24 09:56:01 -07001266 commonFeatures.features.add(name, req);
1267 if (req) {
1268 addParentFeatures(&commonFeatures, name);
Adam Lesinski282e1812014-01-23 18:17:42 -08001269 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001270 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001271 int vers = AaptXml::getIntegerAttribute(tree,
Adam Lesinski282e1812014-01-23 18:17:42 -08001272 GL_ES_VERSION_ATTR, &error);
1273 if (error == "") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001274 if (vers > commonFeatures.openGLESVersion) {
1275 commonFeatures.openGLESVersion = vers;
1276 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001277 }
1278 }
1279 } else if (tag == "uses-permission") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001280 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001281 if (name != "" && error == "") {
1282 if (name == "android.permission.CAMERA") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001283 addImpliedFeature(&impliedFeatures, "android.hardware.camera",
Adam Lesinski2c72b682014-06-24 09:56:01 -07001284 String8::format("requested %s permission", name.string())
1285 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001286 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001287 addImpliedFeature(&impliedFeatures, "android.hardware.location.gps",
1288 String8::format("requested %s permission", name.string())
1289 .string());
1290 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1291 String8::format("requested %s permission", name.string())
1292 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001293 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001294 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1295 String8::format("requested %s permission", name.string())
1296 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001297 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001298 addImpliedFeature(&impliedFeatures, "android.hardware.location.network",
1299 String8::format("requested %s permission", name.string())
1300 .string());
1301 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1302 String8::format("requested %s permission", name.string())
1303 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001304 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
1305 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001306 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1307 String8::format("requested %s permission", name.string())
1308 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001309 } else if (name == "android.permission.BLUETOOTH" ||
1310 name == "android.permission.BLUETOOTH_ADMIN") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001311 if (targetSdk > 4) {
1312 addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
1313 String8::format("requested %s permission", name.string())
1314 .string());
1315 addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
1316 "targetSdkVersion > 4");
1317 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001318 } else if (name == "android.permission.RECORD_AUDIO") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001319 addImpliedFeature(&impliedFeatures, "android.hardware.microphone",
1320 String8::format("requested %s permission", name.string())
1321 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001322 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1323 name == "android.permission.CHANGE_WIFI_STATE" ||
1324 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001325 addImpliedFeature(&impliedFeatures, "android.hardware.wifi",
1326 String8::format("requested %s permission", name.string())
1327 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001328 } else if (name == "android.permission.CALL_PHONE" ||
1329 name == "android.permission.CALL_PRIVILEGED" ||
1330 name == "android.permission.MODIFY_PHONE_STATE" ||
1331 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1332 name == "android.permission.READ_SMS" ||
1333 name == "android.permission.RECEIVE_SMS" ||
1334 name == "android.permission.RECEIVE_MMS" ||
1335 name == "android.permission.RECEIVE_WAP_PUSH" ||
1336 name == "android.permission.SEND_SMS" ||
1337 name == "android.permission.WRITE_APN_SETTINGS" ||
1338 name == "android.permission.WRITE_SMS") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001339 addImpliedFeature(&impliedFeatures, "android.hardware.telephony",
1340 String8("requested a telephony permission").string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001341 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1342 hasWriteExternalStoragePermission = true;
1343 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1344 hasReadExternalStoragePermission = true;
1345 } else if (name == "android.permission.READ_PHONE_STATE") {
1346 hasReadPhoneStatePermission = true;
1347 } else if (name == "android.permission.READ_CONTACTS") {
1348 hasReadContactsPermission = true;
1349 } else if (name == "android.permission.WRITE_CONTACTS") {
1350 hasWriteContactsPermission = true;
1351 } else if (name == "android.permission.READ_CALL_LOG") {
1352 hasReadCallLogPermission = true;
1353 } else if (name == "android.permission.WRITE_CALL_LOG") {
1354 hasWriteCallLogPermission = true;
1355 }
Adam Lesinski58f1f362013-11-12 12:59:08 -08001356
1357 printUsesPermission(name,
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001358 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
1359 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
Adam Lesinski58f1f362013-11-12 12:59:08 -08001360 } else {
Adam Lesinski282e1812014-01-23 18:17:42 -08001361 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1362 error.string());
1363 goto bail;
1364 }
1365 } else if (tag == "uses-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001366 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001367 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001368 printf("uses-package:'%s'\n",
1369 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001370 } else {
1371 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1372 error.string());
1373 goto bail;
1374 }
1375 } else if (tag == "original-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001376 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001377 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001378 printf("original-package:'%s'\n",
1379 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001380 } else {
1381 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1382 error.string());
1383 goto bail;
1384 }
1385 } else if (tag == "supports-gl-texture") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001386 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001387 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001388 printf("supports-gl-texture:'%s'\n",
1389 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001390 } else {
1391 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1392 error.string());
1393 goto bail;
1394 }
1395 } else if (tag == "compatible-screens") {
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001396 printCompatibleScreens(tree, &error);
1397 if (error != "") {
1398 fprintf(stderr, "ERROR getting compatible screens: %s\n",
1399 error.string());
1400 goto bail;
1401 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001402 depth--;
1403 } else if (tag == "package-verifier") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001404 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001405 if (name != "" && error == "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001406 String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001407 if (publicKey != "" && error == "") {
1408 printf("package-verifier: name='%s' publicKey='%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001409 ResTable::normalizeForOutput(name.string()).string(),
1410 ResTable::normalizeForOutput(publicKey.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001411 }
1412 }
1413 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001414 } else if (depth == 3) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001415 withinActivity = false;
1416 withinReceiver = false;
1417 withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001418 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001419 hasIntentFilter = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001420 hasMetaHostPaymentCategory = false;
1421 hasMetaOffHostPaymentCategory = false;
1422 hasBindDeviceAdminPermission = false;
1423 hasBindInputMethodPermission = false;
1424 hasBindAccessibilityServicePermission = false;
1425 hasBindPrintServicePermission = false;
1426 hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001427 hasRequiredSafAttributes = false;
1428 hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001429 hasBindDreamServicePermission = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001430 if (withinApplication) {
1431 if(tag == "activity") {
1432 withinActivity = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001433 activityName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001434 if (error != "") {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001435 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1436 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001437 goto bail;
1438 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001439
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001440 activityLabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1441 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001442 if (error != "") {
1443 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
1444 error.string());
1445 goto bail;
1446 }
1447
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001448 activityIcon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1449 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001450 if (error != "") {
1451 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1452 error.string());
1453 goto bail;
1454 }
1455
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001456 activityBanner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1457 &error);
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001458 if (error != "") {
1459 fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
1460 error.string());
1461 goto bail;
1462 }
1463
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001464 int32_t orien = AaptXml::getResolvedIntegerAttribute(res, tree,
Michael Wrightec4fdec2013-09-06 16:50:52 -07001465 SCREEN_ORIENTATION_ATTR, &error);
1466 if (error == "") {
1467 if (orien == 0 || orien == 6 || orien == 8) {
1468 // Requests landscape, sensorLandscape, or reverseLandscape.
Adam Lesinski2c72b682014-06-24 09:56:01 -07001469 addImpliedFeature(&impliedFeatures, "android.hardware.screen.landscape",
1470 "one or more activities have specified a landscape orientation");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001471 } else if (orien == 1 || orien == 7 || orien == 9) {
1472 // Requests portrait, sensorPortrait, or reversePortrait.
Adam Lesinski2c72b682014-06-24 09:56:01 -07001473 addImpliedFeature(&impliedFeatures, "android.hardware.screen.portrait",
1474 "one or more activities have specified a portrait orientation");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001475 }
1476 }
1477 } else if (tag == "uses-library") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001478 String8 libraryName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001479 if (error != "") {
1480 fprintf(stderr,
1481 "ERROR getting 'android:name' attribute for uses-library"
1482 " %s\n", error.string());
1483 goto bail;
1484 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001485 int req = AaptXml::getIntegerAttribute(tree,
1486 REQUIRED_ATTR, 1);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001487 printf("uses-library%s:'%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001488 req ? "" : "-not-required", ResTable::normalizeForOutput(
1489 libraryName.string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001490 } else if (tag == "receiver") {
1491 withinReceiver = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001492 receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001493
1494 if (error != "") {
1495 fprintf(stderr,
1496 "ERROR getting 'android:name' attribute for receiver:"
1497 " %s\n", error.string());
1498 goto bail;
1499 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001500
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001501 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1502 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001503 if (error == "") {
1504 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1505 hasBindDeviceAdminPermission = true;
1506 }
1507 } else {
1508 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1509 " receiver '%s': %s\n", receiverName.string(), error.string());
1510 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001511 } else if (tag == "service") {
1512 withinService = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001513 serviceName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001514
1515 if (error != "") {
1516 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1517 "service:%s\n", error.string());
1518 goto bail;
1519 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001520
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001521 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1522 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001523 if (error == "") {
1524 if (permission == "android.permission.BIND_INPUT_METHOD") {
1525 hasBindInputMethodPermission = true;
1526 } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") {
1527 hasBindAccessibilityServicePermission = true;
1528 } else if (permission == "android.permission.BIND_PRINT_SERVICE") {
1529 hasBindPrintServicePermission = true;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001530 } else if (permission == "android.permission.BIND_NFC_SERVICE") {
1531 hasBindNfcServicePermission = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001532 } else if (permission == "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
1533 hasBindNotificationListenerServicePermission = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001534 } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
1535 hasBindDreamServicePermission = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001536 }
1537 } else {
1538 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1539 " service '%s': %s\n", serviceName.string(), error.string());
1540 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001541 } else if (tag == "provider") {
1542 withinProvider = true;
1543
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001544 bool exported = AaptXml::getResolvedIntegerAttribute(res, tree,
1545 EXPORTED_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001546 if (error != "") {
1547 fprintf(stderr, "ERROR getting 'android:exported' attribute for provider:"
1548 " %s\n", error.string());
1549 goto bail;
1550 }
1551
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001552 bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute(
1553 res, tree, GRANT_URI_PERMISSIONS_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001554 if (error != "") {
1555 fprintf(stderr, "ERROR getting 'android:grantUriPermissions' attribute for provider:"
1556 " %s\n", error.string());
1557 goto bail;
1558 }
1559
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001560 String8 permission = AaptXml::getResolvedAttribute(res, tree,
1561 PERMISSION_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001562 if (error != "") {
1563 fprintf(stderr, "ERROR getting 'android:permission' attribute for provider:"
1564 " %s\n", error.string());
1565 goto bail;
1566 }
1567
1568 hasRequiredSafAttributes |= exported && grantUriPermissions &&
1569 permission == "android.permission.MANAGE_DOCUMENTS";
1570
Michael Wrightec4fdec2013-09-06 16:50:52 -07001571 } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001572 String8 metaDataName = AaptXml::getResolvedAttribute(res, tree,
1573 NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001574 if (error != "") {
1575 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1576 "meta-data:%s\n", error.string());
1577 goto bail;
1578 }
Maurice Chu2675f762013-10-22 17:33:11 -07001579 printf("meta-data: name='%s' ",
1580 ResTable::normalizeForOutput(metaDataName.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001581 printResolvedResourceAttribute(res, tree, VALUE_ATTR, String8("value"),
Maurice Chu76327312013-10-16 18:28:46 -07001582 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001583 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001584 // Try looking for a RESOURCE_ATTR
1585 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001586 printResolvedResourceAttribute(res, tree, RESOURCE_ATTR,
Maurice Chu76327312013-10-16 18:28:46 -07001587 String8("resource"), &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001588 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001589 fprintf(stderr, "ERROR getting 'android:value' or "
1590 "'android:resource' attribute for "
1591 "meta-data:%s\n", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001592 goto bail;
1593 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001594 }
Maurice Chu76327312013-10-16 18:28:46 -07001595 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001596 } else if (withinSupportsInput && tag == "input-type") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001597 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001598 if (name != "" && error == "") {
1599 supportedInput.add(name);
1600 } else {
1601 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1602 error.string());
1603 goto bail;
1604 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001605 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001606 } else if (withinFeatureGroup && tag == "uses-feature") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001607 FeatureGroup& top = featureGroups.editTop();
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001608
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001609 String8 name = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001610 if (name != "" && error == "") {
Adam Lesinskid3edfde2014-08-08 17:32:44 -07001611 top.features.add(name, true);
1612 addParentFeatures(&top, name);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001613 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001614 int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR,
1615 &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001616 if (error == "") {
1617 if (vers > top.openGLESVersion) {
1618 top.openGLESVersion = vers;
1619 }
1620 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001621 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001622 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001623 } else if (depth == 4) {
1624 if (tag == "intent-filter") {
1625 hasIntentFilter = true;
1626 withinIntentFilter = true;
1627 actMainActivity = false;
1628 actWidgetReceivers = false;
1629 actImeService = false;
1630 actWallpaperService = false;
1631 actAccessibilityService = false;
1632 actPrintService = false;
1633 actDeviceAdminEnabled = false;
1634 actHostApduService = false;
1635 actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001636 actDocumentsProvider = false;
1637 actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001638 actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001639 actCamera = false;
1640 actCameraSecure = false;
1641 catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001642 } else if (withinService && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001643 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001644 if (error != "") {
1645 fprintf(stderr, "ERROR getting 'android:name' attribute for"
1646 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1647 goto bail;
1648 }
1649
1650 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1651 name == "android.nfc.cardemulation.off_host_apdu_service") {
1652 bool offHost = true;
1653 if (name == "android.nfc.cardemulation.host_apdu_service") {
1654 offHost = false;
1655 }
1656
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001657 String8 xmlPath = AaptXml::getResolvedAttribute(res, tree,
1658 RESOURCE_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001659 if (error != "") {
1660 fprintf(stderr, "ERROR getting 'android:resource' attribute for"
1661 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1662 goto bail;
1663 }
1664
1665 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1666 offHost, &error);
1667 if (error != "") {
1668 fprintf(stderr, "ERROR getting AID category for service '%s'\n",
1669 serviceName.string());
1670 goto bail;
1671 }
1672
1673 const size_t catLen = categories.size();
1674 for (size_t i = 0; i < catLen; i++) {
1675 bool paymentCategory = (categories[i] == "payment");
1676 if (offHost) {
1677 hasMetaOffHostPaymentCategory |= paymentCategory;
1678 } else {
1679 hasMetaHostPaymentCategory |= paymentCategory;
1680 }
1681 }
1682 }
1683 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001684 } else if ((depth == 5) && withinIntentFilter) {
1685 String8 action;
1686 if (tag == "action") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001687 action = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001688 if (error != "") {
1689 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1690 error.string());
1691 goto bail;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001692 }
1693
Adam Lesinskia5018c92013-09-30 16:23:15 -07001694 if (withinActivity) {
1695 if (action == "android.intent.action.MAIN") {
1696 isMainActivity = true;
1697 actMainActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001698 } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
1699 action == "android.media.action.VIDEO_CAMERA") {
1700 actCamera = true;
1701 } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
1702 actCameraSecure = true;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001703 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001704 } else if (withinReceiver) {
1705 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1706 actWidgetReceivers = true;
1707 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1708 actDeviceAdminEnabled = true;
1709 }
1710 } else if (withinService) {
1711 if (action == "android.view.InputMethod") {
1712 actImeService = true;
1713 } else if (action == "android.service.wallpaper.WallpaperService") {
1714 actWallpaperService = true;
1715 } else if (action == "android.accessibilityservice.AccessibilityService") {
1716 actAccessibilityService = true;
1717 } else if (action == "android.printservice.PrintService") {
1718 actPrintService = true;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001719 } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
1720 actHostApduService = true;
1721 } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
1722 actOffHostApduService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001723 } else if (action == "android.service.notification.NotificationListenerService") {
1724 actNotificationListenerService = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001725 } else if (action == "android.service.dreams.DreamService") {
1726 actDreamService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001727 }
1728 } else if (withinProvider) {
1729 if (action == "android.content.action.DOCUMENTS_PROVIDER") {
1730 actDocumentsProvider = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001731 }
1732 }
1733 if (action == "android.intent.action.SEARCH") {
1734 isSearchable = true;
1735 }
1736 }
1737
1738 if (tag == "category") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001739 String8 category = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001740 if (error != "") {
1741 fprintf(stderr, "ERROR getting 'name' attribute: %s\n",
1742 error.string());
1743 goto bail;
1744 }
1745 if (withinActivity) {
1746 if (category == "android.intent.category.LAUNCHER") {
1747 isLauncherActivity = true;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001748 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
1749 isLeanbackLauncherActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001750 } else if (category == "android.intent.category.HOME") {
1751 catLauncher = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001752 }
1753 }
1754 }
1755 }
1756 }
1757
1758 // Pre-1.6 implicitly granted permission compatibility logic
1759 if (targetSdk < 4) {
1760 if (!hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001761 printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
1762 printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
1763 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001764 hasWriteExternalStoragePermission = true;
1765 }
1766 if (!hasReadPhoneStatePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001767 printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
1768 printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
1769 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001770 }
1771 }
1772
1773 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1774 // force them to always take READ_EXTERNAL_STORAGE as well. We always
1775 // do this (regardless of target API version) because we can't have
1776 // an app with write permission but not read permission.
1777 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001778 printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"));
1779 printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
1780 String8("requested WRITE_EXTERNAL_STORAGE"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001781 }
1782
1783 // Pre-JellyBean call log permission compatibility.
1784 if (targetSdk < 16) {
1785 if (!hasReadCallLogPermission && hasReadContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001786 printUsesPermission(String8("android.permission.READ_CALL_LOG"));
1787 printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
1788 String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001789 }
1790 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001791 printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
1792 printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
1793 String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001794 }
1795 }
1796
Adam Lesinski2c72b682014-06-24 09:56:01 -07001797 addImpliedFeature(&impliedFeatures, "android.hardware.touchscreen",
1798 "default feature for all apps");
1799
1800 const size_t numFeatureGroups = featureGroups.size();
1801 if (numFeatureGroups == 0) {
1802 // If no <feature-group> tags were defined, apply auto-implied features.
1803 printFeatureGroup(commonFeatures, &impliedFeatures);
1804
1805 } else {
1806 // <feature-group> tags are defined, so we ignore implied features and
1807 for (size_t i = 0; i < numFeatureGroups; i++) {
1808 FeatureGroup& grp = featureGroups.editItemAt(i);
1809
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001810 if (commonFeatures.openGLESVersion > grp.openGLESVersion) {
1811 grp.openGLESVersion = commonFeatures.openGLESVersion;
1812 }
1813
Adam Lesinski2c72b682014-06-24 09:56:01 -07001814 // Merge the features defined in the top level (not inside a <feature-group>)
1815 // with this feature group.
1816 const size_t numCommonFeatures = commonFeatures.features.size();
1817 for (size_t j = 0; j < numCommonFeatures; j++) {
1818 if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001819 grp.features.add(commonFeatures.features.keyAt(j),
1820 commonFeatures.features[j]);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001821 }
1822 }
1823
1824 if (!grp.features.isEmpty()) {
1825 printFeatureGroup(grp);
Adam Lesinski282e1812014-01-23 18:17:42 -08001826 }
1827 }
1828 }
1829
Adam Lesinski282e1812014-01-23 18:17:42 -08001830
Adam Lesinski282e1812014-01-23 18:17:42 -08001831 if (hasWidgetReceivers) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001832 printComponentPresence("app-widget");
Adam Lesinski282e1812014-01-23 18:17:42 -08001833 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001834 if (hasDeviceAdminReceiver) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001835 printComponentPresence("device-admin");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001836 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001837 if (hasImeService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001838 printComponentPresence("ime");
Adam Lesinski282e1812014-01-23 18:17:42 -08001839 }
1840 if (hasWallpaperService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001841 printComponentPresence("wallpaper");
Adam Lesinski282e1812014-01-23 18:17:42 -08001842 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001843 if (hasAccessibilityService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001844 printComponentPresence("accessibility");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001845 }
1846 if (hasPrintService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001847 printComponentPresence("print-service");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001848 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001849 if (hasPaymentService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001850 printComponentPresence("payment");
1851 }
1852 if (isSearchable) {
1853 printComponentPresence("search");
1854 }
1855 if (hasDocumentsProvider) {
1856 printComponentPresence("document-provider");
1857 }
1858 if (hasLauncher) {
1859 printComponentPresence("launcher");
1860 }
1861 if (hasNotificationListenerService) {
1862 printComponentPresence("notification-listener");
1863 }
John Spurlockeb8d1be2014-06-25 17:46:15 -04001864 if (hasDreamService) {
1865 printComponentPresence("dream");
1866 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001867 if (hasCameraActivity) {
1868 printComponentPresence("camera");
1869 }
1870 if (hasCameraSecureActivity) {
1871 printComponentPresence("camera-secure");
1872 }
1873
1874 if (hasMainActivity) {
1875 printf("main\n");
Adam Lesinski94fc9122013-09-30 17:16:09 -07001876 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001877 if (hasOtherActivities) {
1878 printf("other-activities\n");
1879 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001880 if (hasOtherReceivers) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001881 printf("other-receivers\n");
1882 }
1883 if (hasOtherServices) {
1884 printf("other-services\n");
1885 }
1886
1887 // For modern apps, if screen size buckets haven't been specified
1888 // but the new width ranges have, then infer the buckets from them.
1889 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1890 && requiresSmallestWidthDp > 0) {
1891 int compatWidth = compatibleWidthLimitDp;
1892 if (compatWidth <= 0) {
1893 compatWidth = requiresSmallestWidthDp;
1894 }
1895 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1896 smallScreen = -1;
1897 } else {
1898 smallScreen = 0;
1899 }
1900 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1901 normalScreen = -1;
1902 } else {
1903 normalScreen = 0;
1904 }
1905 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1906 largeScreen = -1;
1907 } else {
1908 largeScreen = 0;
1909 }
1910 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1911 xlargeScreen = -1;
1912 } else {
1913 xlargeScreen = 0;
1914 }
1915 }
1916
1917 // Determine default values for any unspecified screen sizes,
1918 // based on the target SDK of the package. As of 4 (donut)
1919 // the screen size support was introduced, so all default to
1920 // enabled.
1921 if (smallScreen > 0) {
1922 smallScreen = targetSdk >= 4 ? -1 : 0;
1923 }
1924 if (normalScreen > 0) {
1925 normalScreen = -1;
1926 }
1927 if (largeScreen > 0) {
1928 largeScreen = targetSdk >= 4 ? -1 : 0;
1929 }
1930 if (xlargeScreen > 0) {
1931 // Introduced in Gingerbread.
1932 xlargeScreen = targetSdk >= 9 ? -1 : 0;
1933 }
1934 if (anyDensity > 0) {
1935 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1936 || compatibleWidthLimitDp > 0) ? -1 : 0;
1937 }
1938 printf("supports-screens:");
1939 if (smallScreen != 0) {
1940 printf(" 'small'");
1941 }
1942 if (normalScreen != 0) {
1943 printf(" 'normal'");
1944 }
1945 if (largeScreen != 0) {
1946 printf(" 'large'");
1947 }
1948 if (xlargeScreen != 0) {
1949 printf(" 'xlarge'");
1950 }
1951 printf("\n");
1952 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
1953 if (requiresSmallestWidthDp > 0) {
1954 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1955 }
1956 if (compatibleWidthLimitDp > 0) {
1957 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1958 }
1959 if (largestWidthLimitDp > 0) {
1960 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1961 }
1962
1963 printf("locales:");
1964 const size_t NL = locales.size();
1965 for (size_t i=0; i<NL; i++) {
1966 const char* localeStr = locales[i].string();
1967 if (localeStr == NULL || strlen(localeStr) == 0) {
1968 localeStr = "--_--";
1969 }
1970 printf(" '%s'", localeStr);
1971 }
1972 printf("\n");
1973
1974 printf("densities:");
1975 const size_t ND = densities.size();
1976 for (size_t i=0; i<ND; i++) {
1977 printf(" '%d'", densities[i]);
1978 }
1979 printf("\n");
1980
1981 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1982 if (dir != NULL) {
1983 if (dir->getFileCount() > 0) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07001984 SortedVector<String8> architectures;
Adam Lesinski282e1812014-01-23 18:17:42 -08001985 for (size_t i=0; i<dir->getFileCount(); i++) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07001986 architectures.add(ResTable::normalizeForOutput(
1987 dir->getFileName(i).string()));
Adam Lesinski282e1812014-01-23 18:17:42 -08001988 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07001989
1990 bool outputAltNativeCode = false;
1991 // A multiArch package is one that contains 64-bit and
1992 // 32-bit versions of native code and expects 3rd-party
1993 // apps to load these native code libraries. Since most
1994 // 64-bit systems also support 32-bit apps, the apps
1995 // loading this multiArch package's code may be either
1996 // 32-bit or 64-bit.
1997 if (hasMultiArch) {
1998 // If this is a multiArch package, report the 64-bit
1999 // version only. Then as a separate entry, report the
2000 // rest.
2001 //
2002 // If we report the 32-bit architecture, this APK will
2003 // be installed on a 32-bit device, causing a large waste
2004 // of bandwidth and disk space. This assumes that
2005 // the developer of the multiArch package has also
2006 // made a version that is 32-bit only.
2007 String8 intel64("x86_64");
2008 String8 arm64("arm64-v8a");
2009 ssize_t index = architectures.indexOf(intel64);
2010 if (index < 0) {
2011 index = architectures.indexOf(arm64);
2012 }
2013
2014 if (index >= 0) {
2015 printf("native-code: '%s'\n", architectures[index].string());
2016 architectures.removeAt(index);
2017 outputAltNativeCode = true;
2018 }
2019 }
2020
2021 const size_t archCount = architectures.size();
2022 if (archCount > 0) {
2023 if (outputAltNativeCode) {
2024 printf("alt-");
2025 }
2026 printf("native-code:");
2027 for (size_t i = 0; i < archCount; i++) {
2028 printf(" '%s'", architectures[i].string());
2029 }
2030 printf("\n");
2031 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002032 }
2033 delete dir;
2034 }
2035 } else if (strcmp("badger", option) == 0) {
2036 printf("%s", CONSOLE_DATA);
2037 } else if (strcmp("configurations", option) == 0) {
2038 Vector<ResTable_config> configs;
2039 res.getConfigurations(&configs);
2040 const size_t N = configs.size();
2041 for (size_t i=0; i<N; i++) {
2042 printf("%s\n", configs[i].toString().string());
2043 }
2044 } else {
2045 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2046 goto bail;
2047 }
2048 }
2049
2050 result = NO_ERROR;
2051
2052bail:
2053 if (asset) {
2054 delete asset;
2055 }
2056 return (result != NO_ERROR);
2057}
2058
2059
2060/*
2061 * Handle the "add" command, which wants to add files to a new or
2062 * pre-existing archive.
2063 */
2064int doAdd(Bundle* bundle)
2065{
2066 ZipFile* zip = NULL;
2067 status_t result = UNKNOWN_ERROR;
2068 const char* zipFileName;
2069
2070 if (bundle->getUpdate()) {
2071 /* avoid confusion */
2072 fprintf(stderr, "ERROR: can't use '-u' with add\n");
2073 goto bail;
2074 }
2075
2076 if (bundle->getFileSpecCount() < 1) {
2077 fprintf(stderr, "ERROR: must specify zip file name\n");
2078 goto bail;
2079 }
2080 zipFileName = bundle->getFileSpecEntry(0);
2081
2082 if (bundle->getFileSpecCount() < 2) {
2083 fprintf(stderr, "NOTE: nothing to do\n");
2084 goto bail;
2085 }
2086
2087 zip = openReadWrite(zipFileName, true);
2088 if (zip == NULL) {
2089 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2090 goto bail;
2091 }
2092
2093 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2094 const char* fileName = bundle->getFileSpecEntry(i);
2095
2096 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
2097 printf(" '%s'... (from gzip)\n", fileName);
2098 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
2099 } else {
2100 if (bundle->getJunkPath()) {
2101 String8 storageName = String8(fileName).getPathLeaf();
Maurice Chu2675f762013-10-22 17:33:11 -07002102 printf(" '%s' as '%s'...\n", fileName,
2103 ResTable::normalizeForOutput(storageName.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08002104 result = zip->add(fileName, storageName.string(),
2105 bundle->getCompressionMethod(), NULL);
2106 } else {
2107 printf(" '%s'...\n", fileName);
2108 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2109 }
2110 }
2111 if (result != NO_ERROR) {
2112 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2113 if (result == NAME_NOT_FOUND) {
2114 fprintf(stderr, ": file not found\n");
2115 } else if (result == ALREADY_EXISTS) {
2116 fprintf(stderr, ": already exists in archive\n");
2117 } else {
2118 fprintf(stderr, "\n");
2119 }
2120 goto bail;
2121 }
2122 }
2123
2124 result = NO_ERROR;
2125
2126bail:
2127 delete zip;
2128 return (result != NO_ERROR);
2129}
2130
2131
2132/*
2133 * Delete files from an existing archive.
2134 */
2135int doRemove(Bundle* bundle)
2136{
2137 ZipFile* zip = NULL;
2138 status_t result = UNKNOWN_ERROR;
2139 const char* zipFileName;
2140
2141 if (bundle->getFileSpecCount() < 1) {
2142 fprintf(stderr, "ERROR: must specify zip file name\n");
2143 goto bail;
2144 }
2145 zipFileName = bundle->getFileSpecEntry(0);
2146
2147 if (bundle->getFileSpecCount() < 2) {
2148 fprintf(stderr, "NOTE: nothing to do\n");
2149 goto bail;
2150 }
2151
2152 zip = openReadWrite(zipFileName, false);
2153 if (zip == NULL) {
2154 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2155 zipFileName);
2156 goto bail;
2157 }
2158
2159 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2160 const char* fileName = bundle->getFileSpecEntry(i);
2161 ZipEntry* entry;
2162
2163 entry = zip->getEntryByName(fileName);
2164 if (entry == NULL) {
2165 printf(" '%s' NOT FOUND\n", fileName);
2166 continue;
2167 }
2168
2169 result = zip->remove(entry);
2170
2171 if (result != NO_ERROR) {
2172 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2173 bundle->getFileSpecEntry(i), zipFileName);
2174 goto bail;
2175 }
2176 }
2177
2178 /* update the archive */
2179 zip->flush();
2180
2181bail:
2182 delete zip;
2183 return (result != NO_ERROR);
2184}
2185
Adam Lesinski3921e872014-05-13 10:56:25 -07002186static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002187 const size_t numDirs = dir->getDirs().size();
2188 for (size_t i = 0; i < numDirs; i++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002189 bool ignore = ignoreConfig;
2190 const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2191 const char* dirStr = subDir->getLeaf().string();
2192 if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2193 ignore = true;
2194 }
2195 status_t err = addResourcesToBuilder(subDir, builder, ignore);
Adam Lesinskifab50872014-04-16 14:40:42 -07002196 if (err != NO_ERROR) {
2197 return err;
2198 }
2199 }
2200
2201 const size_t numFiles = dir->getFiles().size();
2202 for (size_t i = 0; i < numFiles; i++) {
2203 sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2204 const size_t numConfigs = gp->getFiles().size();
2205 for (size_t j = 0; j < numConfigs; j++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002206 status_t err = NO_ERROR;
2207 if (ignoreConfig) {
2208 err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2209 } else {
2210 err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2211 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002212 if (err != NO_ERROR) {
2213 fprintf(stderr, "Failed to add %s (%s) to builder.\n",
2214 gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
2215 return err;
2216 }
2217 }
2218 }
2219 return NO_ERROR;
2220}
2221
2222static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2223 if (split->isBase()) {
2224 return original;
2225 }
2226
2227 String8 ext(original.getPathExtension());
2228 if (ext == String8(".apk")) {
2229 return String8::format("%s_%s%s",
2230 original.getBasePath().string(),
2231 split->getDirectorySafeName().string(),
2232 ext.string());
2233 }
2234
2235 return String8::format("%s_%s", original.string(),
2236 split->getDirectorySafeName().string());
2237}
Adam Lesinski282e1812014-01-23 18:17:42 -08002238
2239/*
2240 * Package up an asset directory and associated application files.
2241 */
2242int doPackage(Bundle* bundle)
2243{
2244 const char* outputAPKFile;
2245 int retVal = 1;
2246 status_t err;
2247 sp<AaptAssets> assets;
2248 int N;
2249 FILE* fp;
2250 String8 dependencyFile;
Adam Lesinskifab50872014-04-16 14:40:42 -07002251 sp<ApkBuilder> builder;
Adam Lesinski282e1812014-01-23 18:17:42 -08002252
Anton Krumina2ef5c02014-03-12 14:46:44 -07002253 // -c en_XA or/and ar_XB means do pseudolocalization
Adam Lesinskifab50872014-04-16 14:40:42 -07002254 sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2255 err = configFilter->parse(bundle->getConfigurations());
Adam Lesinski282e1812014-01-23 18:17:42 -08002256 if (err != NO_ERROR) {
2257 goto bail;
2258 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002259 if (configFilter->containsPseudo()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002260 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2261 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002262 if (configFilter->containsPseudoBidi()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002263 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
Adam Lesinski282e1812014-01-23 18:17:42 -08002264 }
2265
2266 N = bundle->getFileSpecCount();
2267 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
Adam Lesinski09384302014-01-22 16:07:42 -08002268 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002269 fprintf(stderr, "ERROR: no input files\n");
2270 goto bail;
2271 }
2272
2273 outputAPKFile = bundle->getOutputAPKFile();
2274
2275 // Make sure the filenames provided exist and are of the appropriate type.
2276 if (outputAPKFile) {
2277 FileType type;
2278 type = getFileType(outputAPKFile);
2279 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2280 fprintf(stderr,
2281 "ERROR: output file '%s' exists but is not regular file\n",
2282 outputAPKFile);
2283 goto bail;
2284 }
2285 }
2286
2287 // Load the assets.
2288 assets = new AaptAssets();
2289
2290 // Set up the resource gathering in assets if we're going to generate
2291 // dependency files. Every time we encounter a resource while slurping
2292 // the tree, we'll add it to these stores so we have full resource paths
2293 // to write to a dependency file.
2294 if (bundle->getGenDependencies()) {
2295 sp<FilePathStore> resPathStore = new FilePathStore;
2296 assets->setFullResPaths(resPathStore);
2297 sp<FilePathStore> assetPathStore = new FilePathStore;
2298 assets->setFullAssetPaths(assetPathStore);
2299 }
2300
2301 err = assets->slurpFromArgs(bundle);
2302 if (err < 0) {
2303 goto bail;
2304 }
2305
2306 if (bundle->getVerbose()) {
2307 assets->print(String8());
2308 }
2309
Adam Lesinskifab50872014-04-16 14:40:42 -07002310 // Create the ApkBuilder, which will collect the compiled files
2311 // to write to the final APK (or sets of APKs if we are building
2312 // a Split APK.
2313 builder = new ApkBuilder(configFilter);
2314
2315 // If we are generating a Split APK, find out which configurations to split on.
2316 if (bundle->getSplitConfigurations().size() > 0) {
2317 const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2318 const size_t numSplits = splitStrs.size();
2319 for (size_t i = 0; i < numSplits; i++) {
2320 std::set<ConfigDescription> configs;
2321 if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2322 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2323 goto bail;
2324 }
2325
2326 err = builder->createSplitForConfigs(configs);
2327 if (err != NO_ERROR) {
2328 goto bail;
2329 }
2330 }
2331 }
2332
Adam Lesinski282e1812014-01-23 18:17:42 -08002333 // If they asked for any fileAs that need to be compiled, do so.
2334 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002335 err = buildResources(bundle, assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002336 if (err != 0) {
2337 goto bail;
2338 }
2339 }
2340
2341 // At this point we've read everything and processed everything. From here
2342 // on out it's just writing output files.
2343 if (SourcePos::hasErrors()) {
2344 goto bail;
2345 }
2346
2347 // Update symbols with information about which ones are needed as Java symbols.
2348 assets->applyJavaSymbols();
2349 if (SourcePos::hasErrors()) {
2350 goto bail;
2351 }
2352
2353 // If we've been asked to generate a dependency file, do that here
2354 if (bundle->getGenDependencies()) {
2355 // If this is the packaging step, generate the dependency file next to
2356 // the output apk (e.g. bin/resources.ap_.d)
2357 if (outputAPKFile) {
2358 dependencyFile = String8(outputAPKFile);
2359 // Add the .d extension to the dependency file.
2360 dependencyFile.append(".d");
2361 } else {
2362 // Else if this is the R.java dependency generation step,
2363 // generate the dependency file in the R.java package subdirectory
2364 // e.g. gen/com/foo/app/R.java.d
2365 dependencyFile = String8(bundle->getRClassDir());
2366 dependencyFile.appendPath("R.java.d");
2367 }
2368 // Make sure we have a clean dependency file to start with
2369 fp = fopen(dependencyFile, "w");
2370 fclose(fp);
2371 }
2372
2373 // Write out R.java constants
2374 if (!assets->havePrivateSymbols()) {
2375 if (bundle->getCustomPackage() == NULL) {
2376 // Write the R.java file into the appropriate class directory
2377 // e.g. gen/com/foo/app/R.java
Adam Lesinski1e4663852014-08-15 14:47:28 -07002378 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true,
2379 bundle->getBuildSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002380 } else {
2381 const String8 customPkg(bundle->getCustomPackage());
Adam Lesinski1e4663852014-08-15 14:47:28 -07002382 err = writeResourceSymbols(bundle, assets, customPkg, true,
2383 bundle->getBuildSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002384 }
2385 if (err < 0) {
2386 goto bail;
2387 }
2388 // If we have library files, we're going to write our R.java file into
2389 // the appropriate class directory for those libraries as well.
2390 // e.g. gen/com/foo/app/lib/R.java
2391 if (bundle->getExtraPackages() != NULL) {
2392 // Split on colon
2393 String8 libs(bundle->getExtraPackages());
2394 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2395 while (packageString != NULL) {
2396 // Write the R.java file out with the correct package name
Marcin Kosiba0f3a5a62014-09-11 13:48:48 +01002397 err = writeResourceSymbols(bundle, assets, String8(packageString), true,
2398 bundle->getBuildSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002399 if (err < 0) {
2400 goto bail;
2401 }
2402 packageString = strtok(NULL, ":");
2403 }
2404 libs.unlockBuffer();
2405 }
2406 } else {
Adam Lesinski1e4663852014-08-15 14:47:28 -07002407 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002408 if (err < 0) {
2409 goto bail;
2410 }
Adam Lesinski1e4663852014-08-15 14:47:28 -07002411 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002412 if (err < 0) {
2413 goto bail;
2414 }
2415 }
2416
2417 // Write out the ProGuard file
2418 err = writeProguardFile(bundle, assets);
2419 if (err < 0) {
2420 goto bail;
2421 }
2422
2423 // Write the apk
2424 if (outputAPKFile) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002425 // Gather all resources and add them to the APK Builder. The builder will then
2426 // figure out which Split they belong in.
2427 err = addResourcesToBuilder(assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002428 if (err != NO_ERROR) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002429 goto bail;
2430 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002431
2432 const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2433 const size_t numSplits = splits.size();
2434 for (size_t i = 0; i < numSplits; i++) {
2435 const sp<ApkSplit>& split = splits[i];
2436 String8 outputPath = buildApkName(String8(outputAPKFile), split);
2437 err = writeAPK(bundle, outputPath, split);
2438 if (err != NO_ERROR) {
2439 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2440 goto bail;
2441 }
2442 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002443 }
2444
2445 // If we've been asked to generate a dependency file, we need to finish up here.
2446 // the writeResourceSymbols and writeAPK functions have already written the target
2447 // half of the dependency file, now we need to write the prerequisites. (files that
2448 // the R.java file or .ap_ file depend on)
2449 if (bundle->getGenDependencies()) {
2450 // Now that writeResourceSymbols or writeAPK has taken care of writing
2451 // the targets to our dependency file, we'll write the prereqs
2452 fp = fopen(dependencyFile, "a+");
2453 fprintf(fp, " : ");
2454 bool includeRaw = (outputAPKFile != NULL);
2455 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2456 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2457 // and therefore was not added to our pathstores during slurping
2458 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2459 fclose(fp);
2460 }
2461
2462 retVal = 0;
2463bail:
2464 if (SourcePos::hasErrors()) {
2465 SourcePos::printErrors(stderr);
2466 }
2467 return retVal;
2468}
2469
2470/*
2471 * Do PNG Crunching
2472 * PRECONDITIONS
2473 * -S flag points to a source directory containing drawable* folders
2474 * -C flag points to destination directory. The folder structure in the
2475 * source directory will be mirrored to the destination (cache) directory
2476 *
2477 * POSTCONDITIONS
2478 * Destination directory will be updated to match the PNG files in
Maurice Chu2675f762013-10-22 17:33:11 -07002479 * the source directory.
Adam Lesinski282e1812014-01-23 18:17:42 -08002480 */
2481int doCrunch(Bundle* bundle)
2482{
2483 fprintf(stdout, "Crunching PNG Files in ");
2484 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2485 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2486
2487 updatePreProcessedCache(bundle);
2488
2489 return NO_ERROR;
2490}
2491
2492/*
2493 * Do PNG Crunching on a single flag
2494 * -i points to a single png file
2495 * -o points to a single png output file
2496 */
2497int doSingleCrunch(Bundle* bundle)
2498{
2499 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2500 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2501
2502 String8 input(bundle->getSingleCrunchInputFile());
2503 String8 output(bundle->getSingleCrunchOutputFile());
2504
2505 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2506 // we can't return the status_t as it gets truncate to the lower 8 bits.
2507 return 42;
2508 }
2509
2510 return NO_ERROR;
2511}
2512
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002513int runInDaemonMode(Bundle* bundle) {
2514 std::cout << "Ready" << std::endl;
2515 for (std::string line; std::getline(std::cin, line);) {
2516 if (line == "quit") {
2517 return NO_ERROR;
2518 }
2519 std::stringstream ss;
2520 ss << line;
2521 std::string s;
2522
2523 std::string command, parameterOne, parameterTwo;
2524 std::getline(ss, command, ' ');
2525 std::getline(ss, parameterOne, ' ');
2526 std::getline(ss, parameterTwo, ' ');
2527 if (command[0] == 's') {
2528 bundle->setSingleCrunchInputFile(parameterOne.c_str());
2529 bundle->setSingleCrunchOutputFile(parameterTwo.c_str());
2530 std::cout << "Crunching " << parameterOne << std::endl;
2531 if (doSingleCrunch(bundle) != NO_ERROR) {
2532 std::cout << "Error" << std::endl;
2533 }
2534 std::cout << "Done" << std::endl;
2535 } else {
2536 // in case of invalid command, just bail out.
2537 std::cerr << "Unknown command" << std::endl;
2538 return -1;
2539 }
2540 }
2541 return -1;
2542}
2543
Adam Lesinski282e1812014-01-23 18:17:42 -08002544char CONSOLE_DATA[2925] = {
2545 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2546 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2547 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2548 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2549 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2550 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2551 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2552 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2553 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2554 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2555 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2556 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2557 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2558 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2559 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2560 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2561 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2562 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2563 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2564 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2565 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2566 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2567 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2568 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2569 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2570 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2571 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2572 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2573 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2574 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2575 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2576 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2577 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2578 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2579 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2580 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2581 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2582 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2583 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2584 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2585 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2586 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2587 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2588 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2589 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2590 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2591 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2592 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2593 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2594 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2595 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2596 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2597 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2598 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2599 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2600 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2601 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2602 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2603 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2604 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2605 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2606 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2607 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2608 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2609 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2610 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2611 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2612 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2613 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2614 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2615 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2616 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2617 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2618 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2619 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2620 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2621 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2622 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2623 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2624 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2625 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2626 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2627 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2628 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2629 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2630 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2631 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2632 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2633 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2634 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2635 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2636 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2637 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2638 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2639 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2640 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2641 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2642 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2643 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2644 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2645 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2646 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2647 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2648 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2649 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2650 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2651 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2652 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2653 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2654 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2655 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2656 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2657 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2658 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2659 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2660 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2661 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2662 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2663 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2664 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2665 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2666 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2667 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2668 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2669 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2670 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2671 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2672 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2673 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2674 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2675 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2676 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2677 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2678 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2679 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2680 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2681 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2682 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2683 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2684 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2685 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2686 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2687 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2688 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2689 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2690 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2691 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2692 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2693 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2694 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2695 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2696 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2697 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2698 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2699 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2700 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2701 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2702 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2703 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2704 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2705 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2706 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2707 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2708 };