blob: 0b11c9323c1925f9777999ba9f1f691575a3c621 [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);
Adam Lesinski282e1812014-01-23 18:17:42 -0800215#ifndef HAVE_ANDROID_OS
Dan Alberta8995ad2014-09-08 18:34:29 -0700216 printf("\nResource table:\n");
217 res.print(false);
Adam Lesinski282e1812014-01-23 18:17:42 -0800218#endif
Adam Lesinski282e1812014-01-23 18:17:42 -0800219
220 Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
221 Asset::ACCESS_BUFFER);
222 if (manifestAsset == NULL) {
223 printf("\nNo AndroidManifest.xml found.\n");
224 } else {
225 printf("\nAndroid manifest:\n");
226 ResXMLTree tree;
227 tree.setTo(manifestAsset->getBuffer(true),
228 manifestAsset->getLength());
229 printXMLBlock(&tree);
230 }
231 delete manifestAsset;
232 }
233
234 result = 0;
235
236bail:
237 delete zip;
238 return result;
239}
240
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700241static void printResolvedResourceAttribute(const ResTable& resTable, const ResXMLTree& tree,
Maurice Chu76327312013-10-16 18:28:46 -0700242 uint32_t attrRes, String8 attrLabel, String8* outError)
243{
244 Res_value value;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700245 AaptXml::getResolvedResourceAttribute(resTable, tree, attrRes, &value, outError);
Maurice Chu76327312013-10-16 18:28:46 -0700246 if (*outError != "") {
247 *outError = "error print resolved resource attribute";
248 return;
249 }
250 if (value.dataType == Res_value::TYPE_STRING) {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700251 String8 result = AaptXml::getResolvedAttribute(resTable, tree, attrRes, outError);
Maurice Chu2675f762013-10-22 17:33:11 -0700252 printf("%s='%s'", attrLabel.string(),
253 ResTable::normalizeForOutput(result.string()).string());
Maurice Chu76327312013-10-16 18:28:46 -0700254 } else if (Res_value::TYPE_FIRST_INT <= value.dataType &&
255 value.dataType <= Res_value::TYPE_LAST_INT) {
256 printf("%s='%d'", attrLabel.string(), value.data);
257 } else {
258 printf("%s='0x%x'", attrLabel.string(), (int)value.data);
259 }
260}
261
Adam Lesinski282e1812014-01-23 18:17:42 -0800262// These are attribute resource constants for the platform, as found
263// in android.R.attr
264enum {
265 LABEL_ATTR = 0x01010001,
266 ICON_ATTR = 0x01010002,
267 NAME_ATTR = 0x01010003,
Adam Lesinskia5018c92013-09-30 16:23:15 -0700268 PERMISSION_ATTR = 0x01010006,
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700269 EXPORTED_ATTR = 0x01010010,
270 GRANT_URI_PERMISSIONS_ATTR = 0x0101001b,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700271 RESOURCE_ATTR = 0x01010025,
Adam Lesinski282e1812014-01-23 18:17:42 -0800272 DEBUGGABLE_ATTR = 0x0101000f,
273 VALUE_ATTR = 0x01010024,
274 VERSION_CODE_ATTR = 0x0101021b,
275 VERSION_NAME_ATTR = 0x0101021c,
276 SCREEN_ORIENTATION_ATTR = 0x0101001e,
277 MIN_SDK_VERSION_ATTR = 0x0101020c,
278 MAX_SDK_VERSION_ATTR = 0x01010271,
279 REQ_TOUCH_SCREEN_ATTR = 0x01010227,
280 REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
281 REQ_HARD_KEYBOARD_ATTR = 0x01010229,
282 REQ_NAVIGATION_ATTR = 0x0101022a,
283 REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
284 TARGET_SDK_VERSION_ATTR = 0x01010270,
285 TEST_ONLY_ATTR = 0x01010272,
286 ANY_DENSITY_ATTR = 0x0101026c,
287 GL_ES_VERSION_ATTR = 0x01010281,
288 SMALL_SCREEN_ATTR = 0x01010284,
289 NORMAL_SCREEN_ATTR = 0x01010285,
290 LARGE_SCREEN_ATTR = 0x01010286,
291 XLARGE_SCREEN_ATTR = 0x010102bf,
292 REQUIRED_ATTR = 0x0101028e,
Adam Lesinskicaf797c2014-08-22 12:56:26 -0700293 INSTALL_LOCATION_ATTR = 0x010102b7,
Adam Lesinski282e1812014-01-23 18:17:42 -0800294 SCREEN_SIZE_ATTR = 0x010102ca,
295 SCREEN_DENSITY_ATTR = 0x010102cb,
296 REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
297 COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
298 LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
299 PUBLIC_KEY_ATTR = 0x010103a6,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700300 CATEGORY_ATTR = 0x010103e8,
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800301 BANNER_ATTR = 0x10103f2,
Adam Lesinski282e1812014-01-23 18:17:42 -0800302};
303
Maurice Chu2675f762013-10-22 17:33:11 -0700304String8 getComponentName(String8 &pkgName, String8 &componentName) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800305 ssize_t idx = componentName.find(".");
306 String8 retStr(pkgName);
307 if (idx == 0) {
308 retStr += componentName;
309 } else if (idx < 0) {
310 retStr += ".";
311 retStr += componentName;
312 } else {
Maurice Chu2675f762013-10-22 17:33:11 -0700313 return componentName;
Adam Lesinski282e1812014-01-23 18:17:42 -0800314 }
Maurice Chu2675f762013-10-22 17:33:11 -0700315 return retStr;
Adam Lesinski282e1812014-01-23 18:17:42 -0800316}
317
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700318static void printCompatibleScreens(ResXMLTree& tree, String8* outError) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800319 size_t len;
320 ResXMLTree::event_code_t code;
321 int depth = 0;
322 bool first = true;
323 printf("compatible-screens:");
324 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
325 if (code == ResXMLTree::END_TAG) {
326 depth--;
327 if (depth < 0) {
328 break;
329 }
330 continue;
331 }
332 if (code != ResXMLTree::START_TAG) {
333 continue;
334 }
335 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700336 const char16_t* ctag16 = tree.getElementName(&len);
337 if (ctag16 == NULL) {
338 *outError = "failed to get XML element name (bad string pool)";
339 return;
340 }
341 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800342 if (tag == "screen") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700343 int32_t screenSize = AaptXml::getIntegerAttribute(tree,
344 SCREEN_SIZE_ATTR);
345 int32_t screenDensity = AaptXml::getIntegerAttribute(tree,
346 SCREEN_DENSITY_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -0800347 if (screenSize > 0 && screenDensity > 0) {
348 if (!first) {
349 printf(",");
350 }
351 first = false;
352 printf("'%d/%d'", screenSize, screenDensity);
353 }
354 }
355 }
356 printf("\n");
357}
358
Adam Lesinski58f1f362013-11-12 12:59:08 -0800359static void printUsesPermission(const String8& name, bool optional=false, int maxSdkVersion=-1) {
360 printf("uses-permission: name='%s'", ResTable::normalizeForOutput(name.string()).string());
361 if (maxSdkVersion != -1) {
362 printf(" maxSdkVersion='%d'", maxSdkVersion);
363 }
364 printf("\n");
365
366 if (optional) {
367 printf("optional-permission: name='%s'",
368 ResTable::normalizeForOutput(name.string()).string());
369 if (maxSdkVersion != -1) {
370 printf(" maxSdkVersion='%d'", maxSdkVersion);
371 }
372 printf("\n");
373 }
374}
375
376static void printUsesImpliedPermission(const String8& name, const String8& reason) {
377 printf("uses-implied-permission: name='%s' reason='%s'\n",
378 ResTable::normalizeForOutput(name.string()).string(),
379 ResTable::normalizeForOutput(reason.string()).string());
380}
381
Adam Lesinski94fc9122013-09-30 17:16:09 -0700382Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool offHost,
383 String8 *outError = NULL)
384{
385 Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER);
386 if (aidAsset == NULL) {
387 if (outError != NULL) *outError = "xml resource does not exist";
388 return Vector<String8>();
389 }
390
391 const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service");
392
393 bool withinApduService = false;
394 Vector<String8> categories;
395
396 String8 error;
397 ResXMLTree tree;
398 tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength());
399
400 size_t len;
401 int depth = 0;
402 ResXMLTree::event_code_t code;
403 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
404 if (code == ResXMLTree::END_TAG) {
405 depth--;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700406 const char16_t* ctag16 = tree.getElementName(&len);
407 if (ctag16 == NULL) {
408 *outError = "failed to get XML element name (bad string pool)";
409 return Vector<String8>();
410 }
411 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700412
413 if (depth == 0 && tag == serviceTagName) {
414 withinApduService = false;
415 }
416
417 } else if (code == ResXMLTree::START_TAG) {
418 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700419 const char16_t* ctag16 = tree.getElementName(&len);
420 if (ctag16 == NULL) {
421 *outError = "failed to get XML element name (bad string pool)";
422 return Vector<String8>();
423 }
424 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700425
426 if (depth == 1) {
427 if (tag == serviceTagName) {
428 withinApduService = true;
429 }
430 } else if (depth == 2 && withinApduService) {
431 if (tag == "aid-group") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700432 String8 category = AaptXml::getAttribute(tree, CATEGORY_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700433 if (error != "") {
434 if (outError != NULL) *outError = error;
435 return Vector<String8>();
436 }
437
438 categories.add(category);
439 }
440 }
441 }
442 }
443 aidAsset->close();
444 return categories;
445}
446
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700447static void printComponentPresence(const char* componentName) {
448 printf("provides-component:'%s'\n", componentName);
449}
450
Adam Lesinski2c72b682014-06-24 09:56:01 -0700451/**
452 * Represents a feature that has been automatically added due to
453 * a pre-requisite or some other reason.
454 */
455struct ImpliedFeature {
456 /**
457 * Name of the implied feature.
458 */
459 String8 name;
460
461 /**
462 * List of human-readable reasons for why this feature was implied.
463 */
464 SortedVector<String8> reasons;
465};
466
467/**
468 * Represents a <feature-group> tag in the AndroidManifest.xml
469 */
470struct FeatureGroup {
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700471 FeatureGroup() : openGLESVersion(-1) {}
472
Adam Lesinski2c72b682014-06-24 09:56:01 -0700473 /**
474 * Human readable label
475 */
476 String8 label;
477
478 /**
479 * Explicit features defined in the group
480 */
481 KeyedVector<String8, bool> features;
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700482
483 /**
484 * OpenGL ES version required
485 */
486 int openGLESVersion;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700487};
488
489static void addImpliedFeature(KeyedVector<String8, ImpliedFeature>* impliedFeatures,
490 const char* name, const char* reason) {
491 String8 name8(name);
492 ssize_t idx = impliedFeatures->indexOfKey(name8);
493 if (idx < 0) {
494 idx = impliedFeatures->add(name8, ImpliedFeature());
495 impliedFeatures->editValueAt(idx).name = name8;
496 }
497 impliedFeatures->editValueAt(idx).reasons.add(String8(reason));
498}
499
500static void printFeatureGroup(const FeatureGroup& grp,
501 const KeyedVector<String8, ImpliedFeature>* impliedFeatures = NULL) {
502 printf("feature-group: label='%s'\n", grp.label.string());
503
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700504 if (grp.openGLESVersion > 0) {
505 printf(" uses-gl-es: '0x%x'\n", grp.openGLESVersion);
506 }
507
Adam Lesinski2c72b682014-06-24 09:56:01 -0700508 const size_t numFeatures = grp.features.size();
509 for (size_t i = 0; i < numFeatures; i++) {
510 if (!grp.features[i]) {
511 continue;
512 }
513
514 const String8& featureName = grp.features.keyAt(i);
515 printf(" uses-feature: name='%s'\n",
516 ResTable::normalizeForOutput(featureName.string()).string());
517 }
518
519 const size_t numImpliedFeatures =
520 (impliedFeatures != NULL) ? impliedFeatures->size() : 0;
521 for (size_t i = 0; i < numImpliedFeatures; i++) {
522 const ImpliedFeature& impliedFeature = impliedFeatures->valueAt(i);
523 if (grp.features.indexOfKey(impliedFeature.name) >= 0) {
524 // The feature is explicitly set, no need to use implied
525 // definition.
526 continue;
527 }
528
529 String8 printableFeatureName(ResTable::normalizeForOutput(
530 impliedFeature.name.string()));
531 printf(" uses-feature: name='%s'\n", printableFeatureName.string());
532 printf(" uses-implied-feature: name='%s' reason='",
533 printableFeatureName.string());
534 const size_t numReasons = impliedFeature.reasons.size();
535 for (size_t j = 0; j < numReasons; j++) {
536 printf("%s", impliedFeature.reasons[j].string());
537 if (j + 2 < numReasons) {
538 printf(", ");
539 } else if (j + 1 < numReasons) {
540 printf(", and ");
541 }
542 }
543 printf("'\n");
544 }
545}
546
547static void addParentFeatures(FeatureGroup* grp, const String8& name) {
548 if (name == "android.hardware.camera.autofocus" ||
549 name == "android.hardware.camera.flash") {
550 grp->features.add(String8("android.hardware.camera"), true);
551 } else if (name == "android.hardware.location.gps" ||
552 name == "android.hardware.location.network") {
553 grp->features.add(String8("android.hardware.location"), true);
554 } else if (name == "android.hardware.touchscreen.multitouch") {
555 grp->features.add(String8("android.hardware.touchscreen"), true);
556 } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
557 grp->features.add(String8("android.hardware.touchscreen.multitouch"), true);
558 grp->features.add(String8("android.hardware.touchscreen"), true);
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700559 } else if (name == "android.hardware.opengles.aep") {
560 const int openGLESVersion31 = 0x00030001;
561 if (openGLESVersion31 > grp->openGLESVersion) {
562 grp->openGLESVersion = openGLESVersion31;
563 }
Adam Lesinski2c72b682014-06-24 09:56:01 -0700564 }
565}
566
Adam Lesinski282e1812014-01-23 18:17:42 -0800567/*
568 * Handle the "dump" command, to extract select data from an archive.
569 */
570extern char CONSOLE_DATA[2925]; // see EOF
571int doDump(Bundle* bundle)
572{
573 status_t result = UNKNOWN_ERROR;
Adam Lesinski282e1812014-01-23 18:17:42 -0800574
575 if (bundle->getFileSpecCount() < 1) {
576 fprintf(stderr, "ERROR: no dump option specified\n");
577 return 1;
578 }
579
580 if (bundle->getFileSpecCount() < 2) {
581 fprintf(stderr, "ERROR: no dump file specified\n");
582 return 1;
583 }
584
585 const char* option = bundle->getFileSpecEntry(0);
586 const char* filename = bundle->getFileSpecEntry(1);
587
588 AssetManager assets;
Narayan Kamathf85e41f2014-01-24 14:14:30 +0000589 int32_t assetsCookie;
Adam Lesinski282e1812014-01-23 18:17:42 -0800590 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
591 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
592 return 1;
593 }
594
595 // Make a dummy config for retrieving resources... we need to supply
596 // non-default values for some configs so that we can retrieve resources
597 // in the app that don't have a default. The most important of these is
598 // the API version because key resources like icons will have an implicit
599 // version if they are using newer config types like density.
600 ResTable_config config;
Narayan Kamath91447d82014-01-21 15:32:36 +0000601 memset(&config, 0, sizeof(ResTable_config));
Adam Lesinski282e1812014-01-23 18:17:42 -0800602 config.language[0] = 'e';
603 config.language[1] = 'n';
604 config.country[0] = 'U';
605 config.country[1] = 'S';
606 config.orientation = ResTable_config::ORIENTATION_PORT;
607 config.density = ResTable_config::DENSITY_MEDIUM;
608 config.sdkVersion = 10000; // Very high.
609 config.screenWidthDp = 320;
610 config.screenHeightDp = 480;
611 config.smallestScreenWidthDp = 320;
Adam Lesinskic2dea8d2014-08-04 16:40:41 -0700612 config.screenLayout |= ResTable_config::SCREENSIZE_NORMAL;
Adam Lesinski282e1812014-01-23 18:17:42 -0800613 assets.setConfiguration(config);
614
615 const ResTable& res = assets.getResources(false);
Dan Albert68001652014-09-09 09:51:01 -0700616 if (res.getError() != NO_ERROR) {
Adam Lesinski25e9d552014-05-19 15:01:43 -0700617 fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
Adam Lesinski63e646e2014-07-30 11:40:39 -0700618 return 1;
Adam Lesinski282e1812014-01-23 18:17:42 -0800619 }
620
Adam Lesinski2cb761e2014-08-15 13:59:02 -0700621 // The dynamicRefTable can be null if there are no resources for this asset cookie.
622 // This fine.
Adam Lesinski63e646e2014-07-30 11:40:39 -0700623 const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700624
625 Asset* asset = NULL;
626
Adam Lesinski282e1812014-01-23 18:17:42 -0800627 if (strcmp("resources", option) == 0) {
628#ifndef HAVE_ANDROID_OS
629 res.print(bundle->getValues());
630#endif
631
632 } else if (strcmp("strings", option) == 0) {
633 const ResStringPool* pool = res.getTableStringBlock(0);
634 printStringPool(pool);
635
636 } else if (strcmp("xmltree", option) == 0) {
637 if (bundle->getFileSpecCount() < 3) {
638 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
639 goto bail;
640 }
641
642 for (int i=2; i<bundle->getFileSpecCount(); i++) {
643 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700644 ResXMLTree tree(dynamicRefTable);
645 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800646 if (asset == NULL) {
647 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
648 goto bail;
649 }
650
651 if (tree.setTo(asset->getBuffer(true),
652 asset->getLength()) != NO_ERROR) {
653 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
654 goto bail;
655 }
656 tree.restart();
657 printXMLBlock(&tree);
658 tree.uninit();
659 delete asset;
660 asset = NULL;
661 }
662
663 } else if (strcmp("xmlstrings", option) == 0) {
664 if (bundle->getFileSpecCount() < 3) {
665 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
666 goto bail;
667 }
668
669 for (int i=2; i<bundle->getFileSpecCount(); i++) {
670 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700671 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800672 if (asset == NULL) {
673 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
674 goto bail;
675 }
676
Adam Lesinski63e646e2014-07-30 11:40:39 -0700677 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800678 if (tree.setTo(asset->getBuffer(true),
679 asset->getLength()) != NO_ERROR) {
680 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
681 goto bail;
682 }
683 printStringPool(&tree.getStrings());
684 delete asset;
685 asset = NULL;
686 }
687
688 } else {
Adam Lesinski63e646e2014-07-30 11:40:39 -0700689 asset = assets.openNonAsset(assetsCookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800690 if (asset == NULL) {
691 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
692 goto bail;
693 }
694
Adam Lesinski63e646e2014-07-30 11:40:39 -0700695 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800696 if (tree.setTo(asset->getBuffer(true),
697 asset->getLength()) != NO_ERROR) {
698 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
699 goto bail;
700 }
701 tree.restart();
702
703 if (strcmp("permissions", option) == 0) {
704 size_t len;
705 ResXMLTree::event_code_t code;
706 int depth = 0;
707 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
708 if (code == ResXMLTree::END_TAG) {
709 depth--;
710 continue;
711 }
712 if (code != ResXMLTree::START_TAG) {
713 continue;
714 }
715 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700716 const char16_t* ctag16 = tree.getElementName(&len);
717 if (ctag16 == NULL) {
718 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
719 goto bail;
720 }
721 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800722 //printf("Depth %d tag %s\n", depth, tag.string());
723 if (depth == 1) {
724 if (tag != "manifest") {
725 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
726 goto bail;
727 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700728 String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -0700729 printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800730 } else if (depth == 2 && tag == "permission") {
731 String8 error;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700732 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -0800733 if (error != "") {
734 fprintf(stderr, "ERROR: %s\n", error.string());
735 goto bail;
736 }
Maurice Chu2675f762013-10-22 17:33:11 -0700737 printf("permission: %s\n",
738 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800739 } else if (depth == 2 && tag == "uses-permission") {
740 String8 error;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700741 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -0800742 if (error != "") {
743 fprintf(stderr, "ERROR: %s\n", error.string());
744 goto bail;
745 }
Adam Lesinski58f1f362013-11-12 12:59:08 -0800746 printUsesPermission(name,
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700747 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
748 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
Adam Lesinski282e1812014-01-23 18:17:42 -0800749 }
750 }
751 } else if (strcmp("badging", option) == 0) {
752 Vector<String8> locales;
753 res.getLocales(&locales);
754
755 Vector<ResTable_config> configs;
756 res.getConfigurations(&configs);
757 SortedVector<int> densities;
758 const size_t NC = configs.size();
759 for (size_t i=0; i<NC; i++) {
760 int dens = configs[i].density;
761 if (dens == 0) {
762 dens = 160;
763 }
764 densities.add(dens);
765 }
766
767 size_t len;
768 ResXMLTree::event_code_t code;
769 int depth = 0;
770 String8 error;
771 bool withinActivity = false;
772 bool isMainActivity = false;
773 bool isLauncherActivity = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800774 bool isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800775 bool isSearchable = false;
776 bool withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700777 bool withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700778 bool withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800779 bool withinReceiver = false;
780 bool withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700781 bool withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800782 bool withinIntentFilter = false;
783 bool hasMainActivity = false;
784 bool hasOtherActivities = false;
785 bool hasOtherReceivers = false;
786 bool hasOtherServices = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700787 bool hasIntentFilter = false;
788
Adam Lesinski282e1812014-01-23 18:17:42 -0800789 bool hasWallpaperService = false;
790 bool hasImeService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700791 bool hasAccessibilityService = false;
792 bool hasPrintService = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800793 bool hasWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700794 bool hasDeviceAdminReceiver = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700795 bool hasPaymentService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700796 bool hasDocumentsProvider = false;
797 bool hasCameraActivity = false;
798 bool hasCameraSecureActivity = false;
799 bool hasLauncher = false;
800 bool hasNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400801 bool hasDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700802
Adam Lesinski282e1812014-01-23 18:17:42 -0800803 bool actMainActivity = false;
804 bool actWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700805 bool actDeviceAdminEnabled = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800806 bool actImeService = false;
807 bool actWallpaperService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700808 bool actAccessibilityService = false;
809 bool actPrintService = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700810 bool actHostApduService = false;
811 bool actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700812 bool actDocumentsProvider = false;
813 bool actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400814 bool actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700815 bool actCamera = false;
816 bool actCameraSecure = false;
817 bool catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700818 bool hasMetaHostPaymentCategory = false;
819 bool hasMetaOffHostPaymentCategory = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700820
821 // These permissions are required by services implementing services
822 // the system binds to (IME, Accessibility, PrintServices, etc.)
823 bool hasBindDeviceAdminPermission = false;
824 bool hasBindInputMethodPermission = false;
825 bool hasBindAccessibilityServicePermission = false;
826 bool hasBindPrintServicePermission = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700827 bool hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700828 bool hasRequiredSafAttributes = false;
829 bool hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400830 bool hasBindDreamServicePermission = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800831
832 // These two implement the implicit permissions that are granted
833 // to pre-1.6 applications.
834 bool hasWriteExternalStoragePermission = false;
835 bool hasReadPhoneStatePermission = false;
836
837 // If an app requests write storage, they will also get read storage.
838 bool hasReadExternalStoragePermission = false;
839
840 // Implement transition to read and write call log.
841 bool hasReadContactsPermission = false;
842 bool hasWriteContactsPermission = false;
843 bool hasReadCallLogPermission = false;
844 bool hasWriteCallLogPermission = false;
845
Adam Lesinskie47fd122014-08-15 22:25:36 -0700846 // If an app declares itself as multiArch, we report the
847 // native libraries differently.
848 bool hasMultiArch = false;
849
Adam Lesinski282e1812014-01-23 18:17:42 -0800850 // This next group of variables is used to implement a group of
851 // backward-compatibility heuristics necessitated by the addition of
852 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
853 // heuristic is "if an app requests a permission but doesn't explicitly
854 // request the corresponding <uses-feature>, presume it's there anyway".
Adam Lesinski2c72b682014-06-24 09:56:01 -0700855
Adam Lesinski282e1812014-01-23 18:17:42 -0800856 // 2.2 also added some other features that apps can request, but that
857 // have no corresponding permission, so we cannot implement any
858 // back-compatibility heuristic for them. The below are thus unnecessary
859 // (but are retained here for documentary purposes.)
860 //bool specCompassFeature = false;
861 //bool specAccelerometerFeature = false;
862 //bool specProximityFeature = false;
863 //bool specAmbientLightFeature = false;
864 //bool specLiveWallpaperFeature = false;
865
866 int targetSdk = 0;
867 int smallScreen = 1;
868 int normalScreen = 1;
869 int largeScreen = 1;
870 int xlargeScreen = 1;
871 int anyDensity = 1;
872 int requiresSmallestWidthDp = 0;
873 int compatibleWidthLimitDp = 0;
874 int largestWidthLimitDp = 0;
875 String8 pkg;
876 String8 activityName;
877 String8 activityLabel;
878 String8 activityIcon;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800879 String8 activityBanner;
Adam Lesinski282e1812014-01-23 18:17:42 -0800880 String8 receiverName;
881 String8 serviceName;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700882 Vector<String8> supportedInput;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700883
884 FeatureGroup commonFeatures;
885 Vector<FeatureGroup> featureGroups;
886 KeyedVector<String8, ImpliedFeature> impliedFeatures;
887
Adam Lesinski282e1812014-01-23 18:17:42 -0800888 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
889 if (code == ResXMLTree::END_TAG) {
890 depth--;
891 if (depth < 2) {
Michael Wrightec4fdec2013-09-06 16:50:52 -0700892 if (withinSupportsInput && !supportedInput.isEmpty()) {
893 printf("supports-input: '");
894 const size_t N = supportedInput.size();
895 for (size_t i=0; i<N; i++) {
Maurice Chu2675f762013-10-22 17:33:11 -0700896 printf("%s", ResTable::normalizeForOutput(
897 supportedInput[i].string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -0700898 if (i != N - 1) {
899 printf("' '");
900 } else {
901 printf("'\n");
902 }
903 }
904 supportedInput.clear();
905 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800906 withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700907 withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700908 withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800909 } else if (depth < 3) {
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800910 if (withinActivity && isMainActivity) {
Maurice Chu2675f762013-10-22 17:33:11 -0700911 String8 aName(getComponentName(pkg, activityName));
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800912 if (isLauncherActivity) {
913 printf("launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800914 if (aName.length() > 0) {
915 printf(" name='%s' ",
916 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800917 }
918 printf(" label='%s' icon='%s'\n",
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800919 ResTable::normalizeForOutput(activityLabel.string()).string(),
920 ResTable::normalizeForOutput(activityIcon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800921 }
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800922 if (isLeanbackLauncherActivity) {
923 printf("leanback-launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800924 if (aName.length() > 0) {
925 printf(" name='%s' ",
926 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800927 }
928 printf(" label='%s' icon='%s' banner='%s'\n",
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800929 ResTable::normalizeForOutput(activityLabel.string()).string(),
930 ResTable::normalizeForOutput(activityIcon.string()).string(),
931 ResTable::normalizeForOutput(activityBanner.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800932 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800933 }
934 if (!hasIntentFilter) {
935 hasOtherActivities |= withinActivity;
936 hasOtherReceivers |= withinReceiver;
937 hasOtherServices |= withinService;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700938 } else {
939 if (withinService) {
940 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
941 hasBindNfcServicePermission);
942 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
943 hasBindNfcServicePermission);
944 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800945 }
946 withinActivity = false;
947 withinService = false;
948 withinReceiver = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700949 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800950 hasIntentFilter = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800951 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800952 } else if (depth < 4) {
953 if (withinIntentFilter) {
954 if (withinActivity) {
955 hasMainActivity |= actMainActivity;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700956 hasLauncher |= catLauncher;
957 hasCameraActivity |= actCamera;
958 hasCameraSecureActivity |= actCameraSecure;
959 hasOtherActivities |= !actMainActivity && !actCamera && !actCameraSecure;
Adam Lesinski282e1812014-01-23 18:17:42 -0800960 } else if (withinReceiver) {
961 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700962 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
963 hasBindDeviceAdminPermission);
964 hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled);
Adam Lesinski282e1812014-01-23 18:17:42 -0800965 } else if (withinService) {
966 hasImeService |= actImeService;
967 hasWallpaperService |= actWallpaperService;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700968 hasAccessibilityService |= (actAccessibilityService &&
969 hasBindAccessibilityServicePermission);
970 hasPrintService |= (actPrintService && hasBindPrintServicePermission);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700971 hasNotificationListenerService |= actNotificationListenerService &&
972 hasBindNotificationListenerServicePermission;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400973 hasDreamService |= actDreamService && hasBindDreamServicePermission;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700974 hasOtherServices |= (!actImeService && !actWallpaperService &&
Adam Lesinski94fc9122013-09-30 17:16:09 -0700975 !actAccessibilityService && !actPrintService &&
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700976 !actHostApduService && !actOffHostApduService &&
977 !actNotificationListenerService);
978 } else if (withinProvider) {
979 hasDocumentsProvider |= actDocumentsProvider && hasRequiredSafAttributes;
Adam Lesinski282e1812014-01-23 18:17:42 -0800980 }
981 }
982 withinIntentFilter = false;
983 }
984 continue;
985 }
986 if (code != ResXMLTree::START_TAG) {
987 continue;
988 }
989 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700990
991 const char16_t* ctag16 = tree.getElementName(&len);
992 if (ctag16 == NULL) {
993 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
994 goto bail;
995 }
996 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800997 //printf("Depth %d, %s\n", depth, tag.string());
998 if (depth == 1) {
999 if (tag != "manifest") {
1000 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
1001 goto bail;
1002 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001003 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -07001004 printf("package: name='%s' ",
1005 ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001006 int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR,
1007 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001008 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001009 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n",
1010 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001011 goto bail;
1012 }
1013 if (versionCode > 0) {
1014 printf("versionCode='%d' ", versionCode);
1015 } else {
1016 printf("versionCode='' ");
1017 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001018 String8 versionName = AaptXml::getResolvedAttribute(res, tree,
1019 VERSION_NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001020 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001021 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n",
1022 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001023 goto bail;
1024 }
Adam Lesinski25d35a92014-08-11 09:41:56 -07001025 printf("versionName='%s'",
Maurice Chu2675f762013-10-22 17:33:11 -07001026 ResTable::normalizeForOutput(versionName.string()).string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001027
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001028 String8 splitName = AaptXml::getAttribute(tree, NULL, "split");
Adam Lesinski25d35a92014-08-11 09:41:56 -07001029 if (!splitName.isEmpty()) {
1030 printf(" split='%s'", ResTable::normalizeForOutput(
1031 splitName.string()).string());
1032 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001033
Adam Lesinski5283fab2014-08-29 11:23:55 -07001034 String8 platformVersionName = AaptXml::getAttribute(tree, NULL,
1035 "platformBuildVersionName");
1036 printf(" platformBuildVersionName='%s'", platformVersionName.string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001037 printf("\n");
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001038
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001039 int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
1040 INSTALL_LOCATION_ATTR, &error);
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001041 if (error != "") {
1042 fprintf(stderr, "ERROR getting 'android:installLocation' attribute: %s\n",
1043 error.string());
1044 goto bail;
1045 }
1046
1047 if (installLocation >= 0) {
1048 printf("install-location:'");
1049 switch (installLocation) {
1050 case 0:
1051 printf("auto");
1052 break;
1053 case 1:
1054 printf("internalOnly");
1055 break;
1056 case 2:
1057 printf("preferExternal");
1058 break;
1059 default:
1060 fprintf(stderr, "Invalid installLocation %d\n", installLocation);
1061 goto bail;
1062 }
1063 printf("'\n");
1064 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001065 } else if (depth == 2) {
1066 withinApplication = false;
1067 if (tag == "application") {
1068 withinApplication = true;
1069
1070 String8 label;
1071 const size_t NL = locales.size();
1072 for (size_t i=0; i<NL; i++) {
1073 const char* localeStr = locales[i].string();
1074 assets.setLocale(localeStr != NULL ? localeStr : "");
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001075 String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1076 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001077 if (llabel != "") {
1078 if (localeStr == NULL || strlen(localeStr) == 0) {
1079 label = llabel;
Maurice Chu2675f762013-10-22 17:33:11 -07001080 printf("application-label:'%s'\n",
1081 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001082 } else {
1083 if (label == "") {
1084 label = llabel;
1085 }
1086 printf("application-label-%s:'%s'\n", localeStr,
Maurice Chu2675f762013-10-22 17:33:11 -07001087 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001088 }
1089 }
1090 }
1091
1092 ResTable_config tmpConfig = config;
1093 const size_t ND = densities.size();
1094 for (size_t i=0; i<ND; i++) {
1095 tmpConfig.density = densities[i];
1096 assets.setConfiguration(tmpConfig);
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001097 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1098 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001099 if (icon != "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001100 printf("application-icon-%d:'%s'\n", densities[i],
1101 ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001102 }
1103 }
1104 assets.setConfiguration(config);
1105
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001106 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001107 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001108 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1109 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001110 goto bail;
1111 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001112 int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0,
1113 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001114 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001115 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n",
1116 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001117 goto bail;
1118 }
Maurice Chu2675f762013-10-22 17:33:11 -07001119 printf("application: label='%s' ",
1120 ResTable::normalizeForOutput(label.string()).string());
1121 printf("icon='%s'\n", ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001122 if (testOnly != 0) {
1123 printf("testOnly='%d'\n", testOnly);
1124 }
1125
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001126 int32_t debuggable = AaptXml::getResolvedIntegerAttribute(res, tree,
1127 DEBUGGABLE_ATTR, 0, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001128 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001129 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n",
1130 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001131 goto bail;
1132 }
1133 if (debuggable != 0) {
1134 printf("application-debuggable\n");
1135 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07001136
1137 // We must search by name because the multiArch flag hasn't been API
1138 // frozen yet.
1139 int32_t multiArchIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
1140 "multiArch");
1141 if (multiArchIndex >= 0) {
1142 Res_value value;
1143 if (tree.getAttributeValue(multiArchIndex, &value) != NO_ERROR) {
1144 if (value.dataType >= Res_value::TYPE_FIRST_INT &&
1145 value.dataType <= Res_value::TYPE_LAST_INT) {
1146 hasMultiArch = value.data;
1147 }
1148 }
1149 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001150 } else if (tag == "uses-sdk") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001151 int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001152 if (error != "") {
1153 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001154 String8 name = AaptXml::getResolvedAttribute(res, tree,
1155 MIN_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001156 if (error != "") {
1157 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
1158 error.string());
1159 goto bail;
1160 }
1161 if (name == "Donut") targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001162 printf("sdkVersion:'%s'\n",
1163 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001164 } else if (code != -1) {
1165 targetSdk = code;
1166 printf("sdkVersion:'%d'\n", code);
1167 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001168 code = AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -08001169 if (code != -1) {
1170 printf("maxSdkVersion:'%d'\n", code);
1171 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001172 code = AaptXml::getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001173 if (error != "") {
1174 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001175 String8 name = AaptXml::getResolvedAttribute(res, tree,
1176 TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001177 if (error != "") {
1178 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
1179 error.string());
1180 goto bail;
1181 }
1182 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001183 printf("targetSdkVersion:'%s'\n",
1184 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001185 } else if (code != -1) {
1186 if (targetSdk < code) {
1187 targetSdk = code;
1188 }
1189 printf("targetSdkVersion:'%d'\n", code);
1190 }
1191 } else if (tag == "uses-configuration") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001192 int32_t reqTouchScreen = AaptXml::getIntegerAttribute(tree,
1193 REQ_TOUCH_SCREEN_ATTR, 0);
1194 int32_t reqKeyboardType = AaptXml::getIntegerAttribute(tree,
1195 REQ_KEYBOARD_TYPE_ATTR, 0);
1196 int32_t reqHardKeyboard = AaptXml::getIntegerAttribute(tree,
1197 REQ_HARD_KEYBOARD_ATTR, 0);
1198 int32_t reqNavigation = AaptXml::getIntegerAttribute(tree,
1199 REQ_NAVIGATION_ATTR, 0);
1200 int32_t reqFiveWayNav = AaptXml::getIntegerAttribute(tree,
1201 REQ_FIVE_WAY_NAV_ATTR, 0);
Adam Lesinski282e1812014-01-23 18:17:42 -08001202 printf("uses-configuration:");
1203 if (reqTouchScreen != 0) {
1204 printf(" reqTouchScreen='%d'", reqTouchScreen);
1205 }
1206 if (reqKeyboardType != 0) {
1207 printf(" reqKeyboardType='%d'", reqKeyboardType);
1208 }
1209 if (reqHardKeyboard != 0) {
1210 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1211 }
1212 if (reqNavigation != 0) {
1213 printf(" reqNavigation='%d'", reqNavigation);
1214 }
1215 if (reqFiveWayNav != 0) {
1216 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1217 }
1218 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001219 } else if (tag == "supports-input") {
1220 withinSupportsInput = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001221 } else if (tag == "supports-screens") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001222 smallScreen = AaptXml::getIntegerAttribute(tree,
1223 SMALL_SCREEN_ATTR, 1);
1224 normalScreen = AaptXml::getIntegerAttribute(tree,
1225 NORMAL_SCREEN_ATTR, 1);
1226 largeScreen = AaptXml::getIntegerAttribute(tree,
1227 LARGE_SCREEN_ATTR, 1);
1228 xlargeScreen = AaptXml::getIntegerAttribute(tree,
1229 XLARGE_SCREEN_ATTR, 1);
1230 anyDensity = AaptXml::getIntegerAttribute(tree,
1231 ANY_DENSITY_ATTR, 1);
1232 requiresSmallestWidthDp = AaptXml::getIntegerAttribute(tree,
1233 REQUIRES_SMALLEST_WIDTH_DP_ATTR, 0);
1234 compatibleWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1235 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, 0);
1236 largestWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1237 LARGEST_WIDTH_LIMIT_DP_ATTR, 0);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001238 } else if (tag == "feature-group") {
1239 withinFeatureGroup = true;
1240 FeatureGroup group;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001241 group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001242 if (error != "") {
1243 fprintf(stderr, "ERROR getting 'android:label' attribute:"
1244 " %s\n", error.string());
1245 goto bail;
1246 }
1247 featureGroups.add(group);
1248
Adam Lesinski282e1812014-01-23 18:17:42 -08001249 } else if (tag == "uses-feature") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001250 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001251 if (name != "" && error == "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001252 int req = AaptXml::getIntegerAttribute(tree,
1253 REQUIRED_ATTR, 1);
Adam Lesinski282e1812014-01-23 18:17:42 -08001254
Adam Lesinski2c72b682014-06-24 09:56:01 -07001255 commonFeatures.features.add(name, req);
1256 if (req) {
1257 addParentFeatures(&commonFeatures, name);
Adam Lesinski282e1812014-01-23 18:17:42 -08001258 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001259 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001260 int vers = AaptXml::getIntegerAttribute(tree,
Adam Lesinski282e1812014-01-23 18:17:42 -08001261 GL_ES_VERSION_ATTR, &error);
1262 if (error == "") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001263 if (vers > commonFeatures.openGLESVersion) {
1264 commonFeatures.openGLESVersion = vers;
1265 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001266 }
1267 }
1268 } else if (tag == "uses-permission") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001269 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001270 if (name != "" && error == "") {
1271 if (name == "android.permission.CAMERA") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001272 addImpliedFeature(&impliedFeatures, "android.hardware.camera",
Adam Lesinski2c72b682014-06-24 09:56:01 -07001273 String8::format("requested %s permission", name.string())
1274 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001275 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001276 addImpliedFeature(&impliedFeatures, "android.hardware.location.gps",
1277 String8::format("requested %s permission", name.string())
1278 .string());
1279 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1280 String8::format("requested %s permission", name.string())
1281 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001282 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001283 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1284 String8::format("requested %s permission", name.string())
1285 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001286 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001287 addImpliedFeature(&impliedFeatures, "android.hardware.location.network",
1288 String8::format("requested %s permission", name.string())
1289 .string());
1290 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1291 String8::format("requested %s permission", name.string())
1292 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001293 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
1294 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001295 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1296 String8::format("requested %s permission", name.string())
1297 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001298 } else if (name == "android.permission.BLUETOOTH" ||
1299 name == "android.permission.BLUETOOTH_ADMIN") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001300 if (targetSdk > 4) {
1301 addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
1302 String8::format("requested %s permission", name.string())
1303 .string());
1304 addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
1305 "targetSdkVersion > 4");
1306 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001307 } else if (name == "android.permission.RECORD_AUDIO") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001308 addImpliedFeature(&impliedFeatures, "android.hardware.microphone",
1309 String8::format("requested %s permission", name.string())
1310 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001311 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1312 name == "android.permission.CHANGE_WIFI_STATE" ||
1313 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001314 addImpliedFeature(&impliedFeatures, "android.hardware.wifi",
1315 String8::format("requested %s permission", name.string())
1316 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001317 } else if (name == "android.permission.CALL_PHONE" ||
1318 name == "android.permission.CALL_PRIVILEGED" ||
1319 name == "android.permission.MODIFY_PHONE_STATE" ||
1320 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1321 name == "android.permission.READ_SMS" ||
1322 name == "android.permission.RECEIVE_SMS" ||
1323 name == "android.permission.RECEIVE_MMS" ||
1324 name == "android.permission.RECEIVE_WAP_PUSH" ||
1325 name == "android.permission.SEND_SMS" ||
1326 name == "android.permission.WRITE_APN_SETTINGS" ||
1327 name == "android.permission.WRITE_SMS") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001328 addImpliedFeature(&impliedFeatures, "android.hardware.telephony",
1329 String8("requested a telephony permission").string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001330 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1331 hasWriteExternalStoragePermission = true;
1332 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1333 hasReadExternalStoragePermission = true;
1334 } else if (name == "android.permission.READ_PHONE_STATE") {
1335 hasReadPhoneStatePermission = true;
1336 } else if (name == "android.permission.READ_CONTACTS") {
1337 hasReadContactsPermission = true;
1338 } else if (name == "android.permission.WRITE_CONTACTS") {
1339 hasWriteContactsPermission = true;
1340 } else if (name == "android.permission.READ_CALL_LOG") {
1341 hasReadCallLogPermission = true;
1342 } else if (name == "android.permission.WRITE_CALL_LOG") {
1343 hasWriteCallLogPermission = true;
1344 }
Adam Lesinski58f1f362013-11-12 12:59:08 -08001345
1346 printUsesPermission(name,
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001347 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
1348 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
Adam Lesinski58f1f362013-11-12 12:59:08 -08001349 } else {
Adam Lesinski282e1812014-01-23 18:17:42 -08001350 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1351 error.string());
1352 goto bail;
1353 }
1354 } else if (tag == "uses-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001355 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001356 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001357 printf("uses-package:'%s'\n",
1358 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001359 } else {
1360 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1361 error.string());
1362 goto bail;
1363 }
1364 } else if (tag == "original-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001365 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001366 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001367 printf("original-package:'%s'\n",
1368 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001369 } else {
1370 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1371 error.string());
1372 goto bail;
1373 }
1374 } else if (tag == "supports-gl-texture") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001375 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001376 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001377 printf("supports-gl-texture:'%s'\n",
1378 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001379 } else {
1380 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1381 error.string());
1382 goto bail;
1383 }
1384 } else if (tag == "compatible-screens") {
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001385 printCompatibleScreens(tree, &error);
1386 if (error != "") {
1387 fprintf(stderr, "ERROR getting compatible screens: %s\n",
1388 error.string());
1389 goto bail;
1390 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001391 depth--;
1392 } else if (tag == "package-verifier") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001393 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001394 if (name != "" && error == "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001395 String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001396 if (publicKey != "" && error == "") {
1397 printf("package-verifier: name='%s' publicKey='%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001398 ResTable::normalizeForOutput(name.string()).string(),
1399 ResTable::normalizeForOutput(publicKey.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001400 }
1401 }
1402 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001403 } else if (depth == 3) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001404 withinActivity = false;
1405 withinReceiver = false;
1406 withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001407 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001408 hasIntentFilter = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001409 hasMetaHostPaymentCategory = false;
1410 hasMetaOffHostPaymentCategory = false;
1411 hasBindDeviceAdminPermission = false;
1412 hasBindInputMethodPermission = false;
1413 hasBindAccessibilityServicePermission = false;
1414 hasBindPrintServicePermission = false;
1415 hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001416 hasRequiredSafAttributes = false;
1417 hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001418 hasBindDreamServicePermission = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001419 if (withinApplication) {
1420 if(tag == "activity") {
1421 withinActivity = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001422 activityName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001423 if (error != "") {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001424 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1425 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001426 goto bail;
1427 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001428
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001429 activityLabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1430 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001431 if (error != "") {
1432 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
1433 error.string());
1434 goto bail;
1435 }
1436
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001437 activityIcon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1438 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001439 if (error != "") {
1440 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1441 error.string());
1442 goto bail;
1443 }
1444
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001445 activityBanner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1446 &error);
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001447 if (error != "") {
1448 fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
1449 error.string());
1450 goto bail;
1451 }
1452
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001453 int32_t orien = AaptXml::getResolvedIntegerAttribute(res, tree,
Michael Wrightec4fdec2013-09-06 16:50:52 -07001454 SCREEN_ORIENTATION_ATTR, &error);
1455 if (error == "") {
1456 if (orien == 0 || orien == 6 || orien == 8) {
1457 // Requests landscape, sensorLandscape, or reverseLandscape.
Adam Lesinski2c72b682014-06-24 09:56:01 -07001458 addImpliedFeature(&impliedFeatures, "android.hardware.screen.landscape",
1459 "one or more activities have specified a landscape orientation");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001460 } else if (orien == 1 || orien == 7 || orien == 9) {
1461 // Requests portrait, sensorPortrait, or reversePortrait.
Adam Lesinski2c72b682014-06-24 09:56:01 -07001462 addImpliedFeature(&impliedFeatures, "android.hardware.screen.portrait",
1463 "one or more activities have specified a portrait orientation");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001464 }
1465 }
1466 } else if (tag == "uses-library") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001467 String8 libraryName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001468 if (error != "") {
1469 fprintf(stderr,
1470 "ERROR getting 'android:name' attribute for uses-library"
1471 " %s\n", error.string());
1472 goto bail;
1473 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001474 int req = AaptXml::getIntegerAttribute(tree,
1475 REQUIRED_ATTR, 1);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001476 printf("uses-library%s:'%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001477 req ? "" : "-not-required", ResTable::normalizeForOutput(
1478 libraryName.string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001479 } else if (tag == "receiver") {
1480 withinReceiver = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001481 receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001482
1483 if (error != "") {
1484 fprintf(stderr,
1485 "ERROR getting 'android:name' attribute for receiver:"
1486 " %s\n", error.string());
1487 goto bail;
1488 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001489
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001490 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1491 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001492 if (error == "") {
1493 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1494 hasBindDeviceAdminPermission = true;
1495 }
1496 } else {
1497 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1498 " receiver '%s': %s\n", receiverName.string(), error.string());
1499 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001500 } else if (tag == "service") {
1501 withinService = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001502 serviceName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001503
1504 if (error != "") {
1505 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1506 "service:%s\n", error.string());
1507 goto bail;
1508 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001509
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001510 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1511 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001512 if (error == "") {
1513 if (permission == "android.permission.BIND_INPUT_METHOD") {
1514 hasBindInputMethodPermission = true;
1515 } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") {
1516 hasBindAccessibilityServicePermission = true;
1517 } else if (permission == "android.permission.BIND_PRINT_SERVICE") {
1518 hasBindPrintServicePermission = true;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001519 } else if (permission == "android.permission.BIND_NFC_SERVICE") {
1520 hasBindNfcServicePermission = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001521 } else if (permission == "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
1522 hasBindNotificationListenerServicePermission = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001523 } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
1524 hasBindDreamServicePermission = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001525 }
1526 } else {
1527 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1528 " service '%s': %s\n", serviceName.string(), error.string());
1529 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001530 } else if (tag == "provider") {
1531 withinProvider = true;
1532
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001533 bool exported = AaptXml::getResolvedIntegerAttribute(res, tree,
1534 EXPORTED_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001535 if (error != "") {
1536 fprintf(stderr, "ERROR getting 'android:exported' attribute for provider:"
1537 " %s\n", error.string());
1538 goto bail;
1539 }
1540
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001541 bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute(
1542 res, tree, GRANT_URI_PERMISSIONS_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001543 if (error != "") {
1544 fprintf(stderr, "ERROR getting 'android:grantUriPermissions' attribute for provider:"
1545 " %s\n", error.string());
1546 goto bail;
1547 }
1548
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001549 String8 permission = AaptXml::getResolvedAttribute(res, tree,
1550 PERMISSION_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001551 if (error != "") {
1552 fprintf(stderr, "ERROR getting 'android:permission' attribute for provider:"
1553 " %s\n", error.string());
1554 goto bail;
1555 }
1556
1557 hasRequiredSafAttributes |= exported && grantUriPermissions &&
1558 permission == "android.permission.MANAGE_DOCUMENTS";
1559
Michael Wrightec4fdec2013-09-06 16:50:52 -07001560 } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001561 String8 metaDataName = AaptXml::getResolvedAttribute(res, tree,
1562 NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001563 if (error != "") {
1564 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1565 "meta-data:%s\n", error.string());
1566 goto bail;
1567 }
Maurice Chu2675f762013-10-22 17:33:11 -07001568 printf("meta-data: name='%s' ",
1569 ResTable::normalizeForOutput(metaDataName.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001570 printResolvedResourceAttribute(res, tree, VALUE_ATTR, String8("value"),
Maurice Chu76327312013-10-16 18:28:46 -07001571 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001572 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001573 // Try looking for a RESOURCE_ATTR
1574 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001575 printResolvedResourceAttribute(res, tree, RESOURCE_ATTR,
Maurice Chu76327312013-10-16 18:28:46 -07001576 String8("resource"), &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001577 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001578 fprintf(stderr, "ERROR getting 'android:value' or "
1579 "'android:resource' attribute for "
1580 "meta-data:%s\n", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001581 goto bail;
1582 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001583 }
Maurice Chu76327312013-10-16 18:28:46 -07001584 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001585 } else if (withinSupportsInput && tag == "input-type") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001586 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001587 if (name != "" && error == "") {
1588 supportedInput.add(name);
1589 } else {
1590 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1591 error.string());
1592 goto bail;
1593 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001594 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001595 } else if (withinFeatureGroup && tag == "uses-feature") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001596 FeatureGroup& top = featureGroups.editTop();
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001597
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001598 String8 name = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001599 if (name != "" && error == "") {
Adam Lesinskid3edfde2014-08-08 17:32:44 -07001600 top.features.add(name, true);
1601 addParentFeatures(&top, name);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001602 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001603 int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR,
1604 &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001605 if (error == "") {
1606 if (vers > top.openGLESVersion) {
1607 top.openGLESVersion = vers;
1608 }
1609 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001610 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001611 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001612 } else if (depth == 4) {
1613 if (tag == "intent-filter") {
1614 hasIntentFilter = true;
1615 withinIntentFilter = true;
1616 actMainActivity = false;
1617 actWidgetReceivers = false;
1618 actImeService = false;
1619 actWallpaperService = false;
1620 actAccessibilityService = false;
1621 actPrintService = false;
1622 actDeviceAdminEnabled = false;
1623 actHostApduService = false;
1624 actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001625 actDocumentsProvider = false;
1626 actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001627 actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001628 actCamera = false;
1629 actCameraSecure = false;
1630 catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001631 } else if (withinService && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001632 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001633 if (error != "") {
1634 fprintf(stderr, "ERROR getting 'android:name' attribute for"
1635 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1636 goto bail;
1637 }
1638
1639 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1640 name == "android.nfc.cardemulation.off_host_apdu_service") {
1641 bool offHost = true;
1642 if (name == "android.nfc.cardemulation.host_apdu_service") {
1643 offHost = false;
1644 }
1645
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001646 String8 xmlPath = AaptXml::getResolvedAttribute(res, tree,
1647 RESOURCE_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001648 if (error != "") {
1649 fprintf(stderr, "ERROR getting 'android:resource' attribute for"
1650 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1651 goto bail;
1652 }
1653
1654 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1655 offHost, &error);
1656 if (error != "") {
1657 fprintf(stderr, "ERROR getting AID category for service '%s'\n",
1658 serviceName.string());
1659 goto bail;
1660 }
1661
1662 const size_t catLen = categories.size();
1663 for (size_t i = 0; i < catLen; i++) {
1664 bool paymentCategory = (categories[i] == "payment");
1665 if (offHost) {
1666 hasMetaOffHostPaymentCategory |= paymentCategory;
1667 } else {
1668 hasMetaHostPaymentCategory |= paymentCategory;
1669 }
1670 }
1671 }
1672 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001673 } else if ((depth == 5) && withinIntentFilter) {
1674 String8 action;
1675 if (tag == "action") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001676 action = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001677 if (error != "") {
1678 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1679 error.string());
1680 goto bail;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001681 }
1682
Adam Lesinskia5018c92013-09-30 16:23:15 -07001683 if (withinActivity) {
1684 if (action == "android.intent.action.MAIN") {
1685 isMainActivity = true;
1686 actMainActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001687 } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
1688 action == "android.media.action.VIDEO_CAMERA") {
1689 actCamera = true;
1690 } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
1691 actCameraSecure = true;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001692 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001693 } else if (withinReceiver) {
1694 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1695 actWidgetReceivers = true;
1696 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1697 actDeviceAdminEnabled = true;
1698 }
1699 } else if (withinService) {
1700 if (action == "android.view.InputMethod") {
1701 actImeService = true;
1702 } else if (action == "android.service.wallpaper.WallpaperService") {
1703 actWallpaperService = true;
1704 } else if (action == "android.accessibilityservice.AccessibilityService") {
1705 actAccessibilityService = true;
1706 } else if (action == "android.printservice.PrintService") {
1707 actPrintService = true;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001708 } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
1709 actHostApduService = true;
1710 } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
1711 actOffHostApduService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001712 } else if (action == "android.service.notification.NotificationListenerService") {
1713 actNotificationListenerService = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001714 } else if (action == "android.service.dreams.DreamService") {
1715 actDreamService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001716 }
1717 } else if (withinProvider) {
1718 if (action == "android.content.action.DOCUMENTS_PROVIDER") {
1719 actDocumentsProvider = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001720 }
1721 }
1722 if (action == "android.intent.action.SEARCH") {
1723 isSearchable = true;
1724 }
1725 }
1726
1727 if (tag == "category") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001728 String8 category = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001729 if (error != "") {
1730 fprintf(stderr, "ERROR getting 'name' attribute: %s\n",
1731 error.string());
1732 goto bail;
1733 }
1734 if (withinActivity) {
1735 if (category == "android.intent.category.LAUNCHER") {
1736 isLauncherActivity = true;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001737 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
1738 isLeanbackLauncherActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001739 } else if (category == "android.intent.category.HOME") {
1740 catLauncher = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001741 }
1742 }
1743 }
1744 }
1745 }
1746
1747 // Pre-1.6 implicitly granted permission compatibility logic
1748 if (targetSdk < 4) {
1749 if (!hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001750 printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
1751 printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
1752 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001753 hasWriteExternalStoragePermission = true;
1754 }
1755 if (!hasReadPhoneStatePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001756 printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
1757 printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
1758 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001759 }
1760 }
1761
1762 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1763 // force them to always take READ_EXTERNAL_STORAGE as well. We always
1764 // do this (regardless of target API version) because we can't have
1765 // an app with write permission but not read permission.
1766 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001767 printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"));
1768 printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
1769 String8("requested WRITE_EXTERNAL_STORAGE"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001770 }
1771
1772 // Pre-JellyBean call log permission compatibility.
1773 if (targetSdk < 16) {
1774 if (!hasReadCallLogPermission && hasReadContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001775 printUsesPermission(String8("android.permission.READ_CALL_LOG"));
1776 printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
1777 String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001778 }
1779 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001780 printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
1781 printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
1782 String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001783 }
1784 }
1785
Adam Lesinski2c72b682014-06-24 09:56:01 -07001786 addImpliedFeature(&impliedFeatures, "android.hardware.touchscreen",
1787 "default feature for all apps");
1788
1789 const size_t numFeatureGroups = featureGroups.size();
1790 if (numFeatureGroups == 0) {
1791 // If no <feature-group> tags were defined, apply auto-implied features.
1792 printFeatureGroup(commonFeatures, &impliedFeatures);
1793
1794 } else {
1795 // <feature-group> tags are defined, so we ignore implied features and
1796 for (size_t i = 0; i < numFeatureGroups; i++) {
1797 FeatureGroup& grp = featureGroups.editItemAt(i);
1798
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001799 if (commonFeatures.openGLESVersion > grp.openGLESVersion) {
1800 grp.openGLESVersion = commonFeatures.openGLESVersion;
1801 }
1802
Adam Lesinski2c72b682014-06-24 09:56:01 -07001803 // Merge the features defined in the top level (not inside a <feature-group>)
1804 // with this feature group.
1805 const size_t numCommonFeatures = commonFeatures.features.size();
1806 for (size_t j = 0; j < numCommonFeatures; j++) {
1807 if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001808 grp.features.add(commonFeatures.features.keyAt(j),
1809 commonFeatures.features[j]);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001810 }
1811 }
1812
1813 if (!grp.features.isEmpty()) {
1814 printFeatureGroup(grp);
Adam Lesinski282e1812014-01-23 18:17:42 -08001815 }
1816 }
1817 }
1818
Adam Lesinski282e1812014-01-23 18:17:42 -08001819
Adam Lesinski282e1812014-01-23 18:17:42 -08001820 if (hasWidgetReceivers) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001821 printComponentPresence("app-widget");
Adam Lesinski282e1812014-01-23 18:17:42 -08001822 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001823 if (hasDeviceAdminReceiver) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001824 printComponentPresence("device-admin");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001825 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001826 if (hasImeService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001827 printComponentPresence("ime");
Adam Lesinski282e1812014-01-23 18:17:42 -08001828 }
1829 if (hasWallpaperService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001830 printComponentPresence("wallpaper");
Adam Lesinski282e1812014-01-23 18:17:42 -08001831 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001832 if (hasAccessibilityService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001833 printComponentPresence("accessibility");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001834 }
1835 if (hasPrintService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001836 printComponentPresence("print-service");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001837 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001838 if (hasPaymentService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001839 printComponentPresence("payment");
1840 }
1841 if (isSearchable) {
1842 printComponentPresence("search");
1843 }
1844 if (hasDocumentsProvider) {
1845 printComponentPresence("document-provider");
1846 }
1847 if (hasLauncher) {
1848 printComponentPresence("launcher");
1849 }
1850 if (hasNotificationListenerService) {
1851 printComponentPresence("notification-listener");
1852 }
John Spurlockeb8d1be2014-06-25 17:46:15 -04001853 if (hasDreamService) {
1854 printComponentPresence("dream");
1855 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001856 if (hasCameraActivity) {
1857 printComponentPresence("camera");
1858 }
1859 if (hasCameraSecureActivity) {
1860 printComponentPresence("camera-secure");
1861 }
1862
1863 if (hasMainActivity) {
1864 printf("main\n");
Adam Lesinski94fc9122013-09-30 17:16:09 -07001865 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001866 if (hasOtherActivities) {
1867 printf("other-activities\n");
1868 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001869 if (hasOtherReceivers) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001870 printf("other-receivers\n");
1871 }
1872 if (hasOtherServices) {
1873 printf("other-services\n");
1874 }
1875
1876 // For modern apps, if screen size buckets haven't been specified
1877 // but the new width ranges have, then infer the buckets from them.
1878 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1879 && requiresSmallestWidthDp > 0) {
1880 int compatWidth = compatibleWidthLimitDp;
1881 if (compatWidth <= 0) {
1882 compatWidth = requiresSmallestWidthDp;
1883 }
1884 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1885 smallScreen = -1;
1886 } else {
1887 smallScreen = 0;
1888 }
1889 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1890 normalScreen = -1;
1891 } else {
1892 normalScreen = 0;
1893 }
1894 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1895 largeScreen = -1;
1896 } else {
1897 largeScreen = 0;
1898 }
1899 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1900 xlargeScreen = -1;
1901 } else {
1902 xlargeScreen = 0;
1903 }
1904 }
1905
1906 // Determine default values for any unspecified screen sizes,
1907 // based on the target SDK of the package. As of 4 (donut)
1908 // the screen size support was introduced, so all default to
1909 // enabled.
1910 if (smallScreen > 0) {
1911 smallScreen = targetSdk >= 4 ? -1 : 0;
1912 }
1913 if (normalScreen > 0) {
1914 normalScreen = -1;
1915 }
1916 if (largeScreen > 0) {
1917 largeScreen = targetSdk >= 4 ? -1 : 0;
1918 }
1919 if (xlargeScreen > 0) {
1920 // Introduced in Gingerbread.
1921 xlargeScreen = targetSdk >= 9 ? -1 : 0;
1922 }
1923 if (anyDensity > 0) {
1924 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1925 || compatibleWidthLimitDp > 0) ? -1 : 0;
1926 }
1927 printf("supports-screens:");
1928 if (smallScreen != 0) {
1929 printf(" 'small'");
1930 }
1931 if (normalScreen != 0) {
1932 printf(" 'normal'");
1933 }
1934 if (largeScreen != 0) {
1935 printf(" 'large'");
1936 }
1937 if (xlargeScreen != 0) {
1938 printf(" 'xlarge'");
1939 }
1940 printf("\n");
1941 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
1942 if (requiresSmallestWidthDp > 0) {
1943 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1944 }
1945 if (compatibleWidthLimitDp > 0) {
1946 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1947 }
1948 if (largestWidthLimitDp > 0) {
1949 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1950 }
1951
1952 printf("locales:");
1953 const size_t NL = locales.size();
1954 for (size_t i=0; i<NL; i++) {
1955 const char* localeStr = locales[i].string();
1956 if (localeStr == NULL || strlen(localeStr) == 0) {
1957 localeStr = "--_--";
1958 }
1959 printf(" '%s'", localeStr);
1960 }
1961 printf("\n");
1962
1963 printf("densities:");
1964 const size_t ND = densities.size();
1965 for (size_t i=0; i<ND; i++) {
1966 printf(" '%d'", densities[i]);
1967 }
1968 printf("\n");
1969
1970 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1971 if (dir != NULL) {
1972 if (dir->getFileCount() > 0) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07001973 SortedVector<String8> architectures;
Adam Lesinski282e1812014-01-23 18:17:42 -08001974 for (size_t i=0; i<dir->getFileCount(); i++) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07001975 architectures.add(ResTable::normalizeForOutput(
1976 dir->getFileName(i).string()));
Adam Lesinski282e1812014-01-23 18:17:42 -08001977 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07001978
1979 bool outputAltNativeCode = false;
1980 // A multiArch package is one that contains 64-bit and
1981 // 32-bit versions of native code and expects 3rd-party
1982 // apps to load these native code libraries. Since most
1983 // 64-bit systems also support 32-bit apps, the apps
1984 // loading this multiArch package's code may be either
1985 // 32-bit or 64-bit.
1986 if (hasMultiArch) {
1987 // If this is a multiArch package, report the 64-bit
1988 // version only. Then as a separate entry, report the
1989 // rest.
1990 //
1991 // If we report the 32-bit architecture, this APK will
1992 // be installed on a 32-bit device, causing a large waste
1993 // of bandwidth and disk space. This assumes that
1994 // the developer of the multiArch package has also
1995 // made a version that is 32-bit only.
1996 String8 intel64("x86_64");
1997 String8 arm64("arm64-v8a");
1998 ssize_t index = architectures.indexOf(intel64);
1999 if (index < 0) {
2000 index = architectures.indexOf(arm64);
2001 }
2002
2003 if (index >= 0) {
2004 printf("native-code: '%s'\n", architectures[index].string());
2005 architectures.removeAt(index);
2006 outputAltNativeCode = true;
2007 }
2008 }
2009
2010 const size_t archCount = architectures.size();
2011 if (archCount > 0) {
2012 if (outputAltNativeCode) {
2013 printf("alt-");
2014 }
2015 printf("native-code:");
2016 for (size_t i = 0; i < archCount; i++) {
2017 printf(" '%s'", architectures[i].string());
2018 }
2019 printf("\n");
2020 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002021 }
2022 delete dir;
2023 }
2024 } else if (strcmp("badger", option) == 0) {
2025 printf("%s", CONSOLE_DATA);
2026 } else if (strcmp("configurations", option) == 0) {
2027 Vector<ResTable_config> configs;
2028 res.getConfigurations(&configs);
2029 const size_t N = configs.size();
2030 for (size_t i=0; i<N; i++) {
2031 printf("%s\n", configs[i].toString().string());
2032 }
2033 } else {
2034 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2035 goto bail;
2036 }
2037 }
2038
2039 result = NO_ERROR;
2040
2041bail:
2042 if (asset) {
2043 delete asset;
2044 }
2045 return (result != NO_ERROR);
2046}
2047
2048
2049/*
2050 * Handle the "add" command, which wants to add files to a new or
2051 * pre-existing archive.
2052 */
2053int doAdd(Bundle* bundle)
2054{
2055 ZipFile* zip = NULL;
2056 status_t result = UNKNOWN_ERROR;
2057 const char* zipFileName;
2058
2059 if (bundle->getUpdate()) {
2060 /* avoid confusion */
2061 fprintf(stderr, "ERROR: can't use '-u' with add\n");
2062 goto bail;
2063 }
2064
2065 if (bundle->getFileSpecCount() < 1) {
2066 fprintf(stderr, "ERROR: must specify zip file name\n");
2067 goto bail;
2068 }
2069 zipFileName = bundle->getFileSpecEntry(0);
2070
2071 if (bundle->getFileSpecCount() < 2) {
2072 fprintf(stderr, "NOTE: nothing to do\n");
2073 goto bail;
2074 }
2075
2076 zip = openReadWrite(zipFileName, true);
2077 if (zip == NULL) {
2078 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2079 goto bail;
2080 }
2081
2082 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2083 const char* fileName = bundle->getFileSpecEntry(i);
2084
2085 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
2086 printf(" '%s'... (from gzip)\n", fileName);
2087 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
2088 } else {
2089 if (bundle->getJunkPath()) {
2090 String8 storageName = String8(fileName).getPathLeaf();
Maurice Chu2675f762013-10-22 17:33:11 -07002091 printf(" '%s' as '%s'...\n", fileName,
2092 ResTable::normalizeForOutput(storageName.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08002093 result = zip->add(fileName, storageName.string(),
2094 bundle->getCompressionMethod(), NULL);
2095 } else {
2096 printf(" '%s'...\n", fileName);
2097 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2098 }
2099 }
2100 if (result != NO_ERROR) {
2101 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2102 if (result == NAME_NOT_FOUND) {
2103 fprintf(stderr, ": file not found\n");
2104 } else if (result == ALREADY_EXISTS) {
2105 fprintf(stderr, ": already exists in archive\n");
2106 } else {
2107 fprintf(stderr, "\n");
2108 }
2109 goto bail;
2110 }
2111 }
2112
2113 result = NO_ERROR;
2114
2115bail:
2116 delete zip;
2117 return (result != NO_ERROR);
2118}
2119
2120
2121/*
2122 * Delete files from an existing archive.
2123 */
2124int doRemove(Bundle* bundle)
2125{
2126 ZipFile* zip = NULL;
2127 status_t result = UNKNOWN_ERROR;
2128 const char* zipFileName;
2129
2130 if (bundle->getFileSpecCount() < 1) {
2131 fprintf(stderr, "ERROR: must specify zip file name\n");
2132 goto bail;
2133 }
2134 zipFileName = bundle->getFileSpecEntry(0);
2135
2136 if (bundle->getFileSpecCount() < 2) {
2137 fprintf(stderr, "NOTE: nothing to do\n");
2138 goto bail;
2139 }
2140
2141 zip = openReadWrite(zipFileName, false);
2142 if (zip == NULL) {
2143 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2144 zipFileName);
2145 goto bail;
2146 }
2147
2148 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2149 const char* fileName = bundle->getFileSpecEntry(i);
2150 ZipEntry* entry;
2151
2152 entry = zip->getEntryByName(fileName);
2153 if (entry == NULL) {
2154 printf(" '%s' NOT FOUND\n", fileName);
2155 continue;
2156 }
2157
2158 result = zip->remove(entry);
2159
2160 if (result != NO_ERROR) {
2161 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2162 bundle->getFileSpecEntry(i), zipFileName);
2163 goto bail;
2164 }
2165 }
2166
2167 /* update the archive */
2168 zip->flush();
2169
2170bail:
2171 delete zip;
2172 return (result != NO_ERROR);
2173}
2174
Adam Lesinski3921e872014-05-13 10:56:25 -07002175static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002176 const size_t numDirs = dir->getDirs().size();
2177 for (size_t i = 0; i < numDirs; i++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002178 bool ignore = ignoreConfig;
2179 const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2180 const char* dirStr = subDir->getLeaf().string();
2181 if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2182 ignore = true;
2183 }
2184 status_t err = addResourcesToBuilder(subDir, builder, ignore);
Adam Lesinskifab50872014-04-16 14:40:42 -07002185 if (err != NO_ERROR) {
2186 return err;
2187 }
2188 }
2189
2190 const size_t numFiles = dir->getFiles().size();
2191 for (size_t i = 0; i < numFiles; i++) {
2192 sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2193 const size_t numConfigs = gp->getFiles().size();
2194 for (size_t j = 0; j < numConfigs; j++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002195 status_t err = NO_ERROR;
2196 if (ignoreConfig) {
2197 err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2198 } else {
2199 err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2200 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002201 if (err != NO_ERROR) {
2202 fprintf(stderr, "Failed to add %s (%s) to builder.\n",
2203 gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
2204 return err;
2205 }
2206 }
2207 }
2208 return NO_ERROR;
2209}
2210
2211static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2212 if (split->isBase()) {
2213 return original;
2214 }
2215
2216 String8 ext(original.getPathExtension());
2217 if (ext == String8(".apk")) {
2218 return String8::format("%s_%s%s",
2219 original.getBasePath().string(),
2220 split->getDirectorySafeName().string(),
2221 ext.string());
2222 }
2223
2224 return String8::format("%s_%s", original.string(),
2225 split->getDirectorySafeName().string());
2226}
Adam Lesinski282e1812014-01-23 18:17:42 -08002227
2228/*
2229 * Package up an asset directory and associated application files.
2230 */
2231int doPackage(Bundle* bundle)
2232{
2233 const char* outputAPKFile;
2234 int retVal = 1;
2235 status_t err;
2236 sp<AaptAssets> assets;
2237 int N;
2238 FILE* fp;
2239 String8 dependencyFile;
Adam Lesinskifab50872014-04-16 14:40:42 -07002240 sp<ApkBuilder> builder;
Adam Lesinski282e1812014-01-23 18:17:42 -08002241
Anton Krumina2ef5c02014-03-12 14:46:44 -07002242 // -c en_XA or/and ar_XB means do pseudolocalization
Adam Lesinskifab50872014-04-16 14:40:42 -07002243 sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2244 err = configFilter->parse(bundle->getConfigurations());
Adam Lesinski282e1812014-01-23 18:17:42 -08002245 if (err != NO_ERROR) {
2246 goto bail;
2247 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002248 if (configFilter->containsPseudo()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002249 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2250 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002251 if (configFilter->containsPseudoBidi()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002252 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
Adam Lesinski282e1812014-01-23 18:17:42 -08002253 }
2254
2255 N = bundle->getFileSpecCount();
2256 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
Adam Lesinski09384302014-01-22 16:07:42 -08002257 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002258 fprintf(stderr, "ERROR: no input files\n");
2259 goto bail;
2260 }
2261
2262 outputAPKFile = bundle->getOutputAPKFile();
2263
2264 // Make sure the filenames provided exist and are of the appropriate type.
2265 if (outputAPKFile) {
2266 FileType type;
2267 type = getFileType(outputAPKFile);
2268 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2269 fprintf(stderr,
2270 "ERROR: output file '%s' exists but is not regular file\n",
2271 outputAPKFile);
2272 goto bail;
2273 }
2274 }
2275
2276 // Load the assets.
2277 assets = new AaptAssets();
2278
2279 // Set up the resource gathering in assets if we're going to generate
2280 // dependency files. Every time we encounter a resource while slurping
2281 // the tree, we'll add it to these stores so we have full resource paths
2282 // to write to a dependency file.
2283 if (bundle->getGenDependencies()) {
2284 sp<FilePathStore> resPathStore = new FilePathStore;
2285 assets->setFullResPaths(resPathStore);
2286 sp<FilePathStore> assetPathStore = new FilePathStore;
2287 assets->setFullAssetPaths(assetPathStore);
2288 }
2289
2290 err = assets->slurpFromArgs(bundle);
2291 if (err < 0) {
2292 goto bail;
2293 }
2294
2295 if (bundle->getVerbose()) {
2296 assets->print(String8());
2297 }
2298
Adam Lesinskifab50872014-04-16 14:40:42 -07002299 // Create the ApkBuilder, which will collect the compiled files
2300 // to write to the final APK (or sets of APKs if we are building
2301 // a Split APK.
2302 builder = new ApkBuilder(configFilter);
2303
2304 // If we are generating a Split APK, find out which configurations to split on.
2305 if (bundle->getSplitConfigurations().size() > 0) {
2306 const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2307 const size_t numSplits = splitStrs.size();
2308 for (size_t i = 0; i < numSplits; i++) {
2309 std::set<ConfigDescription> configs;
2310 if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2311 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2312 goto bail;
2313 }
2314
2315 err = builder->createSplitForConfigs(configs);
2316 if (err != NO_ERROR) {
2317 goto bail;
2318 }
2319 }
2320 }
2321
Adam Lesinski282e1812014-01-23 18:17:42 -08002322 // If they asked for any fileAs that need to be compiled, do so.
2323 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002324 err = buildResources(bundle, assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002325 if (err != 0) {
2326 goto bail;
2327 }
2328 }
2329
2330 // At this point we've read everything and processed everything. From here
2331 // on out it's just writing output files.
2332 if (SourcePos::hasErrors()) {
2333 goto bail;
2334 }
2335
2336 // Update symbols with information about which ones are needed as Java symbols.
2337 assets->applyJavaSymbols();
2338 if (SourcePos::hasErrors()) {
2339 goto bail;
2340 }
2341
2342 // If we've been asked to generate a dependency file, do that here
2343 if (bundle->getGenDependencies()) {
2344 // If this is the packaging step, generate the dependency file next to
2345 // the output apk (e.g. bin/resources.ap_.d)
2346 if (outputAPKFile) {
2347 dependencyFile = String8(outputAPKFile);
2348 // Add the .d extension to the dependency file.
2349 dependencyFile.append(".d");
2350 } else {
2351 // Else if this is the R.java dependency generation step,
2352 // generate the dependency file in the R.java package subdirectory
2353 // e.g. gen/com/foo/app/R.java.d
2354 dependencyFile = String8(bundle->getRClassDir());
2355 dependencyFile.appendPath("R.java.d");
2356 }
2357 // Make sure we have a clean dependency file to start with
2358 fp = fopen(dependencyFile, "w");
2359 fclose(fp);
2360 }
2361
2362 // Write out R.java constants
2363 if (!assets->havePrivateSymbols()) {
2364 if (bundle->getCustomPackage() == NULL) {
2365 // Write the R.java file into the appropriate class directory
2366 // e.g. gen/com/foo/app/R.java
Adam Lesinski1e4663852014-08-15 14:47:28 -07002367 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true,
2368 bundle->getBuildSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002369 } else {
2370 const String8 customPkg(bundle->getCustomPackage());
Adam Lesinski1e4663852014-08-15 14:47:28 -07002371 err = writeResourceSymbols(bundle, assets, customPkg, true,
2372 bundle->getBuildSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002373 }
2374 if (err < 0) {
2375 goto bail;
2376 }
2377 // If we have library files, we're going to write our R.java file into
2378 // the appropriate class directory for those libraries as well.
2379 // e.g. gen/com/foo/app/lib/R.java
2380 if (bundle->getExtraPackages() != NULL) {
2381 // Split on colon
2382 String8 libs(bundle->getExtraPackages());
2383 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2384 while (packageString != NULL) {
2385 // Write the R.java file out with the correct package name
Adam Lesinski1e4663852014-08-15 14:47:28 -07002386 err = writeResourceSymbols(bundle, assets, String8(packageString), true, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002387 if (err < 0) {
2388 goto bail;
2389 }
2390 packageString = strtok(NULL, ":");
2391 }
2392 libs.unlockBuffer();
2393 }
2394 } else {
Adam Lesinski1e4663852014-08-15 14:47:28 -07002395 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002396 if (err < 0) {
2397 goto bail;
2398 }
Adam Lesinski1e4663852014-08-15 14:47:28 -07002399 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002400 if (err < 0) {
2401 goto bail;
2402 }
2403 }
2404
2405 // Write out the ProGuard file
2406 err = writeProguardFile(bundle, assets);
2407 if (err < 0) {
2408 goto bail;
2409 }
2410
2411 // Write the apk
2412 if (outputAPKFile) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002413 // Gather all resources and add them to the APK Builder. The builder will then
2414 // figure out which Split they belong in.
2415 err = addResourcesToBuilder(assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002416 if (err != NO_ERROR) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002417 goto bail;
2418 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002419
2420 const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2421 const size_t numSplits = splits.size();
2422 for (size_t i = 0; i < numSplits; i++) {
2423 const sp<ApkSplit>& split = splits[i];
2424 String8 outputPath = buildApkName(String8(outputAPKFile), split);
2425 err = writeAPK(bundle, outputPath, split);
2426 if (err != NO_ERROR) {
2427 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2428 goto bail;
2429 }
2430 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002431 }
2432
2433 // If we've been asked to generate a dependency file, we need to finish up here.
2434 // the writeResourceSymbols and writeAPK functions have already written the target
2435 // half of the dependency file, now we need to write the prerequisites. (files that
2436 // the R.java file or .ap_ file depend on)
2437 if (bundle->getGenDependencies()) {
2438 // Now that writeResourceSymbols or writeAPK has taken care of writing
2439 // the targets to our dependency file, we'll write the prereqs
2440 fp = fopen(dependencyFile, "a+");
2441 fprintf(fp, " : ");
2442 bool includeRaw = (outputAPKFile != NULL);
2443 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2444 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2445 // and therefore was not added to our pathstores during slurping
2446 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2447 fclose(fp);
2448 }
2449
2450 retVal = 0;
2451bail:
2452 if (SourcePos::hasErrors()) {
2453 SourcePos::printErrors(stderr);
2454 }
2455 return retVal;
2456}
2457
2458/*
2459 * Do PNG Crunching
2460 * PRECONDITIONS
2461 * -S flag points to a source directory containing drawable* folders
2462 * -C flag points to destination directory. The folder structure in the
2463 * source directory will be mirrored to the destination (cache) directory
2464 *
2465 * POSTCONDITIONS
2466 * Destination directory will be updated to match the PNG files in
Maurice Chu2675f762013-10-22 17:33:11 -07002467 * the source directory.
Adam Lesinski282e1812014-01-23 18:17:42 -08002468 */
2469int doCrunch(Bundle* bundle)
2470{
2471 fprintf(stdout, "Crunching PNG Files in ");
2472 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2473 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2474
2475 updatePreProcessedCache(bundle);
2476
2477 return NO_ERROR;
2478}
2479
2480/*
2481 * Do PNG Crunching on a single flag
2482 * -i points to a single png file
2483 * -o points to a single png output file
2484 */
2485int doSingleCrunch(Bundle* bundle)
2486{
2487 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2488 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2489
2490 String8 input(bundle->getSingleCrunchInputFile());
2491 String8 output(bundle->getSingleCrunchOutputFile());
2492
2493 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2494 // we can't return the status_t as it gets truncate to the lower 8 bits.
2495 return 42;
2496 }
2497
2498 return NO_ERROR;
2499}
2500
2501char CONSOLE_DATA[2925] = {
2502 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2503 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2504 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2505 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2506 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2507 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2508 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2509 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2510 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2511 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2512 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2513 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2514 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2515 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2516 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2517 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2518 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2519 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2520 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2521 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2522 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2523 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2524 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2525 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2526 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2527 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2528 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2529 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2530 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2531 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2532 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2533 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2534 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2535 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2536 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2537 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2538 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2539 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2540 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2541 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2542 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2543 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2544 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2545 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2546 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2547 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2548 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2549 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2550 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2551 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2552 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2553 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2554 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2555 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2556 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2557 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2558 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2559 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2560 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2561 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2562 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2563 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2564 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2565 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2566 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2567 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2568 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2569 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2570 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2571 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2572 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2573 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2574 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2575 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2576 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2577 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2578 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2579 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2580 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2581 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2582 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2583 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2584 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2585 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2586 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2587 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2588 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2589 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2590 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2591 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2592 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2593 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2594 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2595 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2596 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2597 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2598 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2599 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2600 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2601 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2602 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2603 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2604 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2605 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2606 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2607 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2608 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2609 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2610 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2611 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2612 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2613 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2614 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2615 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2616 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2617 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2618 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2619 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2620 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2621 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2622 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2623 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2624 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2625 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2626 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2627 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2628 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2629 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2630 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2631 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2632 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2633 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2634 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2635 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2636 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2637 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2638 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2639 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2640 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2641 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2642 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2643 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2644 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2645 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2646 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2647 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2648 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2649 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2650 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2651 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2652 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2653 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2654 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2655 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2656 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2657 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2658 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2659 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2660 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2661 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2662 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2663 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2664 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2665 };