blob: fd660bb6528003766c2c5ea75b5eac8fd1ddea74 [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
26using namespace android;
27
Adam Lesinskiad751222014-08-18 14:06:38 -070028#ifndef AAPT_VERSION
29 #define AAPT_VERSION ""
30#endif
31
Adam Lesinski282e1812014-01-23 18:17:42 -080032/*
33 * Show version info. All the cool kids do it.
34 */
35int doVersion(Bundle* bundle)
36{
37 if (bundle->getFileSpecCount() != 0) {
38 printf("(ignoring extra arguments)\n");
39 }
Adam Lesinskiad751222014-08-18 14:06:38 -070040 printf("Android Asset Packaging Tool, v0.2-" AAPT_VERSION "\n");
Adam Lesinski282e1812014-01-23 18:17:42 -080041
42 return 0;
43}
44
45
46/*
47 * Open the file read only. The call fails if the file doesn't exist.
48 *
49 * Returns NULL on failure.
50 */
51ZipFile* openReadOnly(const char* fileName)
52{
53 ZipFile* zip;
54 status_t result;
55
56 zip = new ZipFile;
57 result = zip->open(fileName, ZipFile::kOpenReadOnly);
58 if (result != NO_ERROR) {
59 if (result == NAME_NOT_FOUND) {
60 fprintf(stderr, "ERROR: '%s' not found\n", fileName);
61 } else if (result == PERMISSION_DENIED) {
62 fprintf(stderr, "ERROR: '%s' access denied\n", fileName);
63 } else {
64 fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n",
65 fileName);
66 }
67 delete zip;
68 return NULL;
69 }
70
71 return zip;
72}
73
74/*
75 * Open the file read-write. The file will be created if it doesn't
76 * already exist and "okayToCreate" is set.
77 *
78 * Returns NULL on failure.
79 */
80ZipFile* openReadWrite(const char* fileName, bool okayToCreate)
81{
82 ZipFile* zip = NULL;
83 status_t result;
84 int flags;
85
86 flags = ZipFile::kOpenReadWrite;
87 if (okayToCreate) {
88 flags |= ZipFile::kOpenCreate;
89 }
90
91 zip = new ZipFile;
92 result = zip->open(fileName, flags);
93 if (result != NO_ERROR) {
94 delete zip;
95 zip = NULL;
96 goto bail;
97 }
98
99bail:
100 return zip;
101}
102
103
104/*
105 * Return a short string describing the compression method.
106 */
107const char* compressionName(int method)
108{
109 if (method == ZipEntry::kCompressStored) {
110 return "Stored";
111 } else if (method == ZipEntry::kCompressDeflated) {
112 return "Deflated";
113 } else {
114 return "Unknown";
115 }
116}
117
118/*
119 * Return the percent reduction in size (0% == no compression).
120 */
121int calcPercent(long uncompressedLen, long compressedLen)
122{
123 if (!uncompressedLen) {
124 return 0;
125 } else {
126 return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5);
127 }
128}
129
130/*
131 * Handle the "list" command, which can be a simple file dump or
132 * a verbose listing.
133 *
134 * The verbose listing closely matches the output of the Info-ZIP "unzip"
135 * command.
136 */
137int doList(Bundle* bundle)
138{
139 int result = 1;
140 ZipFile* zip = NULL;
141 const ZipEntry* entry;
142 long totalUncLen, totalCompLen;
143 const char* zipFileName;
144
145 if (bundle->getFileSpecCount() != 1) {
146 fprintf(stderr, "ERROR: specify zip file name (only)\n");
147 goto bail;
148 }
149 zipFileName = bundle->getFileSpecEntry(0);
150
151 zip = openReadOnly(zipFileName);
152 if (zip == NULL) {
153 goto bail;
154 }
155
156 int count, i;
157
158 if (bundle->getVerbose()) {
159 printf("Archive: %s\n", zipFileName);
160 printf(
161 " Length Method Size Ratio Offset Date Time CRC-32 Name\n");
162 printf(
163 "-------- ------ ------- ----- ------- ---- ---- ------ ----\n");
164 }
165
166 totalUncLen = totalCompLen = 0;
167
168 count = zip->getNumEntries();
169 for (i = 0; i < count; i++) {
170 entry = zip->getEntryByIndex(i);
171 if (bundle->getVerbose()) {
172 char dateBuf[32];
173 time_t when;
174
175 when = entry->getModWhen();
176 strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M",
177 localtime(&when));
178
179 printf("%8ld %-7.7s %7ld %3d%% %8zd %s %08lx %s\n",
180 (long) entry->getUncompressedLen(),
181 compressionName(entry->getCompressionMethod()),
182 (long) entry->getCompressedLen(),
183 calcPercent(entry->getUncompressedLen(),
184 entry->getCompressedLen()),
185 (size_t) entry->getLFHOffset(),
186 dateBuf,
187 entry->getCRC32(),
188 entry->getFileName());
189 } else {
190 printf("%s\n", entry->getFileName());
191 }
192
193 totalUncLen += entry->getUncompressedLen();
194 totalCompLen += entry->getCompressedLen();
195 }
196
197 if (bundle->getVerbose()) {
198 printf(
199 "-------- ------- --- -------\n");
200 printf("%8ld %7ld %2d%% %d files\n",
201 totalUncLen,
202 totalCompLen,
203 calcPercent(totalUncLen, totalCompLen),
204 zip->getNumEntries());
205 }
206
207 if (bundle->getAndroidList()) {
208 AssetManager assets;
209 if (!assets.addAssetPath(String8(zipFileName), NULL)) {
210 fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n");
211 goto bail;
212 }
213
214 const ResTable& res = assets.getResources(false);
215 if (&res == NULL) {
216 printf("\nNo resource table found.\n");
217 } else {
218#ifndef HAVE_ANDROID_OS
219 printf("\nResource table:\n");
220 res.print(false);
221#endif
222 }
223
224 Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
225 Asset::ACCESS_BUFFER);
226 if (manifestAsset == NULL) {
227 printf("\nNo AndroidManifest.xml found.\n");
228 } else {
229 printf("\nAndroid manifest:\n");
230 ResXMLTree tree;
231 tree.setTo(manifestAsset->getBuffer(true),
232 manifestAsset->getLength());
233 printXMLBlock(&tree);
234 }
235 delete manifestAsset;
236 }
237
238 result = 0;
239
240bail:
241 delete zip;
242 return result;
243}
244
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700245static void printResolvedResourceAttribute(const ResTable& resTable, const ResXMLTree& tree,
Maurice Chu76327312013-10-16 18:28:46 -0700246 uint32_t attrRes, String8 attrLabel, String8* outError)
247{
248 Res_value value;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700249 AaptXml::getResolvedResourceAttribute(resTable, tree, attrRes, &value, outError);
Maurice Chu76327312013-10-16 18:28:46 -0700250 if (*outError != "") {
251 *outError = "error print resolved resource attribute";
252 return;
253 }
254 if (value.dataType == Res_value::TYPE_STRING) {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700255 String8 result = AaptXml::getResolvedAttribute(resTable, tree, attrRes, outError);
Maurice Chu2675f762013-10-22 17:33:11 -0700256 printf("%s='%s'", attrLabel.string(),
257 ResTable::normalizeForOutput(result.string()).string());
Maurice Chu76327312013-10-16 18:28:46 -0700258 } else if (Res_value::TYPE_FIRST_INT <= value.dataType &&
259 value.dataType <= Res_value::TYPE_LAST_INT) {
260 printf("%s='%d'", attrLabel.string(), value.data);
261 } else {
262 printf("%s='0x%x'", attrLabel.string(), (int)value.data);
263 }
264}
265
Adam Lesinski282e1812014-01-23 18:17:42 -0800266// These are attribute resource constants for the platform, as found
267// in android.R.attr
268enum {
269 LABEL_ATTR = 0x01010001,
270 ICON_ATTR = 0x01010002,
271 NAME_ATTR = 0x01010003,
Adam Lesinskia5018c92013-09-30 16:23:15 -0700272 PERMISSION_ATTR = 0x01010006,
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700273 EXPORTED_ATTR = 0x01010010,
274 GRANT_URI_PERMISSIONS_ATTR = 0x0101001b,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700275 RESOURCE_ATTR = 0x01010025,
Adam Lesinski282e1812014-01-23 18:17:42 -0800276 DEBUGGABLE_ATTR = 0x0101000f,
277 VALUE_ATTR = 0x01010024,
278 VERSION_CODE_ATTR = 0x0101021b,
279 VERSION_NAME_ATTR = 0x0101021c,
280 SCREEN_ORIENTATION_ATTR = 0x0101001e,
281 MIN_SDK_VERSION_ATTR = 0x0101020c,
282 MAX_SDK_VERSION_ATTR = 0x01010271,
283 REQ_TOUCH_SCREEN_ATTR = 0x01010227,
284 REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
285 REQ_HARD_KEYBOARD_ATTR = 0x01010229,
286 REQ_NAVIGATION_ATTR = 0x0101022a,
287 REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
288 TARGET_SDK_VERSION_ATTR = 0x01010270,
289 TEST_ONLY_ATTR = 0x01010272,
290 ANY_DENSITY_ATTR = 0x0101026c,
291 GL_ES_VERSION_ATTR = 0x01010281,
292 SMALL_SCREEN_ATTR = 0x01010284,
293 NORMAL_SCREEN_ATTR = 0x01010285,
294 LARGE_SCREEN_ATTR = 0x01010286,
295 XLARGE_SCREEN_ATTR = 0x010102bf,
296 REQUIRED_ATTR = 0x0101028e,
Adam Lesinskicaf797c2014-08-22 12:56:26 -0700297 INSTALL_LOCATION_ATTR = 0x010102b7,
Adam Lesinski282e1812014-01-23 18:17:42 -0800298 SCREEN_SIZE_ATTR = 0x010102ca,
299 SCREEN_DENSITY_ATTR = 0x010102cb,
300 REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
301 COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
302 LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
303 PUBLIC_KEY_ATTR = 0x010103a6,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700304 CATEGORY_ATTR = 0x010103e8,
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800305 BANNER_ATTR = 0x10103f2,
Adam Lesinski282e1812014-01-23 18:17:42 -0800306};
307
Maurice Chu2675f762013-10-22 17:33:11 -0700308String8 getComponentName(String8 &pkgName, String8 &componentName) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800309 ssize_t idx = componentName.find(".");
310 String8 retStr(pkgName);
311 if (idx == 0) {
312 retStr += componentName;
313 } else if (idx < 0) {
314 retStr += ".";
315 retStr += componentName;
316 } else {
Maurice Chu2675f762013-10-22 17:33:11 -0700317 return componentName;
Adam Lesinski282e1812014-01-23 18:17:42 -0800318 }
Maurice Chu2675f762013-10-22 17:33:11 -0700319 return retStr;
Adam Lesinski282e1812014-01-23 18:17:42 -0800320}
321
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700322static void printCompatibleScreens(ResXMLTree& tree, String8* outError) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800323 size_t len;
324 ResXMLTree::event_code_t code;
325 int depth = 0;
326 bool first = true;
327 printf("compatible-screens:");
328 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
329 if (code == ResXMLTree::END_TAG) {
330 depth--;
331 if (depth < 0) {
332 break;
333 }
334 continue;
335 }
336 if (code != ResXMLTree::START_TAG) {
337 continue;
338 }
339 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700340 const char16_t* ctag16 = tree.getElementName(&len);
341 if (ctag16 == NULL) {
342 *outError = "failed to get XML element name (bad string pool)";
343 return;
344 }
345 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800346 if (tag == "screen") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700347 int32_t screenSize = AaptXml::getIntegerAttribute(tree,
348 SCREEN_SIZE_ATTR);
349 int32_t screenDensity = AaptXml::getIntegerAttribute(tree,
350 SCREEN_DENSITY_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -0800351 if (screenSize > 0 && screenDensity > 0) {
352 if (!first) {
353 printf(",");
354 }
355 first = false;
356 printf("'%d/%d'", screenSize, screenDensity);
357 }
358 }
359 }
360 printf("\n");
361}
362
Adam Lesinski58f1f362013-11-12 12:59:08 -0800363static void printUsesPermission(const String8& name, bool optional=false, int maxSdkVersion=-1) {
364 printf("uses-permission: name='%s'", ResTable::normalizeForOutput(name.string()).string());
365 if (maxSdkVersion != -1) {
366 printf(" maxSdkVersion='%d'", maxSdkVersion);
367 }
368 printf("\n");
369
370 if (optional) {
371 printf("optional-permission: name='%s'",
372 ResTable::normalizeForOutput(name.string()).string());
373 if (maxSdkVersion != -1) {
374 printf(" maxSdkVersion='%d'", maxSdkVersion);
375 }
376 printf("\n");
377 }
378}
379
380static void printUsesImpliedPermission(const String8& name, const String8& reason) {
381 printf("uses-implied-permission: name='%s' reason='%s'\n",
382 ResTable::normalizeForOutput(name.string()).string(),
383 ResTable::normalizeForOutput(reason.string()).string());
384}
385
Adam Lesinski94fc9122013-09-30 17:16:09 -0700386Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool offHost,
387 String8 *outError = NULL)
388{
389 Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER);
390 if (aidAsset == NULL) {
391 if (outError != NULL) *outError = "xml resource does not exist";
392 return Vector<String8>();
393 }
394
395 const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service");
396
397 bool withinApduService = false;
398 Vector<String8> categories;
399
400 String8 error;
401 ResXMLTree tree;
402 tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength());
403
404 size_t len;
405 int depth = 0;
406 ResXMLTree::event_code_t code;
407 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
408 if (code == ResXMLTree::END_TAG) {
409 depth--;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700410 const char16_t* ctag16 = tree.getElementName(&len);
411 if (ctag16 == NULL) {
412 *outError = "failed to get XML element name (bad string pool)";
413 return Vector<String8>();
414 }
415 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700416
417 if (depth == 0 && tag == serviceTagName) {
418 withinApduService = false;
419 }
420
421 } else if (code == ResXMLTree::START_TAG) {
422 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700423 const char16_t* ctag16 = tree.getElementName(&len);
424 if (ctag16 == NULL) {
425 *outError = "failed to get XML element name (bad string pool)";
426 return Vector<String8>();
427 }
428 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700429
430 if (depth == 1) {
431 if (tag == serviceTagName) {
432 withinApduService = true;
433 }
434 } else if (depth == 2 && withinApduService) {
435 if (tag == "aid-group") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700436 String8 category = AaptXml::getAttribute(tree, CATEGORY_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700437 if (error != "") {
438 if (outError != NULL) *outError = error;
439 return Vector<String8>();
440 }
441
442 categories.add(category);
443 }
444 }
445 }
446 }
447 aidAsset->close();
448 return categories;
449}
450
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700451static void printComponentPresence(const char* componentName) {
452 printf("provides-component:'%s'\n", componentName);
453}
454
Adam Lesinski2c72b682014-06-24 09:56:01 -0700455/**
456 * Represents a feature that has been automatically added due to
457 * a pre-requisite or some other reason.
458 */
459struct ImpliedFeature {
460 /**
461 * Name of the implied feature.
462 */
463 String8 name;
464
465 /**
466 * List of human-readable reasons for why this feature was implied.
467 */
468 SortedVector<String8> reasons;
469};
470
471/**
472 * Represents a <feature-group> tag in the AndroidManifest.xml
473 */
474struct FeatureGroup {
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700475 FeatureGroup() : openGLESVersion(-1) {}
476
Adam Lesinski2c72b682014-06-24 09:56:01 -0700477 /**
478 * Human readable label
479 */
480 String8 label;
481
482 /**
483 * Explicit features defined in the group
484 */
485 KeyedVector<String8, bool> features;
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700486
487 /**
488 * OpenGL ES version required
489 */
490 int openGLESVersion;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700491};
492
493static void addImpliedFeature(KeyedVector<String8, ImpliedFeature>* impliedFeatures,
494 const char* name, const char* reason) {
495 String8 name8(name);
496 ssize_t idx = impliedFeatures->indexOfKey(name8);
497 if (idx < 0) {
498 idx = impliedFeatures->add(name8, ImpliedFeature());
499 impliedFeatures->editValueAt(idx).name = name8;
500 }
501 impliedFeatures->editValueAt(idx).reasons.add(String8(reason));
502}
503
504static void printFeatureGroup(const FeatureGroup& grp,
505 const KeyedVector<String8, ImpliedFeature>* impliedFeatures = NULL) {
506 printf("feature-group: label='%s'\n", grp.label.string());
507
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700508 if (grp.openGLESVersion > 0) {
509 printf(" uses-gl-es: '0x%x'\n", grp.openGLESVersion);
510 }
511
Adam Lesinski2c72b682014-06-24 09:56:01 -0700512 const size_t numFeatures = grp.features.size();
513 for (size_t i = 0; i < numFeatures; i++) {
514 if (!grp.features[i]) {
515 continue;
516 }
517
518 const String8& featureName = grp.features.keyAt(i);
519 printf(" uses-feature: name='%s'\n",
520 ResTable::normalizeForOutput(featureName.string()).string());
521 }
522
523 const size_t numImpliedFeatures =
524 (impliedFeatures != NULL) ? impliedFeatures->size() : 0;
525 for (size_t i = 0; i < numImpliedFeatures; i++) {
526 const ImpliedFeature& impliedFeature = impliedFeatures->valueAt(i);
527 if (grp.features.indexOfKey(impliedFeature.name) >= 0) {
528 // The feature is explicitly set, no need to use implied
529 // definition.
530 continue;
531 }
532
533 String8 printableFeatureName(ResTable::normalizeForOutput(
534 impliedFeature.name.string()));
535 printf(" uses-feature: name='%s'\n", printableFeatureName.string());
536 printf(" uses-implied-feature: name='%s' reason='",
537 printableFeatureName.string());
538 const size_t numReasons = impliedFeature.reasons.size();
539 for (size_t j = 0; j < numReasons; j++) {
540 printf("%s", impliedFeature.reasons[j].string());
541 if (j + 2 < numReasons) {
542 printf(", ");
543 } else if (j + 1 < numReasons) {
544 printf(", and ");
545 }
546 }
547 printf("'\n");
548 }
549}
550
551static void addParentFeatures(FeatureGroup* grp, const String8& name) {
552 if (name == "android.hardware.camera.autofocus" ||
553 name == "android.hardware.camera.flash") {
554 grp->features.add(String8("android.hardware.camera"), true);
555 } else if (name == "android.hardware.location.gps" ||
556 name == "android.hardware.location.network") {
557 grp->features.add(String8("android.hardware.location"), true);
558 } else if (name == "android.hardware.touchscreen.multitouch") {
559 grp->features.add(String8("android.hardware.touchscreen"), true);
560 } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
561 grp->features.add(String8("android.hardware.touchscreen.multitouch"), true);
562 grp->features.add(String8("android.hardware.touchscreen"), true);
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700563 } else if (name == "android.hardware.opengles.aep") {
564 const int openGLESVersion31 = 0x00030001;
565 if (openGLESVersion31 > grp->openGLESVersion) {
566 grp->openGLESVersion = openGLESVersion31;
567 }
Adam Lesinski2c72b682014-06-24 09:56:01 -0700568 }
569}
570
Adam Lesinski282e1812014-01-23 18:17:42 -0800571/*
572 * Handle the "dump" command, to extract select data from an archive.
573 */
574extern char CONSOLE_DATA[2925]; // see EOF
575int doDump(Bundle* bundle)
576{
577 status_t result = UNKNOWN_ERROR;
Adam Lesinski282e1812014-01-23 18:17:42 -0800578
579 if (bundle->getFileSpecCount() < 1) {
580 fprintf(stderr, "ERROR: no dump option specified\n");
581 return 1;
582 }
583
584 if (bundle->getFileSpecCount() < 2) {
585 fprintf(stderr, "ERROR: no dump file specified\n");
586 return 1;
587 }
588
589 const char* option = bundle->getFileSpecEntry(0);
590 const char* filename = bundle->getFileSpecEntry(1);
591
592 AssetManager assets;
Narayan Kamathf85e41f2014-01-24 14:14:30 +0000593 int32_t assetsCookie;
Adam Lesinski282e1812014-01-23 18:17:42 -0800594 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
595 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
596 return 1;
597 }
598
599 // Make a dummy config for retrieving resources... we need to supply
600 // non-default values for some configs so that we can retrieve resources
601 // in the app that don't have a default. The most important of these is
602 // the API version because key resources like icons will have an implicit
603 // version if they are using newer config types like density.
604 ResTable_config config;
Narayan Kamath91447d82014-01-21 15:32:36 +0000605 memset(&config, 0, sizeof(ResTable_config));
Adam Lesinski282e1812014-01-23 18:17:42 -0800606 config.language[0] = 'e';
607 config.language[1] = 'n';
608 config.country[0] = 'U';
609 config.country[1] = 'S';
610 config.orientation = ResTable_config::ORIENTATION_PORT;
611 config.density = ResTable_config::DENSITY_MEDIUM;
612 config.sdkVersion = 10000; // Very high.
613 config.screenWidthDp = 320;
614 config.screenHeightDp = 480;
615 config.smallestScreenWidthDp = 320;
Adam Lesinskic2dea8d2014-08-04 16:40:41 -0700616 config.screenLayout |= ResTable_config::SCREENSIZE_NORMAL;
Adam Lesinski282e1812014-01-23 18:17:42 -0800617 assets.setConfiguration(config);
618
619 const ResTable& res = assets.getResources(false);
620 if (&res == NULL) {
621 fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
Adam Lesinski63e646e2014-07-30 11:40:39 -0700622 return 1;
Adam Lesinski25e9d552014-05-19 15:01:43 -0700623 } else if (res.getError() != NO_ERROR) {
624 fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
Adam Lesinski63e646e2014-07-30 11:40:39 -0700625 return 1;
Adam Lesinski282e1812014-01-23 18:17:42 -0800626 }
627
Adam Lesinski2cb761e2014-08-15 13:59:02 -0700628 // The dynamicRefTable can be null if there are no resources for this asset cookie.
629 // This fine.
Adam Lesinski63e646e2014-07-30 11:40:39 -0700630 const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700631
632 Asset* asset = NULL;
633
Adam Lesinski282e1812014-01-23 18:17:42 -0800634 if (strcmp("resources", option) == 0) {
635#ifndef HAVE_ANDROID_OS
636 res.print(bundle->getValues());
637#endif
638
639 } else if (strcmp("strings", option) == 0) {
640 const ResStringPool* pool = res.getTableStringBlock(0);
641 printStringPool(pool);
642
643 } else if (strcmp("xmltree", option) == 0) {
644 if (bundle->getFileSpecCount() < 3) {
645 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
646 goto bail;
647 }
648
649 for (int i=2; i<bundle->getFileSpecCount(); i++) {
650 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700651 ResXMLTree tree(dynamicRefTable);
652 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800653 if (asset == NULL) {
654 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
655 goto bail;
656 }
657
658 if (tree.setTo(asset->getBuffer(true),
659 asset->getLength()) != NO_ERROR) {
660 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
661 goto bail;
662 }
663 tree.restart();
664 printXMLBlock(&tree);
665 tree.uninit();
666 delete asset;
667 asset = NULL;
668 }
669
670 } else if (strcmp("xmlstrings", option) == 0) {
671 if (bundle->getFileSpecCount() < 3) {
672 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
673 goto bail;
674 }
675
676 for (int i=2; i<bundle->getFileSpecCount(); i++) {
677 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700678 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800679 if (asset == NULL) {
680 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
681 goto bail;
682 }
683
Adam Lesinski63e646e2014-07-30 11:40:39 -0700684 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800685 if (tree.setTo(asset->getBuffer(true),
686 asset->getLength()) != NO_ERROR) {
687 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
688 goto bail;
689 }
690 printStringPool(&tree.getStrings());
691 delete asset;
692 asset = NULL;
693 }
694
695 } else {
Adam Lesinski63e646e2014-07-30 11:40:39 -0700696 asset = assets.openNonAsset(assetsCookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800697 if (asset == NULL) {
698 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
699 goto bail;
700 }
701
Adam Lesinski63e646e2014-07-30 11:40:39 -0700702 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800703 if (tree.setTo(asset->getBuffer(true),
704 asset->getLength()) != NO_ERROR) {
705 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
706 goto bail;
707 }
708 tree.restart();
709
710 if (strcmp("permissions", option) == 0) {
711 size_t len;
712 ResXMLTree::event_code_t code;
713 int depth = 0;
714 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
715 if (code == ResXMLTree::END_TAG) {
716 depth--;
717 continue;
718 }
719 if (code != ResXMLTree::START_TAG) {
720 continue;
721 }
722 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700723 const char16_t* ctag16 = tree.getElementName(&len);
724 if (ctag16 == NULL) {
725 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
726 goto bail;
727 }
728 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800729 //printf("Depth %d tag %s\n", depth, tag.string());
730 if (depth == 1) {
731 if (tag != "manifest") {
732 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
733 goto bail;
734 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700735 String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -0700736 printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800737 } else if (depth == 2 && tag == "permission") {
738 String8 error;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700739 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -0800740 if (error != "") {
741 fprintf(stderr, "ERROR: %s\n", error.string());
742 goto bail;
743 }
Maurice Chu2675f762013-10-22 17:33:11 -0700744 printf("permission: %s\n",
745 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800746 } else if (depth == 2 && tag == "uses-permission") {
747 String8 error;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700748 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -0800749 if (error != "") {
750 fprintf(stderr, "ERROR: %s\n", error.string());
751 goto bail;
752 }
Adam Lesinski58f1f362013-11-12 12:59:08 -0800753 printUsesPermission(name,
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700754 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
755 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
Adam Lesinski282e1812014-01-23 18:17:42 -0800756 }
757 }
758 } else if (strcmp("badging", option) == 0) {
759 Vector<String8> locales;
760 res.getLocales(&locales);
761
762 Vector<ResTable_config> configs;
763 res.getConfigurations(&configs);
764 SortedVector<int> densities;
765 const size_t NC = configs.size();
766 for (size_t i=0; i<NC; i++) {
767 int dens = configs[i].density;
768 if (dens == 0) {
769 dens = 160;
770 }
771 densities.add(dens);
772 }
773
774 size_t len;
775 ResXMLTree::event_code_t code;
776 int depth = 0;
777 String8 error;
778 bool withinActivity = false;
779 bool isMainActivity = false;
780 bool isLauncherActivity = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800781 bool isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800782 bool isSearchable = false;
783 bool withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700784 bool withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700785 bool withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800786 bool withinReceiver = false;
787 bool withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700788 bool withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800789 bool withinIntentFilter = false;
790 bool hasMainActivity = false;
791 bool hasOtherActivities = false;
792 bool hasOtherReceivers = false;
793 bool hasOtherServices = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700794 bool hasIntentFilter = false;
795
Adam Lesinski282e1812014-01-23 18:17:42 -0800796 bool hasWallpaperService = false;
797 bool hasImeService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700798 bool hasAccessibilityService = false;
799 bool hasPrintService = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800800 bool hasWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700801 bool hasDeviceAdminReceiver = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700802 bool hasPaymentService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700803 bool hasDocumentsProvider = false;
804 bool hasCameraActivity = false;
805 bool hasCameraSecureActivity = false;
806 bool hasLauncher = false;
807 bool hasNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400808 bool hasDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700809
Adam Lesinski282e1812014-01-23 18:17:42 -0800810 bool actMainActivity = false;
811 bool actWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700812 bool actDeviceAdminEnabled = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800813 bool actImeService = false;
814 bool actWallpaperService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700815 bool actAccessibilityService = false;
816 bool actPrintService = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700817 bool actHostApduService = false;
818 bool actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700819 bool actDocumentsProvider = false;
820 bool actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400821 bool actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700822 bool actCamera = false;
823 bool actCameraSecure = false;
824 bool catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700825 bool hasMetaHostPaymentCategory = false;
826 bool hasMetaOffHostPaymentCategory = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700827
828 // These permissions are required by services implementing services
829 // the system binds to (IME, Accessibility, PrintServices, etc.)
830 bool hasBindDeviceAdminPermission = false;
831 bool hasBindInputMethodPermission = false;
832 bool hasBindAccessibilityServicePermission = false;
833 bool hasBindPrintServicePermission = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700834 bool hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700835 bool hasRequiredSafAttributes = false;
836 bool hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400837 bool hasBindDreamServicePermission = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800838
839 // These two implement the implicit permissions that are granted
840 // to pre-1.6 applications.
841 bool hasWriteExternalStoragePermission = false;
842 bool hasReadPhoneStatePermission = false;
843
844 // If an app requests write storage, they will also get read storage.
845 bool hasReadExternalStoragePermission = false;
846
847 // Implement transition to read and write call log.
848 bool hasReadContactsPermission = false;
849 bool hasWriteContactsPermission = false;
850 bool hasReadCallLogPermission = false;
851 bool hasWriteCallLogPermission = false;
852
Adam Lesinskie47fd122014-08-15 22:25:36 -0700853 // If an app declares itself as multiArch, we report the
854 // native libraries differently.
855 bool hasMultiArch = false;
856
Adam Lesinski282e1812014-01-23 18:17:42 -0800857 // This next group of variables is used to implement a group of
858 // backward-compatibility heuristics necessitated by the addition of
859 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
860 // heuristic is "if an app requests a permission but doesn't explicitly
861 // request the corresponding <uses-feature>, presume it's there anyway".
Adam Lesinski2c72b682014-06-24 09:56:01 -0700862
Adam Lesinski282e1812014-01-23 18:17:42 -0800863 // 2.2 also added some other features that apps can request, but that
864 // have no corresponding permission, so we cannot implement any
865 // back-compatibility heuristic for them. The below are thus unnecessary
866 // (but are retained here for documentary purposes.)
867 //bool specCompassFeature = false;
868 //bool specAccelerometerFeature = false;
869 //bool specProximityFeature = false;
870 //bool specAmbientLightFeature = false;
871 //bool specLiveWallpaperFeature = false;
872
873 int targetSdk = 0;
874 int smallScreen = 1;
875 int normalScreen = 1;
876 int largeScreen = 1;
877 int xlargeScreen = 1;
878 int anyDensity = 1;
879 int requiresSmallestWidthDp = 0;
880 int compatibleWidthLimitDp = 0;
881 int largestWidthLimitDp = 0;
882 String8 pkg;
883 String8 activityName;
884 String8 activityLabel;
885 String8 activityIcon;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800886 String8 activityBanner;
Adam Lesinski282e1812014-01-23 18:17:42 -0800887 String8 receiverName;
888 String8 serviceName;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700889 Vector<String8> supportedInput;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700890
891 FeatureGroup commonFeatures;
892 Vector<FeatureGroup> featureGroups;
893 KeyedVector<String8, ImpliedFeature> impliedFeatures;
894
Adam Lesinski282e1812014-01-23 18:17:42 -0800895 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
896 if (code == ResXMLTree::END_TAG) {
897 depth--;
898 if (depth < 2) {
Michael Wrightec4fdec2013-09-06 16:50:52 -0700899 if (withinSupportsInput && !supportedInput.isEmpty()) {
900 printf("supports-input: '");
901 const size_t N = supportedInput.size();
902 for (size_t i=0; i<N; i++) {
Maurice Chu2675f762013-10-22 17:33:11 -0700903 printf("%s", ResTable::normalizeForOutput(
904 supportedInput[i].string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -0700905 if (i != N - 1) {
906 printf("' '");
907 } else {
908 printf("'\n");
909 }
910 }
911 supportedInput.clear();
912 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800913 withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700914 withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700915 withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800916 } else if (depth < 3) {
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800917 if (withinActivity && isMainActivity) {
Maurice Chu2675f762013-10-22 17:33:11 -0700918 String8 aName(getComponentName(pkg, activityName));
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800919 if (isLauncherActivity) {
920 printf("launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800921 if (aName.length() > 0) {
922 printf(" name='%s' ",
923 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800924 }
925 printf(" label='%s' icon='%s'\n",
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800926 ResTable::normalizeForOutput(activityLabel.string()).string(),
927 ResTable::normalizeForOutput(activityIcon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800928 }
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800929 if (isLeanbackLauncherActivity) {
930 printf("leanback-launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800931 if (aName.length() > 0) {
932 printf(" name='%s' ",
933 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800934 }
935 printf(" label='%s' icon='%s' banner='%s'\n",
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800936 ResTable::normalizeForOutput(activityLabel.string()).string(),
937 ResTable::normalizeForOutput(activityIcon.string()).string(),
938 ResTable::normalizeForOutput(activityBanner.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800939 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800940 }
941 if (!hasIntentFilter) {
942 hasOtherActivities |= withinActivity;
943 hasOtherReceivers |= withinReceiver;
944 hasOtherServices |= withinService;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700945 } else {
946 if (withinService) {
947 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
948 hasBindNfcServicePermission);
949 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
950 hasBindNfcServicePermission);
951 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800952 }
953 withinActivity = false;
954 withinService = false;
955 withinReceiver = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700956 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800957 hasIntentFilter = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800958 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800959 } else if (depth < 4) {
960 if (withinIntentFilter) {
961 if (withinActivity) {
962 hasMainActivity |= actMainActivity;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700963 hasLauncher |= catLauncher;
964 hasCameraActivity |= actCamera;
965 hasCameraSecureActivity |= actCameraSecure;
966 hasOtherActivities |= !actMainActivity && !actCamera && !actCameraSecure;
Adam Lesinski282e1812014-01-23 18:17:42 -0800967 } else if (withinReceiver) {
968 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700969 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
970 hasBindDeviceAdminPermission);
971 hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled);
Adam Lesinski282e1812014-01-23 18:17:42 -0800972 } else if (withinService) {
973 hasImeService |= actImeService;
974 hasWallpaperService |= actWallpaperService;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700975 hasAccessibilityService |= (actAccessibilityService &&
976 hasBindAccessibilityServicePermission);
977 hasPrintService |= (actPrintService && hasBindPrintServicePermission);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700978 hasNotificationListenerService |= actNotificationListenerService &&
979 hasBindNotificationListenerServicePermission;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400980 hasDreamService |= actDreamService && hasBindDreamServicePermission;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700981 hasOtherServices |= (!actImeService && !actWallpaperService &&
Adam Lesinski94fc9122013-09-30 17:16:09 -0700982 !actAccessibilityService && !actPrintService &&
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700983 !actHostApduService && !actOffHostApduService &&
984 !actNotificationListenerService);
985 } else if (withinProvider) {
986 hasDocumentsProvider |= actDocumentsProvider && hasRequiredSafAttributes;
Adam Lesinski282e1812014-01-23 18:17:42 -0800987 }
988 }
989 withinIntentFilter = false;
990 }
991 continue;
992 }
993 if (code != ResXMLTree::START_TAG) {
994 continue;
995 }
996 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700997
998 const char16_t* ctag16 = tree.getElementName(&len);
999 if (ctag16 == NULL) {
1000 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
1001 goto bail;
1002 }
1003 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -08001004 //printf("Depth %d, %s\n", depth, tag.string());
1005 if (depth == 1) {
1006 if (tag != "manifest") {
1007 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
1008 goto bail;
1009 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001010 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -07001011 printf("package: name='%s' ",
1012 ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001013 int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR,
1014 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001015 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001016 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n",
1017 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001018 goto bail;
1019 }
1020 if (versionCode > 0) {
1021 printf("versionCode='%d' ", versionCode);
1022 } else {
1023 printf("versionCode='' ");
1024 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001025 String8 versionName = AaptXml::getResolvedAttribute(res, tree,
1026 VERSION_NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001027 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001028 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n",
1029 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001030 goto bail;
1031 }
Adam Lesinski25d35a92014-08-11 09:41:56 -07001032 printf("versionName='%s'",
Maurice Chu2675f762013-10-22 17:33:11 -07001033 ResTable::normalizeForOutput(versionName.string()).string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001034
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001035 String8 splitName = AaptXml::getAttribute(tree, NULL, "split");
Adam Lesinski25d35a92014-08-11 09:41:56 -07001036 if (!splitName.isEmpty()) {
1037 printf(" split='%s'", ResTable::normalizeForOutput(
1038 splitName.string()).string());
1039 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001040
1041 int32_t platformVersionCode = AaptXml::getIntegerAttribute(tree, NULL,
1042 "platformBuildVersionCode");
1043 printf(" platformBuildVersionCode='%d'", platformVersionCode);
Adam Lesinski25d35a92014-08-11 09:41:56 -07001044 printf("\n");
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001045
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001046 int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
1047 INSTALL_LOCATION_ATTR, &error);
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001048 if (error != "") {
1049 fprintf(stderr, "ERROR getting 'android:installLocation' attribute: %s\n",
1050 error.string());
1051 goto bail;
1052 }
1053
1054 if (installLocation >= 0) {
1055 printf("install-location:'");
1056 switch (installLocation) {
1057 case 0:
1058 printf("auto");
1059 break;
1060 case 1:
1061 printf("internalOnly");
1062 break;
1063 case 2:
1064 printf("preferExternal");
1065 break;
1066 default:
1067 fprintf(stderr, "Invalid installLocation %d\n", installLocation);
1068 goto bail;
1069 }
1070 printf("'\n");
1071 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001072 } else if (depth == 2) {
1073 withinApplication = false;
1074 if (tag == "application") {
1075 withinApplication = true;
1076
1077 String8 label;
1078 const size_t NL = locales.size();
1079 for (size_t i=0; i<NL; i++) {
1080 const char* localeStr = locales[i].string();
1081 assets.setLocale(localeStr != NULL ? localeStr : "");
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001082 String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1083 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001084 if (llabel != "") {
1085 if (localeStr == NULL || strlen(localeStr) == 0) {
1086 label = llabel;
Maurice Chu2675f762013-10-22 17:33:11 -07001087 printf("application-label:'%s'\n",
1088 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001089 } else {
1090 if (label == "") {
1091 label = llabel;
1092 }
1093 printf("application-label-%s:'%s'\n", localeStr,
Maurice Chu2675f762013-10-22 17:33:11 -07001094 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001095 }
1096 }
1097 }
1098
1099 ResTable_config tmpConfig = config;
1100 const size_t ND = densities.size();
1101 for (size_t i=0; i<ND; i++) {
1102 tmpConfig.density = densities[i];
1103 assets.setConfiguration(tmpConfig);
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001104 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1105 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001106 if (icon != "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001107 printf("application-icon-%d:'%s'\n", densities[i],
1108 ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001109 }
1110 }
1111 assets.setConfiguration(config);
1112
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001113 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001114 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001115 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1116 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001117 goto bail;
1118 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001119 int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0,
1120 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001121 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001122 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n",
1123 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001124 goto bail;
1125 }
Maurice Chu2675f762013-10-22 17:33:11 -07001126 printf("application: label='%s' ",
1127 ResTable::normalizeForOutput(label.string()).string());
1128 printf("icon='%s'\n", ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001129 if (testOnly != 0) {
1130 printf("testOnly='%d'\n", testOnly);
1131 }
1132
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001133 int32_t debuggable = AaptXml::getResolvedIntegerAttribute(res, tree,
1134 DEBUGGABLE_ATTR, 0, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001135 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001136 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n",
1137 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001138 goto bail;
1139 }
1140 if (debuggable != 0) {
1141 printf("application-debuggable\n");
1142 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07001143
1144 // We must search by name because the multiArch flag hasn't been API
1145 // frozen yet.
1146 int32_t multiArchIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
1147 "multiArch");
1148 if (multiArchIndex >= 0) {
1149 Res_value value;
1150 if (tree.getAttributeValue(multiArchIndex, &value) != NO_ERROR) {
1151 if (value.dataType >= Res_value::TYPE_FIRST_INT &&
1152 value.dataType <= Res_value::TYPE_LAST_INT) {
1153 hasMultiArch = value.data;
1154 }
1155 }
1156 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001157 } else if (tag == "uses-sdk") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001158 int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001159 if (error != "") {
1160 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001161 String8 name = AaptXml::getResolvedAttribute(res, tree,
1162 MIN_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001163 if (error != "") {
1164 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
1165 error.string());
1166 goto bail;
1167 }
1168 if (name == "Donut") targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001169 printf("sdkVersion:'%s'\n",
1170 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001171 } else if (code != -1) {
1172 targetSdk = code;
1173 printf("sdkVersion:'%d'\n", code);
1174 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001175 code = AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -08001176 if (code != -1) {
1177 printf("maxSdkVersion:'%d'\n", code);
1178 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001179 code = AaptXml::getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001180 if (error != "") {
1181 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001182 String8 name = AaptXml::getResolvedAttribute(res, tree,
1183 TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001184 if (error != "") {
1185 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
1186 error.string());
1187 goto bail;
1188 }
1189 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001190 printf("targetSdkVersion:'%s'\n",
1191 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001192 } else if (code != -1) {
1193 if (targetSdk < code) {
1194 targetSdk = code;
1195 }
1196 printf("targetSdkVersion:'%d'\n", code);
1197 }
1198 } else if (tag == "uses-configuration") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001199 int32_t reqTouchScreen = AaptXml::getIntegerAttribute(tree,
1200 REQ_TOUCH_SCREEN_ATTR, 0);
1201 int32_t reqKeyboardType = AaptXml::getIntegerAttribute(tree,
1202 REQ_KEYBOARD_TYPE_ATTR, 0);
1203 int32_t reqHardKeyboard = AaptXml::getIntegerAttribute(tree,
1204 REQ_HARD_KEYBOARD_ATTR, 0);
1205 int32_t reqNavigation = AaptXml::getIntegerAttribute(tree,
1206 REQ_NAVIGATION_ATTR, 0);
1207 int32_t reqFiveWayNav = AaptXml::getIntegerAttribute(tree,
1208 REQ_FIVE_WAY_NAV_ATTR, 0);
Adam Lesinski282e1812014-01-23 18:17:42 -08001209 printf("uses-configuration:");
1210 if (reqTouchScreen != 0) {
1211 printf(" reqTouchScreen='%d'", reqTouchScreen);
1212 }
1213 if (reqKeyboardType != 0) {
1214 printf(" reqKeyboardType='%d'", reqKeyboardType);
1215 }
1216 if (reqHardKeyboard != 0) {
1217 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1218 }
1219 if (reqNavigation != 0) {
1220 printf(" reqNavigation='%d'", reqNavigation);
1221 }
1222 if (reqFiveWayNav != 0) {
1223 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1224 }
1225 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001226 } else if (tag == "supports-input") {
1227 withinSupportsInput = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001228 } else if (tag == "supports-screens") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001229 smallScreen = AaptXml::getIntegerAttribute(tree,
1230 SMALL_SCREEN_ATTR, 1);
1231 normalScreen = AaptXml::getIntegerAttribute(tree,
1232 NORMAL_SCREEN_ATTR, 1);
1233 largeScreen = AaptXml::getIntegerAttribute(tree,
1234 LARGE_SCREEN_ATTR, 1);
1235 xlargeScreen = AaptXml::getIntegerAttribute(tree,
1236 XLARGE_SCREEN_ATTR, 1);
1237 anyDensity = AaptXml::getIntegerAttribute(tree,
1238 ANY_DENSITY_ATTR, 1);
1239 requiresSmallestWidthDp = AaptXml::getIntegerAttribute(tree,
1240 REQUIRES_SMALLEST_WIDTH_DP_ATTR, 0);
1241 compatibleWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1242 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, 0);
1243 largestWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1244 LARGEST_WIDTH_LIMIT_DP_ATTR, 0);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001245 } else if (tag == "feature-group") {
1246 withinFeatureGroup = true;
1247 FeatureGroup group;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001248 group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001249 if (error != "") {
1250 fprintf(stderr, "ERROR getting 'android:label' attribute:"
1251 " %s\n", error.string());
1252 goto bail;
1253 }
1254 featureGroups.add(group);
1255
Adam Lesinski282e1812014-01-23 18:17:42 -08001256 } else if (tag == "uses-feature") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001257 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001258 if (name != "" && error == "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001259 int req = AaptXml::getIntegerAttribute(tree,
1260 REQUIRED_ATTR, 1);
Adam Lesinski282e1812014-01-23 18:17:42 -08001261
Adam Lesinski2c72b682014-06-24 09:56:01 -07001262 commonFeatures.features.add(name, req);
1263 if (req) {
1264 addParentFeatures(&commonFeatures, name);
Adam Lesinski282e1812014-01-23 18:17:42 -08001265 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001266 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001267 int vers = AaptXml::getIntegerAttribute(tree,
Adam Lesinski282e1812014-01-23 18:17:42 -08001268 GL_ES_VERSION_ATTR, &error);
1269 if (error == "") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001270 if (vers > commonFeatures.openGLESVersion) {
1271 commonFeatures.openGLESVersion = vers;
1272 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001273 }
1274 }
1275 } else if (tag == "uses-permission") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001276 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001277 if (name != "" && error == "") {
1278 if (name == "android.permission.CAMERA") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001279 addImpliedFeature(&impliedFeatures, "android.hardware.camera",
Adam Lesinski2c72b682014-06-24 09:56:01 -07001280 String8::format("requested %s permission", name.string())
1281 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001282 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001283 addImpliedFeature(&impliedFeatures, "android.hardware.location.gps",
1284 String8::format("requested %s permission", name.string())
1285 .string());
1286 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1287 String8::format("requested %s permission", name.string())
1288 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001289 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001290 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_COARSE_LOCATION") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001294 addImpliedFeature(&impliedFeatures, "android.hardware.location.network",
1295 String8::format("requested %s permission", name.string())
1296 .string());
1297 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1298 String8::format("requested %s permission", name.string())
1299 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001300 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
1301 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001302 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1303 String8::format("requested %s permission", name.string())
1304 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001305 } else if (name == "android.permission.BLUETOOTH" ||
1306 name == "android.permission.BLUETOOTH_ADMIN") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001307 if (targetSdk > 4) {
1308 addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
1309 String8::format("requested %s permission", name.string())
1310 .string());
1311 addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
1312 "targetSdkVersion > 4");
1313 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001314 } else if (name == "android.permission.RECORD_AUDIO") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001315 addImpliedFeature(&impliedFeatures, "android.hardware.microphone",
1316 String8::format("requested %s permission", name.string())
1317 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001318 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1319 name == "android.permission.CHANGE_WIFI_STATE" ||
1320 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001321 addImpliedFeature(&impliedFeatures, "android.hardware.wifi",
1322 String8::format("requested %s permission", name.string())
1323 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001324 } else if (name == "android.permission.CALL_PHONE" ||
1325 name == "android.permission.CALL_PRIVILEGED" ||
1326 name == "android.permission.MODIFY_PHONE_STATE" ||
1327 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1328 name == "android.permission.READ_SMS" ||
1329 name == "android.permission.RECEIVE_SMS" ||
1330 name == "android.permission.RECEIVE_MMS" ||
1331 name == "android.permission.RECEIVE_WAP_PUSH" ||
1332 name == "android.permission.SEND_SMS" ||
1333 name == "android.permission.WRITE_APN_SETTINGS" ||
1334 name == "android.permission.WRITE_SMS") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001335 addImpliedFeature(&impliedFeatures, "android.hardware.telephony",
1336 String8("requested a telephony permission").string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001337 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1338 hasWriteExternalStoragePermission = true;
1339 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1340 hasReadExternalStoragePermission = true;
1341 } else if (name == "android.permission.READ_PHONE_STATE") {
1342 hasReadPhoneStatePermission = true;
1343 } else if (name == "android.permission.READ_CONTACTS") {
1344 hasReadContactsPermission = true;
1345 } else if (name == "android.permission.WRITE_CONTACTS") {
1346 hasWriteContactsPermission = true;
1347 } else if (name == "android.permission.READ_CALL_LOG") {
1348 hasReadCallLogPermission = true;
1349 } else if (name == "android.permission.WRITE_CALL_LOG") {
1350 hasWriteCallLogPermission = true;
1351 }
Adam Lesinski58f1f362013-11-12 12:59:08 -08001352
1353 printUsesPermission(name,
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001354 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
1355 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
Adam Lesinski58f1f362013-11-12 12:59:08 -08001356 } else {
Adam Lesinski282e1812014-01-23 18:17:42 -08001357 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1358 error.string());
1359 goto bail;
1360 }
1361 } else if (tag == "uses-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001362 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001363 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001364 printf("uses-package:'%s'\n",
1365 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001366 } else {
1367 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1368 error.string());
1369 goto bail;
1370 }
1371 } else if (tag == "original-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001372 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001373 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001374 printf("original-package:'%s'\n",
1375 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001376 } else {
1377 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1378 error.string());
1379 goto bail;
1380 }
1381 } else if (tag == "supports-gl-texture") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001382 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001383 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001384 printf("supports-gl-texture:'%s'\n",
1385 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001386 } else {
1387 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1388 error.string());
1389 goto bail;
1390 }
1391 } else if (tag == "compatible-screens") {
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001392 printCompatibleScreens(tree, &error);
1393 if (error != "") {
1394 fprintf(stderr, "ERROR getting compatible screens: %s\n",
1395 error.string());
1396 goto bail;
1397 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001398 depth--;
1399 } else if (tag == "package-verifier") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001400 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001401 if (name != "" && error == "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001402 String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001403 if (publicKey != "" && error == "") {
1404 printf("package-verifier: name='%s' publicKey='%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001405 ResTable::normalizeForOutput(name.string()).string(),
1406 ResTable::normalizeForOutput(publicKey.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001407 }
1408 }
1409 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001410 } else if (depth == 3) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001411 withinActivity = false;
1412 withinReceiver = false;
1413 withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001414 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001415 hasIntentFilter = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001416 hasMetaHostPaymentCategory = false;
1417 hasMetaOffHostPaymentCategory = false;
1418 hasBindDeviceAdminPermission = false;
1419 hasBindInputMethodPermission = false;
1420 hasBindAccessibilityServicePermission = false;
1421 hasBindPrintServicePermission = false;
1422 hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001423 hasRequiredSafAttributes = false;
1424 hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001425 hasBindDreamServicePermission = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001426 if (withinApplication) {
1427 if(tag == "activity") {
1428 withinActivity = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001429 activityName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001430 if (error != "") {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001431 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1432 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001433 goto bail;
1434 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001435
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001436 activityLabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1437 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001438 if (error != "") {
1439 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
1440 error.string());
1441 goto bail;
1442 }
1443
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001444 activityIcon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1445 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001446 if (error != "") {
1447 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1448 error.string());
1449 goto bail;
1450 }
1451
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001452 activityBanner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1453 &error);
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001454 if (error != "") {
1455 fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
1456 error.string());
1457 goto bail;
1458 }
1459
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001460 int32_t orien = AaptXml::getResolvedIntegerAttribute(res, tree,
Michael Wrightec4fdec2013-09-06 16:50:52 -07001461 SCREEN_ORIENTATION_ATTR, &error);
1462 if (error == "") {
1463 if (orien == 0 || orien == 6 || orien == 8) {
1464 // Requests landscape, sensorLandscape, or reverseLandscape.
Adam Lesinski2c72b682014-06-24 09:56:01 -07001465 addImpliedFeature(&impliedFeatures, "android.hardware.screen.landscape",
1466 "one or more activities have specified a landscape orientation");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001467 } else if (orien == 1 || orien == 7 || orien == 9) {
1468 // Requests portrait, sensorPortrait, or reversePortrait.
Adam Lesinski2c72b682014-06-24 09:56:01 -07001469 addImpliedFeature(&impliedFeatures, "android.hardware.screen.portrait",
1470 "one or more activities have specified a portrait orientation");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001471 }
1472 }
1473 } else if (tag == "uses-library") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001474 String8 libraryName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001475 if (error != "") {
1476 fprintf(stderr,
1477 "ERROR getting 'android:name' attribute for uses-library"
1478 " %s\n", error.string());
1479 goto bail;
1480 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001481 int req = AaptXml::getIntegerAttribute(tree,
1482 REQUIRED_ATTR, 1);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001483 printf("uses-library%s:'%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001484 req ? "" : "-not-required", ResTable::normalizeForOutput(
1485 libraryName.string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001486 } else if (tag == "receiver") {
1487 withinReceiver = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001488 receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001489
1490 if (error != "") {
1491 fprintf(stderr,
1492 "ERROR getting 'android:name' attribute for receiver:"
1493 " %s\n", error.string());
1494 goto bail;
1495 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001496
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001497 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1498 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001499 if (error == "") {
1500 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1501 hasBindDeviceAdminPermission = true;
1502 }
1503 } else {
1504 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1505 " receiver '%s': %s\n", receiverName.string(), error.string());
1506 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001507 } else if (tag == "service") {
1508 withinService = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001509 serviceName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001510
1511 if (error != "") {
1512 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1513 "service:%s\n", error.string());
1514 goto bail;
1515 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001516
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001517 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1518 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001519 if (error == "") {
1520 if (permission == "android.permission.BIND_INPUT_METHOD") {
1521 hasBindInputMethodPermission = true;
1522 } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") {
1523 hasBindAccessibilityServicePermission = true;
1524 } else if (permission == "android.permission.BIND_PRINT_SERVICE") {
1525 hasBindPrintServicePermission = true;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001526 } else if (permission == "android.permission.BIND_NFC_SERVICE") {
1527 hasBindNfcServicePermission = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001528 } else if (permission == "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
1529 hasBindNotificationListenerServicePermission = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001530 } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
1531 hasBindDreamServicePermission = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001532 }
1533 } else {
1534 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1535 " service '%s': %s\n", serviceName.string(), error.string());
1536 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001537 } else if (tag == "provider") {
1538 withinProvider = true;
1539
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001540 bool exported = AaptXml::getResolvedIntegerAttribute(res, tree,
1541 EXPORTED_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001542 if (error != "") {
1543 fprintf(stderr, "ERROR getting 'android:exported' attribute for provider:"
1544 " %s\n", error.string());
1545 goto bail;
1546 }
1547
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001548 bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute(
1549 res, tree, GRANT_URI_PERMISSIONS_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001550 if (error != "") {
1551 fprintf(stderr, "ERROR getting 'android:grantUriPermissions' attribute for provider:"
1552 " %s\n", error.string());
1553 goto bail;
1554 }
1555
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001556 String8 permission = AaptXml::getResolvedAttribute(res, tree,
1557 PERMISSION_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001558 if (error != "") {
1559 fprintf(stderr, "ERROR getting 'android:permission' attribute for provider:"
1560 " %s\n", error.string());
1561 goto bail;
1562 }
1563
1564 hasRequiredSafAttributes |= exported && grantUriPermissions &&
1565 permission == "android.permission.MANAGE_DOCUMENTS";
1566
Michael Wrightec4fdec2013-09-06 16:50:52 -07001567 } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001568 String8 metaDataName = AaptXml::getResolvedAttribute(res, tree,
1569 NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001570 if (error != "") {
1571 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1572 "meta-data:%s\n", error.string());
1573 goto bail;
1574 }
Maurice Chu2675f762013-10-22 17:33:11 -07001575 printf("meta-data: name='%s' ",
1576 ResTable::normalizeForOutput(metaDataName.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001577 printResolvedResourceAttribute(res, tree, VALUE_ATTR, String8("value"),
Maurice Chu76327312013-10-16 18:28:46 -07001578 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001579 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001580 // Try looking for a RESOURCE_ATTR
1581 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001582 printResolvedResourceAttribute(res, tree, RESOURCE_ATTR,
Maurice Chu76327312013-10-16 18:28:46 -07001583 String8("resource"), &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001584 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001585 fprintf(stderr, "ERROR getting 'android:value' or "
1586 "'android:resource' attribute for "
1587 "meta-data:%s\n", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001588 goto bail;
1589 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001590 }
Maurice Chu76327312013-10-16 18:28:46 -07001591 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001592 } else if (withinSupportsInput && tag == "input-type") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001593 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001594 if (name != "" && error == "") {
1595 supportedInput.add(name);
1596 } else {
1597 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1598 error.string());
1599 goto bail;
1600 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001601 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001602 } else if (withinFeatureGroup && tag == "uses-feature") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001603 FeatureGroup& top = featureGroups.editTop();
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001604
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001605 String8 name = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001606 if (name != "" && error == "") {
Adam Lesinskid3edfde2014-08-08 17:32:44 -07001607 top.features.add(name, true);
1608 addParentFeatures(&top, name);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001609 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001610 int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR,
1611 &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001612 if (error == "") {
1613 if (vers > top.openGLESVersion) {
1614 top.openGLESVersion = vers;
1615 }
1616 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001617 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001618 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001619 } else if (depth == 4) {
1620 if (tag == "intent-filter") {
1621 hasIntentFilter = true;
1622 withinIntentFilter = true;
1623 actMainActivity = false;
1624 actWidgetReceivers = false;
1625 actImeService = false;
1626 actWallpaperService = false;
1627 actAccessibilityService = false;
1628 actPrintService = false;
1629 actDeviceAdminEnabled = false;
1630 actHostApduService = false;
1631 actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001632 actDocumentsProvider = false;
1633 actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001634 actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001635 actCamera = false;
1636 actCameraSecure = false;
1637 catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001638 } else if (withinService && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001639 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001640 if (error != "") {
1641 fprintf(stderr, "ERROR getting 'android:name' attribute for"
1642 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1643 goto bail;
1644 }
1645
1646 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1647 name == "android.nfc.cardemulation.off_host_apdu_service") {
1648 bool offHost = true;
1649 if (name == "android.nfc.cardemulation.host_apdu_service") {
1650 offHost = false;
1651 }
1652
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001653 String8 xmlPath = AaptXml::getResolvedAttribute(res, tree,
1654 RESOURCE_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001655 if (error != "") {
1656 fprintf(stderr, "ERROR getting 'android:resource' attribute for"
1657 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1658 goto bail;
1659 }
1660
1661 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1662 offHost, &error);
1663 if (error != "") {
1664 fprintf(stderr, "ERROR getting AID category for service '%s'\n",
1665 serviceName.string());
1666 goto bail;
1667 }
1668
1669 const size_t catLen = categories.size();
1670 for (size_t i = 0; i < catLen; i++) {
1671 bool paymentCategory = (categories[i] == "payment");
1672 if (offHost) {
1673 hasMetaOffHostPaymentCategory |= paymentCategory;
1674 } else {
1675 hasMetaHostPaymentCategory |= paymentCategory;
1676 }
1677 }
1678 }
1679 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001680 } else if ((depth == 5) && withinIntentFilter) {
1681 String8 action;
1682 if (tag == "action") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001683 action = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001684 if (error != "") {
1685 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1686 error.string());
1687 goto bail;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001688 }
1689
Adam Lesinskia5018c92013-09-30 16:23:15 -07001690 if (withinActivity) {
1691 if (action == "android.intent.action.MAIN") {
1692 isMainActivity = true;
1693 actMainActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001694 } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
1695 action == "android.media.action.VIDEO_CAMERA") {
1696 actCamera = true;
1697 } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
1698 actCameraSecure = true;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001699 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001700 } else if (withinReceiver) {
1701 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1702 actWidgetReceivers = true;
1703 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1704 actDeviceAdminEnabled = true;
1705 }
1706 } else if (withinService) {
1707 if (action == "android.view.InputMethod") {
1708 actImeService = true;
1709 } else if (action == "android.service.wallpaper.WallpaperService") {
1710 actWallpaperService = true;
1711 } else if (action == "android.accessibilityservice.AccessibilityService") {
1712 actAccessibilityService = true;
1713 } else if (action == "android.printservice.PrintService") {
1714 actPrintService = true;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001715 } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
1716 actHostApduService = true;
1717 } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
1718 actOffHostApduService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001719 } else if (action == "android.service.notification.NotificationListenerService") {
1720 actNotificationListenerService = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001721 } else if (action == "android.service.dreams.DreamService") {
1722 actDreamService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001723 }
1724 } else if (withinProvider) {
1725 if (action == "android.content.action.DOCUMENTS_PROVIDER") {
1726 actDocumentsProvider = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001727 }
1728 }
1729 if (action == "android.intent.action.SEARCH") {
1730 isSearchable = true;
1731 }
1732 }
1733
1734 if (tag == "category") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001735 String8 category = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001736 if (error != "") {
1737 fprintf(stderr, "ERROR getting 'name' attribute: %s\n",
1738 error.string());
1739 goto bail;
1740 }
1741 if (withinActivity) {
1742 if (category == "android.intent.category.LAUNCHER") {
1743 isLauncherActivity = true;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001744 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
1745 isLeanbackLauncherActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001746 } else if (category == "android.intent.category.HOME") {
1747 catLauncher = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001748 }
1749 }
1750 }
1751 }
1752 }
1753
1754 // Pre-1.6 implicitly granted permission compatibility logic
1755 if (targetSdk < 4) {
1756 if (!hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001757 printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
1758 printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
1759 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001760 hasWriteExternalStoragePermission = true;
1761 }
1762 if (!hasReadPhoneStatePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001763 printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
1764 printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
1765 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001766 }
1767 }
1768
1769 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1770 // force them to always take READ_EXTERNAL_STORAGE as well. We always
1771 // do this (regardless of target API version) because we can't have
1772 // an app with write permission but not read permission.
1773 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001774 printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"));
1775 printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
1776 String8("requested WRITE_EXTERNAL_STORAGE"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001777 }
1778
1779 // Pre-JellyBean call log permission compatibility.
1780 if (targetSdk < 16) {
1781 if (!hasReadCallLogPermission && hasReadContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001782 printUsesPermission(String8("android.permission.READ_CALL_LOG"));
1783 printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
1784 String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001785 }
1786 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001787 printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
1788 printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
1789 String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001790 }
1791 }
1792
Adam Lesinski2c72b682014-06-24 09:56:01 -07001793 addImpliedFeature(&impliedFeatures, "android.hardware.touchscreen",
1794 "default feature for all apps");
1795
1796 const size_t numFeatureGroups = featureGroups.size();
1797 if (numFeatureGroups == 0) {
1798 // If no <feature-group> tags were defined, apply auto-implied features.
1799 printFeatureGroup(commonFeatures, &impliedFeatures);
1800
1801 } else {
1802 // <feature-group> tags are defined, so we ignore implied features and
1803 for (size_t i = 0; i < numFeatureGroups; i++) {
1804 FeatureGroup& grp = featureGroups.editItemAt(i);
1805
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001806 if (commonFeatures.openGLESVersion > grp.openGLESVersion) {
1807 grp.openGLESVersion = commonFeatures.openGLESVersion;
1808 }
1809
Adam Lesinski2c72b682014-06-24 09:56:01 -07001810 // Merge the features defined in the top level (not inside a <feature-group>)
1811 // with this feature group.
1812 const size_t numCommonFeatures = commonFeatures.features.size();
1813 for (size_t j = 0; j < numCommonFeatures; j++) {
1814 if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001815 grp.features.add(commonFeatures.features.keyAt(j),
1816 commonFeatures.features[j]);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001817 }
1818 }
1819
1820 if (!grp.features.isEmpty()) {
1821 printFeatureGroup(grp);
Adam Lesinski282e1812014-01-23 18:17:42 -08001822 }
1823 }
1824 }
1825
Adam Lesinski282e1812014-01-23 18:17:42 -08001826
Adam Lesinski282e1812014-01-23 18:17:42 -08001827 if (hasWidgetReceivers) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001828 printComponentPresence("app-widget");
Adam Lesinski282e1812014-01-23 18:17:42 -08001829 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001830 if (hasDeviceAdminReceiver) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001831 printComponentPresence("device-admin");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001832 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001833 if (hasImeService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001834 printComponentPresence("ime");
Adam Lesinski282e1812014-01-23 18:17:42 -08001835 }
1836 if (hasWallpaperService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001837 printComponentPresence("wallpaper");
Adam Lesinski282e1812014-01-23 18:17:42 -08001838 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001839 if (hasAccessibilityService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001840 printComponentPresence("accessibility");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001841 }
1842 if (hasPrintService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001843 printComponentPresence("print-service");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001844 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001845 if (hasPaymentService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001846 printComponentPresence("payment");
1847 }
1848 if (isSearchable) {
1849 printComponentPresence("search");
1850 }
1851 if (hasDocumentsProvider) {
1852 printComponentPresence("document-provider");
1853 }
1854 if (hasLauncher) {
1855 printComponentPresence("launcher");
1856 }
1857 if (hasNotificationListenerService) {
1858 printComponentPresence("notification-listener");
1859 }
John Spurlockeb8d1be2014-06-25 17:46:15 -04001860 if (hasDreamService) {
1861 printComponentPresence("dream");
1862 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001863 if (hasCameraActivity) {
1864 printComponentPresence("camera");
1865 }
1866 if (hasCameraSecureActivity) {
1867 printComponentPresence("camera-secure");
1868 }
1869
1870 if (hasMainActivity) {
1871 printf("main\n");
Adam Lesinski94fc9122013-09-30 17:16:09 -07001872 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001873 if (hasOtherActivities) {
1874 printf("other-activities\n");
1875 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001876 if (hasOtherReceivers) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001877 printf("other-receivers\n");
1878 }
1879 if (hasOtherServices) {
1880 printf("other-services\n");
1881 }
1882
1883 // For modern apps, if screen size buckets haven't been specified
1884 // but the new width ranges have, then infer the buckets from them.
1885 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1886 && requiresSmallestWidthDp > 0) {
1887 int compatWidth = compatibleWidthLimitDp;
1888 if (compatWidth <= 0) {
1889 compatWidth = requiresSmallestWidthDp;
1890 }
1891 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1892 smallScreen = -1;
1893 } else {
1894 smallScreen = 0;
1895 }
1896 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1897 normalScreen = -1;
1898 } else {
1899 normalScreen = 0;
1900 }
1901 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1902 largeScreen = -1;
1903 } else {
1904 largeScreen = 0;
1905 }
1906 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1907 xlargeScreen = -1;
1908 } else {
1909 xlargeScreen = 0;
1910 }
1911 }
1912
1913 // Determine default values for any unspecified screen sizes,
1914 // based on the target SDK of the package. As of 4 (donut)
1915 // the screen size support was introduced, so all default to
1916 // enabled.
1917 if (smallScreen > 0) {
1918 smallScreen = targetSdk >= 4 ? -1 : 0;
1919 }
1920 if (normalScreen > 0) {
1921 normalScreen = -1;
1922 }
1923 if (largeScreen > 0) {
1924 largeScreen = targetSdk >= 4 ? -1 : 0;
1925 }
1926 if (xlargeScreen > 0) {
1927 // Introduced in Gingerbread.
1928 xlargeScreen = targetSdk >= 9 ? -1 : 0;
1929 }
1930 if (anyDensity > 0) {
1931 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1932 || compatibleWidthLimitDp > 0) ? -1 : 0;
1933 }
1934 printf("supports-screens:");
1935 if (smallScreen != 0) {
1936 printf(" 'small'");
1937 }
1938 if (normalScreen != 0) {
1939 printf(" 'normal'");
1940 }
1941 if (largeScreen != 0) {
1942 printf(" 'large'");
1943 }
1944 if (xlargeScreen != 0) {
1945 printf(" 'xlarge'");
1946 }
1947 printf("\n");
1948 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
1949 if (requiresSmallestWidthDp > 0) {
1950 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1951 }
1952 if (compatibleWidthLimitDp > 0) {
1953 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1954 }
1955 if (largestWidthLimitDp > 0) {
1956 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1957 }
1958
1959 printf("locales:");
1960 const size_t NL = locales.size();
1961 for (size_t i=0; i<NL; i++) {
1962 const char* localeStr = locales[i].string();
1963 if (localeStr == NULL || strlen(localeStr) == 0) {
1964 localeStr = "--_--";
1965 }
1966 printf(" '%s'", localeStr);
1967 }
1968 printf("\n");
1969
1970 printf("densities:");
1971 const size_t ND = densities.size();
1972 for (size_t i=0; i<ND; i++) {
1973 printf(" '%d'", densities[i]);
1974 }
1975 printf("\n");
1976
1977 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1978 if (dir != NULL) {
1979 if (dir->getFileCount() > 0) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07001980 SortedVector<String8> architectures;
Adam Lesinski282e1812014-01-23 18:17:42 -08001981 for (size_t i=0; i<dir->getFileCount(); i++) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07001982 architectures.add(ResTable::normalizeForOutput(
1983 dir->getFileName(i).string()));
Adam Lesinski282e1812014-01-23 18:17:42 -08001984 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07001985
1986 bool outputAltNativeCode = false;
1987 // A multiArch package is one that contains 64-bit and
1988 // 32-bit versions of native code and expects 3rd-party
1989 // apps to load these native code libraries. Since most
1990 // 64-bit systems also support 32-bit apps, the apps
1991 // loading this multiArch package's code may be either
1992 // 32-bit or 64-bit.
1993 if (hasMultiArch) {
1994 // If this is a multiArch package, report the 64-bit
1995 // version only. Then as a separate entry, report the
1996 // rest.
1997 //
1998 // If we report the 32-bit architecture, this APK will
1999 // be installed on a 32-bit device, causing a large waste
2000 // of bandwidth and disk space. This assumes that
2001 // the developer of the multiArch package has also
2002 // made a version that is 32-bit only.
2003 String8 intel64("x86_64");
2004 String8 arm64("arm64-v8a");
2005 ssize_t index = architectures.indexOf(intel64);
2006 if (index < 0) {
2007 index = architectures.indexOf(arm64);
2008 }
2009
2010 if (index >= 0) {
2011 printf("native-code: '%s'\n", architectures[index].string());
2012 architectures.removeAt(index);
2013 outputAltNativeCode = true;
2014 }
2015 }
2016
2017 const size_t archCount = architectures.size();
2018 if (archCount > 0) {
2019 if (outputAltNativeCode) {
2020 printf("alt-");
2021 }
2022 printf("native-code:");
2023 for (size_t i = 0; i < archCount; i++) {
2024 printf(" '%s'", architectures[i].string());
2025 }
2026 printf("\n");
2027 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002028 }
2029 delete dir;
2030 }
2031 } else if (strcmp("badger", option) == 0) {
2032 printf("%s", CONSOLE_DATA);
2033 } else if (strcmp("configurations", option) == 0) {
2034 Vector<ResTable_config> configs;
2035 res.getConfigurations(&configs);
2036 const size_t N = configs.size();
2037 for (size_t i=0; i<N; i++) {
2038 printf("%s\n", configs[i].toString().string());
2039 }
2040 } else {
2041 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2042 goto bail;
2043 }
2044 }
2045
2046 result = NO_ERROR;
2047
2048bail:
2049 if (asset) {
2050 delete asset;
2051 }
2052 return (result != NO_ERROR);
2053}
2054
2055
2056/*
2057 * Handle the "add" command, which wants to add files to a new or
2058 * pre-existing archive.
2059 */
2060int doAdd(Bundle* bundle)
2061{
2062 ZipFile* zip = NULL;
2063 status_t result = UNKNOWN_ERROR;
2064 const char* zipFileName;
2065
2066 if (bundle->getUpdate()) {
2067 /* avoid confusion */
2068 fprintf(stderr, "ERROR: can't use '-u' with add\n");
2069 goto bail;
2070 }
2071
2072 if (bundle->getFileSpecCount() < 1) {
2073 fprintf(stderr, "ERROR: must specify zip file name\n");
2074 goto bail;
2075 }
2076 zipFileName = bundle->getFileSpecEntry(0);
2077
2078 if (bundle->getFileSpecCount() < 2) {
2079 fprintf(stderr, "NOTE: nothing to do\n");
2080 goto bail;
2081 }
2082
2083 zip = openReadWrite(zipFileName, true);
2084 if (zip == NULL) {
2085 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2086 goto bail;
2087 }
2088
2089 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2090 const char* fileName = bundle->getFileSpecEntry(i);
2091
2092 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
2093 printf(" '%s'... (from gzip)\n", fileName);
2094 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
2095 } else {
2096 if (bundle->getJunkPath()) {
2097 String8 storageName = String8(fileName).getPathLeaf();
Maurice Chu2675f762013-10-22 17:33:11 -07002098 printf(" '%s' as '%s'...\n", fileName,
2099 ResTable::normalizeForOutput(storageName.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08002100 result = zip->add(fileName, storageName.string(),
2101 bundle->getCompressionMethod(), NULL);
2102 } else {
2103 printf(" '%s'...\n", fileName);
2104 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2105 }
2106 }
2107 if (result != NO_ERROR) {
2108 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2109 if (result == NAME_NOT_FOUND) {
2110 fprintf(stderr, ": file not found\n");
2111 } else if (result == ALREADY_EXISTS) {
2112 fprintf(stderr, ": already exists in archive\n");
2113 } else {
2114 fprintf(stderr, "\n");
2115 }
2116 goto bail;
2117 }
2118 }
2119
2120 result = NO_ERROR;
2121
2122bail:
2123 delete zip;
2124 return (result != NO_ERROR);
2125}
2126
2127
2128/*
2129 * Delete files from an existing archive.
2130 */
2131int doRemove(Bundle* bundle)
2132{
2133 ZipFile* zip = NULL;
2134 status_t result = UNKNOWN_ERROR;
2135 const char* zipFileName;
2136
2137 if (bundle->getFileSpecCount() < 1) {
2138 fprintf(stderr, "ERROR: must specify zip file name\n");
2139 goto bail;
2140 }
2141 zipFileName = bundle->getFileSpecEntry(0);
2142
2143 if (bundle->getFileSpecCount() < 2) {
2144 fprintf(stderr, "NOTE: nothing to do\n");
2145 goto bail;
2146 }
2147
2148 zip = openReadWrite(zipFileName, false);
2149 if (zip == NULL) {
2150 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2151 zipFileName);
2152 goto bail;
2153 }
2154
2155 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2156 const char* fileName = bundle->getFileSpecEntry(i);
2157 ZipEntry* entry;
2158
2159 entry = zip->getEntryByName(fileName);
2160 if (entry == NULL) {
2161 printf(" '%s' NOT FOUND\n", fileName);
2162 continue;
2163 }
2164
2165 result = zip->remove(entry);
2166
2167 if (result != NO_ERROR) {
2168 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2169 bundle->getFileSpecEntry(i), zipFileName);
2170 goto bail;
2171 }
2172 }
2173
2174 /* update the archive */
2175 zip->flush();
2176
2177bail:
2178 delete zip;
2179 return (result != NO_ERROR);
2180}
2181
Adam Lesinski3921e872014-05-13 10:56:25 -07002182static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002183 const size_t numDirs = dir->getDirs().size();
2184 for (size_t i = 0; i < numDirs; i++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002185 bool ignore = ignoreConfig;
2186 const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2187 const char* dirStr = subDir->getLeaf().string();
2188 if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2189 ignore = true;
2190 }
2191 status_t err = addResourcesToBuilder(subDir, builder, ignore);
Adam Lesinskifab50872014-04-16 14:40:42 -07002192 if (err != NO_ERROR) {
2193 return err;
2194 }
2195 }
2196
2197 const size_t numFiles = dir->getFiles().size();
2198 for (size_t i = 0; i < numFiles; i++) {
2199 sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2200 const size_t numConfigs = gp->getFiles().size();
2201 for (size_t j = 0; j < numConfigs; j++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002202 status_t err = NO_ERROR;
2203 if (ignoreConfig) {
2204 err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2205 } else {
2206 err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2207 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002208 if (err != NO_ERROR) {
2209 fprintf(stderr, "Failed to add %s (%s) to builder.\n",
2210 gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
2211 return err;
2212 }
2213 }
2214 }
2215 return NO_ERROR;
2216}
2217
2218static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2219 if (split->isBase()) {
2220 return original;
2221 }
2222
2223 String8 ext(original.getPathExtension());
2224 if (ext == String8(".apk")) {
2225 return String8::format("%s_%s%s",
2226 original.getBasePath().string(),
2227 split->getDirectorySafeName().string(),
2228 ext.string());
2229 }
2230
2231 return String8::format("%s_%s", original.string(),
2232 split->getDirectorySafeName().string());
2233}
Adam Lesinski282e1812014-01-23 18:17:42 -08002234
2235/*
2236 * Package up an asset directory and associated application files.
2237 */
2238int doPackage(Bundle* bundle)
2239{
2240 const char* outputAPKFile;
2241 int retVal = 1;
2242 status_t err;
2243 sp<AaptAssets> assets;
2244 int N;
2245 FILE* fp;
2246 String8 dependencyFile;
Adam Lesinskifab50872014-04-16 14:40:42 -07002247 sp<ApkBuilder> builder;
Adam Lesinski282e1812014-01-23 18:17:42 -08002248
Anton Krumina2ef5c02014-03-12 14:46:44 -07002249 // -c en_XA or/and ar_XB means do pseudolocalization
Adam Lesinskifab50872014-04-16 14:40:42 -07002250 sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2251 err = configFilter->parse(bundle->getConfigurations());
Adam Lesinski282e1812014-01-23 18:17:42 -08002252 if (err != NO_ERROR) {
2253 goto bail;
2254 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002255 if (configFilter->containsPseudo()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002256 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2257 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002258 if (configFilter->containsPseudoBidi()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002259 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
Adam Lesinski282e1812014-01-23 18:17:42 -08002260 }
2261
2262 N = bundle->getFileSpecCount();
2263 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
Adam Lesinski09384302014-01-22 16:07:42 -08002264 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002265 fprintf(stderr, "ERROR: no input files\n");
2266 goto bail;
2267 }
2268
2269 outputAPKFile = bundle->getOutputAPKFile();
2270
2271 // Make sure the filenames provided exist and are of the appropriate type.
2272 if (outputAPKFile) {
2273 FileType type;
2274 type = getFileType(outputAPKFile);
2275 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2276 fprintf(stderr,
2277 "ERROR: output file '%s' exists but is not regular file\n",
2278 outputAPKFile);
2279 goto bail;
2280 }
2281 }
2282
2283 // Load the assets.
2284 assets = new AaptAssets();
2285
2286 // Set up the resource gathering in assets if we're going to generate
2287 // dependency files. Every time we encounter a resource while slurping
2288 // the tree, we'll add it to these stores so we have full resource paths
2289 // to write to a dependency file.
2290 if (bundle->getGenDependencies()) {
2291 sp<FilePathStore> resPathStore = new FilePathStore;
2292 assets->setFullResPaths(resPathStore);
2293 sp<FilePathStore> assetPathStore = new FilePathStore;
2294 assets->setFullAssetPaths(assetPathStore);
2295 }
2296
2297 err = assets->slurpFromArgs(bundle);
2298 if (err < 0) {
2299 goto bail;
2300 }
2301
2302 if (bundle->getVerbose()) {
2303 assets->print(String8());
2304 }
2305
Adam Lesinskifab50872014-04-16 14:40:42 -07002306 // Create the ApkBuilder, which will collect the compiled files
2307 // to write to the final APK (or sets of APKs if we are building
2308 // a Split APK.
2309 builder = new ApkBuilder(configFilter);
2310
2311 // If we are generating a Split APK, find out which configurations to split on.
2312 if (bundle->getSplitConfigurations().size() > 0) {
2313 const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2314 const size_t numSplits = splitStrs.size();
2315 for (size_t i = 0; i < numSplits; i++) {
2316 std::set<ConfigDescription> configs;
2317 if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2318 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2319 goto bail;
2320 }
2321
2322 err = builder->createSplitForConfigs(configs);
2323 if (err != NO_ERROR) {
2324 goto bail;
2325 }
2326 }
2327 }
2328
Adam Lesinski282e1812014-01-23 18:17:42 -08002329 // If they asked for any fileAs that need to be compiled, do so.
2330 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002331 err = buildResources(bundle, assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002332 if (err != 0) {
2333 goto bail;
2334 }
2335 }
2336
2337 // At this point we've read everything and processed everything. From here
2338 // on out it's just writing output files.
2339 if (SourcePos::hasErrors()) {
2340 goto bail;
2341 }
2342
2343 // Update symbols with information about which ones are needed as Java symbols.
2344 assets->applyJavaSymbols();
2345 if (SourcePos::hasErrors()) {
2346 goto bail;
2347 }
2348
2349 // If we've been asked to generate a dependency file, do that here
2350 if (bundle->getGenDependencies()) {
2351 // If this is the packaging step, generate the dependency file next to
2352 // the output apk (e.g. bin/resources.ap_.d)
2353 if (outputAPKFile) {
2354 dependencyFile = String8(outputAPKFile);
2355 // Add the .d extension to the dependency file.
2356 dependencyFile.append(".d");
2357 } else {
2358 // Else if this is the R.java dependency generation step,
2359 // generate the dependency file in the R.java package subdirectory
2360 // e.g. gen/com/foo/app/R.java.d
2361 dependencyFile = String8(bundle->getRClassDir());
2362 dependencyFile.appendPath("R.java.d");
2363 }
2364 // Make sure we have a clean dependency file to start with
2365 fp = fopen(dependencyFile, "w");
2366 fclose(fp);
2367 }
2368
2369 // Write out R.java constants
2370 if (!assets->havePrivateSymbols()) {
2371 if (bundle->getCustomPackage() == NULL) {
2372 // Write the R.java file into the appropriate class directory
2373 // e.g. gen/com/foo/app/R.java
Adam Lesinski1e4663852014-08-15 14:47:28 -07002374 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true,
2375 bundle->getBuildSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002376 } else {
2377 const String8 customPkg(bundle->getCustomPackage());
Adam Lesinski1e4663852014-08-15 14:47:28 -07002378 err = writeResourceSymbols(bundle, assets, customPkg, true,
2379 bundle->getBuildSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002380 }
2381 if (err < 0) {
2382 goto bail;
2383 }
2384 // If we have library files, we're going to write our R.java file into
2385 // the appropriate class directory for those libraries as well.
2386 // e.g. gen/com/foo/app/lib/R.java
2387 if (bundle->getExtraPackages() != NULL) {
2388 // Split on colon
2389 String8 libs(bundle->getExtraPackages());
2390 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2391 while (packageString != NULL) {
2392 // Write the R.java file out with the correct package name
Adam Lesinski1e4663852014-08-15 14:47:28 -07002393 err = writeResourceSymbols(bundle, assets, String8(packageString), true, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002394 if (err < 0) {
2395 goto bail;
2396 }
2397 packageString = strtok(NULL, ":");
2398 }
2399 libs.unlockBuffer();
2400 }
2401 } else {
Adam Lesinski1e4663852014-08-15 14:47:28 -07002402 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002403 if (err < 0) {
2404 goto bail;
2405 }
Adam Lesinski1e4663852014-08-15 14:47:28 -07002406 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002407 if (err < 0) {
2408 goto bail;
2409 }
2410 }
2411
2412 // Write out the ProGuard file
2413 err = writeProguardFile(bundle, assets);
2414 if (err < 0) {
2415 goto bail;
2416 }
2417
2418 // Write the apk
2419 if (outputAPKFile) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002420 // Gather all resources and add them to the APK Builder. The builder will then
2421 // figure out which Split they belong in.
2422 err = addResourcesToBuilder(assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002423 if (err != NO_ERROR) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002424 goto bail;
2425 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002426
2427 const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2428 const size_t numSplits = splits.size();
2429 for (size_t i = 0; i < numSplits; i++) {
2430 const sp<ApkSplit>& split = splits[i];
2431 String8 outputPath = buildApkName(String8(outputAPKFile), split);
2432 err = writeAPK(bundle, outputPath, split);
2433 if (err != NO_ERROR) {
2434 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2435 goto bail;
2436 }
2437 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002438 }
2439
2440 // If we've been asked to generate a dependency file, we need to finish up here.
2441 // the writeResourceSymbols and writeAPK functions have already written the target
2442 // half of the dependency file, now we need to write the prerequisites. (files that
2443 // the R.java file or .ap_ file depend on)
2444 if (bundle->getGenDependencies()) {
2445 // Now that writeResourceSymbols or writeAPK has taken care of writing
2446 // the targets to our dependency file, we'll write the prereqs
2447 fp = fopen(dependencyFile, "a+");
2448 fprintf(fp, " : ");
2449 bool includeRaw = (outputAPKFile != NULL);
2450 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2451 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2452 // and therefore was not added to our pathstores during slurping
2453 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2454 fclose(fp);
2455 }
2456
2457 retVal = 0;
2458bail:
2459 if (SourcePos::hasErrors()) {
2460 SourcePos::printErrors(stderr);
2461 }
2462 return retVal;
2463}
2464
2465/*
2466 * Do PNG Crunching
2467 * PRECONDITIONS
2468 * -S flag points to a source directory containing drawable* folders
2469 * -C flag points to destination directory. The folder structure in the
2470 * source directory will be mirrored to the destination (cache) directory
2471 *
2472 * POSTCONDITIONS
2473 * Destination directory will be updated to match the PNG files in
Maurice Chu2675f762013-10-22 17:33:11 -07002474 * the source directory.
Adam Lesinski282e1812014-01-23 18:17:42 -08002475 */
2476int doCrunch(Bundle* bundle)
2477{
2478 fprintf(stdout, "Crunching PNG Files in ");
2479 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2480 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2481
2482 updatePreProcessedCache(bundle);
2483
2484 return NO_ERROR;
2485}
2486
2487/*
2488 * Do PNG Crunching on a single flag
2489 * -i points to a single png file
2490 * -o points to a single png output file
2491 */
2492int doSingleCrunch(Bundle* bundle)
2493{
2494 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2495 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2496
2497 String8 input(bundle->getSingleCrunchInputFile());
2498 String8 output(bundle->getSingleCrunchOutputFile());
2499
2500 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2501 // we can't return the status_t as it gets truncate to the lower 8 bits.
2502 return 42;
2503 }
2504
2505 return NO_ERROR;
2506}
2507
2508char CONSOLE_DATA[2925] = {
2509 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2510 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2511 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2512 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2513 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2514 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2515 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2516 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2517 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2518 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2519 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2520 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2521 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2522 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2523 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2524 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2525 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2526 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2527 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2528 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2529 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2530 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2531 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2532 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2533 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2534 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2535 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2536 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2537 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2538 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2539 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2540 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2541 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2542 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2543 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2544 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2545 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2546 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2547 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2548 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2549 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2550 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2551 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2552 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2553 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2554 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2555 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2556 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2557 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2558 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2559 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2560 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2561 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2562 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2563 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2564 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2565 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2566 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2567 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2568 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2569 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2570 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2571 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2572 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2573 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2574 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2575 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2576 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2577 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2578 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2579 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2580 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2581 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2582 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2583 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2584 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2585 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2586 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2587 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2588 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2589 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2590 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2591 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2592 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2593 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2594 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2595 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2596 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2597 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2598 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2599 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2600 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2601 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2602 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2603 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2604 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2605 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2606 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2607 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2608 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2609 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2610 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2611 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2612 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2613 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2614 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2615 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2616 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2617 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2618 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2619 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2620 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2621 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2622 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2623 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2624 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2625 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2626 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2627 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2628 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2629 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2630 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2631 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2632 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2633 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2634 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2635 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2636 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2637 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2638 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2639 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2640 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2641 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2642 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2643 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2644 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2645 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2646 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2647 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2648 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2649 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2650 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2651 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2652 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2653 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2654 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2655 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2656 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2657 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2658 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2659 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2660 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2661 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2662 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2663 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2664 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2665 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2666 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2667 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2668 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2669 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2670 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2671 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2672 };