blob: 258c7c7f964db4871136a7cb7f1c4eaa4bc9fb4e [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
Andreas Gampeb8dc7bc2014-10-01 19:07:51 -0700214#ifdef HAVE_ANDROID_OS
215 static const bool kHaveAndroidOs = true;
216#else
217 static const bool kHaveAndroidOs = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800218#endif
Andreas Gampeb8dc7bc2014-10-01 19:07:51 -0700219 const ResTable& res = assets.getResources(false);
220 if (!kHaveAndroidOs) {
221 printf("\nResource table:\n");
222 res.print(false);
223 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800224
225 Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
226 Asset::ACCESS_BUFFER);
227 if (manifestAsset == NULL) {
228 printf("\nNo AndroidManifest.xml found.\n");
229 } else {
230 printf("\nAndroid manifest:\n");
231 ResXMLTree tree;
232 tree.setTo(manifestAsset->getBuffer(true),
233 manifestAsset->getLength());
234 printXMLBlock(&tree);
235 }
236 delete manifestAsset;
237 }
238
239 result = 0;
240
241bail:
242 delete zip;
243 return result;
244}
245
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700246static void printResolvedResourceAttribute(const ResTable& resTable, const ResXMLTree& tree,
Maurice Chu76327312013-10-16 18:28:46 -0700247 uint32_t attrRes, String8 attrLabel, String8* outError)
248{
249 Res_value value;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700250 AaptXml::getResolvedResourceAttribute(resTable, tree, attrRes, &value, outError);
Maurice Chu76327312013-10-16 18:28:46 -0700251 if (*outError != "") {
252 *outError = "error print resolved resource attribute";
253 return;
254 }
255 if (value.dataType == Res_value::TYPE_STRING) {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700256 String8 result = AaptXml::getResolvedAttribute(resTable, tree, attrRes, outError);
Maurice Chu2675f762013-10-22 17:33:11 -0700257 printf("%s='%s'", attrLabel.string(),
258 ResTable::normalizeForOutput(result.string()).string());
Maurice Chu76327312013-10-16 18:28:46 -0700259 } else if (Res_value::TYPE_FIRST_INT <= value.dataType &&
260 value.dataType <= Res_value::TYPE_LAST_INT) {
261 printf("%s='%d'", attrLabel.string(), value.data);
262 } else {
263 printf("%s='0x%x'", attrLabel.string(), (int)value.data);
264 }
265}
266
Adam Lesinski282e1812014-01-23 18:17:42 -0800267// These are attribute resource constants for the platform, as found
268// in android.R.attr
269enum {
270 LABEL_ATTR = 0x01010001,
271 ICON_ATTR = 0x01010002,
272 NAME_ATTR = 0x01010003,
Adam Lesinskia5018c92013-09-30 16:23:15 -0700273 PERMISSION_ATTR = 0x01010006,
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700274 EXPORTED_ATTR = 0x01010010,
275 GRANT_URI_PERMISSIONS_ATTR = 0x0101001b,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700276 RESOURCE_ATTR = 0x01010025,
Adam Lesinski282e1812014-01-23 18:17:42 -0800277 DEBUGGABLE_ATTR = 0x0101000f,
278 VALUE_ATTR = 0x01010024,
279 VERSION_CODE_ATTR = 0x0101021b,
280 VERSION_NAME_ATTR = 0x0101021c,
281 SCREEN_ORIENTATION_ATTR = 0x0101001e,
282 MIN_SDK_VERSION_ATTR = 0x0101020c,
283 MAX_SDK_VERSION_ATTR = 0x01010271,
284 REQ_TOUCH_SCREEN_ATTR = 0x01010227,
285 REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
286 REQ_HARD_KEYBOARD_ATTR = 0x01010229,
287 REQ_NAVIGATION_ATTR = 0x0101022a,
288 REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
289 TARGET_SDK_VERSION_ATTR = 0x01010270,
290 TEST_ONLY_ATTR = 0x01010272,
291 ANY_DENSITY_ATTR = 0x0101026c,
292 GL_ES_VERSION_ATTR = 0x01010281,
293 SMALL_SCREEN_ATTR = 0x01010284,
294 NORMAL_SCREEN_ATTR = 0x01010285,
295 LARGE_SCREEN_ATTR = 0x01010286,
296 XLARGE_SCREEN_ATTR = 0x010102bf,
297 REQUIRED_ATTR = 0x0101028e,
Adam Lesinskicaf797c2014-08-22 12:56:26 -0700298 INSTALL_LOCATION_ATTR = 0x010102b7,
Adam Lesinski282e1812014-01-23 18:17:42 -0800299 SCREEN_SIZE_ATTR = 0x010102ca,
300 SCREEN_DENSITY_ATTR = 0x010102cb,
301 REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
302 COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
303 LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
304 PUBLIC_KEY_ATTR = 0x010103a6,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700305 CATEGORY_ATTR = 0x010103e8,
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800306 BANNER_ATTR = 0x10103f2,
Adam Lesinski282e1812014-01-23 18:17:42 -0800307};
308
Maurice Chu2675f762013-10-22 17:33:11 -0700309String8 getComponentName(String8 &pkgName, String8 &componentName) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800310 ssize_t idx = componentName.find(".");
311 String8 retStr(pkgName);
312 if (idx == 0) {
313 retStr += componentName;
314 } else if (idx < 0) {
315 retStr += ".";
316 retStr += componentName;
317 } else {
Maurice Chu2675f762013-10-22 17:33:11 -0700318 return componentName;
Adam Lesinski282e1812014-01-23 18:17:42 -0800319 }
Maurice Chu2675f762013-10-22 17:33:11 -0700320 return retStr;
Adam Lesinski282e1812014-01-23 18:17:42 -0800321}
322
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700323static void printCompatibleScreens(ResXMLTree& tree, String8* outError) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800324 size_t len;
325 ResXMLTree::event_code_t code;
326 int depth = 0;
327 bool first = true;
328 printf("compatible-screens:");
329 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
330 if (code == ResXMLTree::END_TAG) {
331 depth--;
332 if (depth < 0) {
333 break;
334 }
335 continue;
336 }
337 if (code != ResXMLTree::START_TAG) {
338 continue;
339 }
340 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700341 const char16_t* ctag16 = tree.getElementName(&len);
342 if (ctag16 == NULL) {
343 *outError = "failed to get XML element name (bad string pool)";
344 return;
345 }
346 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800347 if (tag == "screen") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700348 int32_t screenSize = AaptXml::getIntegerAttribute(tree,
349 SCREEN_SIZE_ATTR);
350 int32_t screenDensity = AaptXml::getIntegerAttribute(tree,
351 SCREEN_DENSITY_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -0800352 if (screenSize > 0 && screenDensity > 0) {
353 if (!first) {
354 printf(",");
355 }
356 first = false;
357 printf("'%d/%d'", screenSize, screenDensity);
358 }
359 }
360 }
361 printf("\n");
362}
363
Adam Lesinski58f1f362013-11-12 12:59:08 -0800364static void printUsesPermission(const String8& name, bool optional=false, int maxSdkVersion=-1) {
365 printf("uses-permission: name='%s'", ResTable::normalizeForOutput(name.string()).string());
366 if (maxSdkVersion != -1) {
367 printf(" maxSdkVersion='%d'", maxSdkVersion);
368 }
369 printf("\n");
370
371 if (optional) {
372 printf("optional-permission: name='%s'",
373 ResTable::normalizeForOutput(name.string()).string());
374 if (maxSdkVersion != -1) {
375 printf(" maxSdkVersion='%d'", maxSdkVersion);
376 }
377 printf("\n");
378 }
379}
380
381static void printUsesImpliedPermission(const String8& name, const String8& reason) {
382 printf("uses-implied-permission: name='%s' reason='%s'\n",
383 ResTable::normalizeForOutput(name.string()).string(),
384 ResTable::normalizeForOutput(reason.string()).string());
385}
386
Adam Lesinski94fc9122013-09-30 17:16:09 -0700387Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool offHost,
388 String8 *outError = NULL)
389{
390 Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER);
391 if (aidAsset == NULL) {
392 if (outError != NULL) *outError = "xml resource does not exist";
393 return Vector<String8>();
394 }
395
396 const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service");
397
398 bool withinApduService = false;
399 Vector<String8> categories;
400
401 String8 error;
402 ResXMLTree tree;
403 tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength());
404
405 size_t len;
406 int depth = 0;
407 ResXMLTree::event_code_t code;
408 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
409 if (code == ResXMLTree::END_TAG) {
410 depth--;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700411 const char16_t* ctag16 = tree.getElementName(&len);
412 if (ctag16 == NULL) {
413 *outError = "failed to get XML element name (bad string pool)";
414 return Vector<String8>();
415 }
416 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700417
418 if (depth == 0 && tag == serviceTagName) {
419 withinApduService = false;
420 }
421
422 } else if (code == ResXMLTree::START_TAG) {
423 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700424 const char16_t* ctag16 = tree.getElementName(&len);
425 if (ctag16 == NULL) {
426 *outError = "failed to get XML element name (bad string pool)";
427 return Vector<String8>();
428 }
429 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700430
431 if (depth == 1) {
432 if (tag == serviceTagName) {
433 withinApduService = true;
434 }
435 } else if (depth == 2 && withinApduService) {
436 if (tag == "aid-group") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700437 String8 category = AaptXml::getAttribute(tree, CATEGORY_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700438 if (error != "") {
439 if (outError != NULL) *outError = error;
440 return Vector<String8>();
441 }
442
443 categories.add(category);
444 }
445 }
446 }
447 }
448 aidAsset->close();
449 return categories;
450}
451
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700452static void printComponentPresence(const char* componentName) {
453 printf("provides-component:'%s'\n", componentName);
454}
455
Adam Lesinski2c72b682014-06-24 09:56:01 -0700456/**
457 * Represents a feature that has been automatically added due to
458 * a pre-requisite or some other reason.
459 */
460struct ImpliedFeature {
461 /**
462 * Name of the implied feature.
463 */
464 String8 name;
465
466 /**
467 * List of human-readable reasons for why this feature was implied.
468 */
469 SortedVector<String8> reasons;
470};
471
472/**
473 * Represents a <feature-group> tag in the AndroidManifest.xml
474 */
475struct FeatureGroup {
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700476 FeatureGroup() : openGLESVersion(-1) {}
477
Adam Lesinski2c72b682014-06-24 09:56:01 -0700478 /**
479 * Human readable label
480 */
481 String8 label;
482
483 /**
484 * Explicit features defined in the group
485 */
486 KeyedVector<String8, bool> features;
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700487
488 /**
489 * OpenGL ES version required
490 */
491 int openGLESVersion;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700492};
493
494static void addImpliedFeature(KeyedVector<String8, ImpliedFeature>* impliedFeatures,
495 const char* name, const char* reason) {
496 String8 name8(name);
497 ssize_t idx = impliedFeatures->indexOfKey(name8);
498 if (idx < 0) {
499 idx = impliedFeatures->add(name8, ImpliedFeature());
500 impliedFeatures->editValueAt(idx).name = name8;
501 }
502 impliedFeatures->editValueAt(idx).reasons.add(String8(reason));
503}
504
505static void printFeatureGroup(const FeatureGroup& grp,
506 const KeyedVector<String8, ImpliedFeature>* impliedFeatures = NULL) {
507 printf("feature-group: label='%s'\n", grp.label.string());
508
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700509 if (grp.openGLESVersion > 0) {
510 printf(" uses-gl-es: '0x%x'\n", grp.openGLESVersion);
511 }
512
Adam Lesinski2c72b682014-06-24 09:56:01 -0700513 const size_t numFeatures = grp.features.size();
514 for (size_t i = 0; i < numFeatures; i++) {
515 if (!grp.features[i]) {
516 continue;
517 }
518
519 const String8& featureName = grp.features.keyAt(i);
520 printf(" uses-feature: name='%s'\n",
521 ResTable::normalizeForOutput(featureName.string()).string());
522 }
523
524 const size_t numImpliedFeatures =
525 (impliedFeatures != NULL) ? impliedFeatures->size() : 0;
526 for (size_t i = 0; i < numImpliedFeatures; i++) {
527 const ImpliedFeature& impliedFeature = impliedFeatures->valueAt(i);
528 if (grp.features.indexOfKey(impliedFeature.name) >= 0) {
529 // The feature is explicitly set, no need to use implied
530 // definition.
531 continue;
532 }
533
534 String8 printableFeatureName(ResTable::normalizeForOutput(
535 impliedFeature.name.string()));
536 printf(" uses-feature: name='%s'\n", printableFeatureName.string());
537 printf(" uses-implied-feature: name='%s' reason='",
538 printableFeatureName.string());
539 const size_t numReasons = impliedFeature.reasons.size();
540 for (size_t j = 0; j < numReasons; j++) {
541 printf("%s", impliedFeature.reasons[j].string());
542 if (j + 2 < numReasons) {
543 printf(", ");
544 } else if (j + 1 < numReasons) {
545 printf(", and ");
546 }
547 }
548 printf("'\n");
549 }
550}
551
552static void addParentFeatures(FeatureGroup* grp, const String8& name) {
553 if (name == "android.hardware.camera.autofocus" ||
554 name == "android.hardware.camera.flash") {
555 grp->features.add(String8("android.hardware.camera"), true);
556 } else if (name == "android.hardware.location.gps" ||
557 name == "android.hardware.location.network") {
558 grp->features.add(String8("android.hardware.location"), true);
559 } else if (name == "android.hardware.touchscreen.multitouch") {
560 grp->features.add(String8("android.hardware.touchscreen"), true);
561 } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
562 grp->features.add(String8("android.hardware.touchscreen.multitouch"), true);
563 grp->features.add(String8("android.hardware.touchscreen"), true);
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700564 } else if (name == "android.hardware.opengles.aep") {
565 const int openGLESVersion31 = 0x00030001;
566 if (openGLESVersion31 > grp->openGLESVersion) {
567 grp->openGLESVersion = openGLESVersion31;
568 }
Adam Lesinski2c72b682014-06-24 09:56:01 -0700569 }
570}
571
Adam Lesinski282e1812014-01-23 18:17:42 -0800572/*
573 * Handle the "dump" command, to extract select data from an archive.
574 */
575extern char CONSOLE_DATA[2925]; // see EOF
576int doDump(Bundle* bundle)
577{
578 status_t result = UNKNOWN_ERROR;
Adam Lesinski282e1812014-01-23 18:17:42 -0800579
580 if (bundle->getFileSpecCount() < 1) {
581 fprintf(stderr, "ERROR: no dump option specified\n");
582 return 1;
583 }
584
585 if (bundle->getFileSpecCount() < 2) {
586 fprintf(stderr, "ERROR: no dump file specified\n");
587 return 1;
588 }
589
590 const char* option = bundle->getFileSpecEntry(0);
591 const char* filename = bundle->getFileSpecEntry(1);
592
593 AssetManager assets;
Narayan Kamathf85e41f2014-01-24 14:14:30 +0000594 int32_t assetsCookie;
Adam Lesinski282e1812014-01-23 18:17:42 -0800595 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
596 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
597 return 1;
598 }
599
600 // Make a dummy config for retrieving resources... we need to supply
601 // non-default values for some configs so that we can retrieve resources
602 // in the app that don't have a default. The most important of these is
603 // the API version because key resources like icons will have an implicit
604 // version if they are using newer config types like density.
605 ResTable_config config;
Narayan Kamath91447d82014-01-21 15:32:36 +0000606 memset(&config, 0, sizeof(ResTable_config));
Adam Lesinski282e1812014-01-23 18:17:42 -0800607 config.language[0] = 'e';
608 config.language[1] = 'n';
609 config.country[0] = 'U';
610 config.country[1] = 'S';
611 config.orientation = ResTable_config::ORIENTATION_PORT;
612 config.density = ResTable_config::DENSITY_MEDIUM;
613 config.sdkVersion = 10000; // Very high.
614 config.screenWidthDp = 320;
615 config.screenHeightDp = 480;
616 config.smallestScreenWidthDp = 320;
Adam Lesinskic2dea8d2014-08-04 16:40:41 -0700617 config.screenLayout |= ResTable_config::SCREENSIZE_NORMAL;
Adam Lesinski282e1812014-01-23 18:17:42 -0800618 assets.setConfiguration(config);
619
620 const ResTable& res = assets.getResources(false);
Dan Albert68001652014-09-09 09:51:01 -0700621 if (res.getError() != NO_ERROR) {
Adam Lesinski25e9d552014-05-19 15:01:43 -0700622 fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
Adam Lesinski63e646e2014-07-30 11:40:39 -0700623 return 1;
Adam Lesinski282e1812014-01-23 18:17:42 -0800624 }
625
Adam Lesinski2cb761e2014-08-15 13:59:02 -0700626 // The dynamicRefTable can be null if there are no resources for this asset cookie.
627 // This fine.
Adam Lesinski63e646e2014-07-30 11:40:39 -0700628 const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700629
630 Asset* asset = NULL;
631
Adam Lesinski282e1812014-01-23 18:17:42 -0800632 if (strcmp("resources", option) == 0) {
633#ifndef HAVE_ANDROID_OS
634 res.print(bundle->getValues());
635#endif
636
637 } else if (strcmp("strings", option) == 0) {
638 const ResStringPool* pool = res.getTableStringBlock(0);
639 printStringPool(pool);
640
641 } else if (strcmp("xmltree", option) == 0) {
642 if (bundle->getFileSpecCount() < 3) {
643 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
644 goto bail;
645 }
646
647 for (int i=2; i<bundle->getFileSpecCount(); i++) {
648 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700649 ResXMLTree tree(dynamicRefTable);
650 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800651 if (asset == NULL) {
652 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
653 goto bail;
654 }
655
656 if (tree.setTo(asset->getBuffer(true),
657 asset->getLength()) != NO_ERROR) {
658 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
659 goto bail;
660 }
661 tree.restart();
662 printXMLBlock(&tree);
663 tree.uninit();
664 delete asset;
665 asset = NULL;
666 }
667
668 } else if (strcmp("xmlstrings", option) == 0) {
669 if (bundle->getFileSpecCount() < 3) {
670 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
671 goto bail;
672 }
673
674 for (int i=2; i<bundle->getFileSpecCount(); i++) {
675 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700676 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800677 if (asset == NULL) {
678 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
679 goto bail;
680 }
681
Adam Lesinski63e646e2014-07-30 11:40:39 -0700682 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800683 if (tree.setTo(asset->getBuffer(true),
684 asset->getLength()) != NO_ERROR) {
685 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
686 goto bail;
687 }
688 printStringPool(&tree.getStrings());
689 delete asset;
690 asset = NULL;
691 }
692
693 } else {
Adam Lesinski63e646e2014-07-30 11:40:39 -0700694 asset = assets.openNonAsset(assetsCookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800695 if (asset == NULL) {
696 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
697 goto bail;
698 }
699
Adam Lesinski63e646e2014-07-30 11:40:39 -0700700 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800701 if (tree.setTo(asset->getBuffer(true),
702 asset->getLength()) != NO_ERROR) {
703 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
704 goto bail;
705 }
706 tree.restart();
707
708 if (strcmp("permissions", option) == 0) {
709 size_t len;
710 ResXMLTree::event_code_t code;
711 int depth = 0;
712 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
713 if (code == ResXMLTree::END_TAG) {
714 depth--;
715 continue;
716 }
717 if (code != ResXMLTree::START_TAG) {
718 continue;
719 }
720 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700721 const char16_t* ctag16 = tree.getElementName(&len);
722 if (ctag16 == NULL) {
723 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
724 goto bail;
725 }
726 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800727 //printf("Depth %d tag %s\n", depth, tag.string());
728 if (depth == 1) {
729 if (tag != "manifest") {
730 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
731 goto bail;
732 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700733 String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -0700734 printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800735 } else if (depth == 2 && tag == "permission") {
736 String8 error;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700737 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -0800738 if (error != "") {
739 fprintf(stderr, "ERROR: %s\n", error.string());
740 goto bail;
741 }
Maurice Chu2675f762013-10-22 17:33:11 -0700742 printf("permission: %s\n",
743 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800744 } else if (depth == 2 && tag == "uses-permission") {
745 String8 error;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700746 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -0800747 if (error != "") {
748 fprintf(stderr, "ERROR: %s\n", error.string());
749 goto bail;
750 }
Adam Lesinski58f1f362013-11-12 12:59:08 -0800751 printUsesPermission(name,
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700752 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
753 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
Adam Lesinski282e1812014-01-23 18:17:42 -0800754 }
755 }
756 } else if (strcmp("badging", option) == 0) {
757 Vector<String8> locales;
758 res.getLocales(&locales);
759
760 Vector<ResTable_config> configs;
761 res.getConfigurations(&configs);
762 SortedVector<int> densities;
763 const size_t NC = configs.size();
764 for (size_t i=0; i<NC; i++) {
765 int dens = configs[i].density;
766 if (dens == 0) {
767 dens = 160;
768 }
769 densities.add(dens);
770 }
771
772 size_t len;
773 ResXMLTree::event_code_t code;
774 int depth = 0;
775 String8 error;
776 bool withinActivity = false;
777 bool isMainActivity = false;
778 bool isLauncherActivity = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800779 bool isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800780 bool isSearchable = false;
781 bool withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700782 bool withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700783 bool withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800784 bool withinReceiver = false;
785 bool withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700786 bool withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800787 bool withinIntentFilter = false;
788 bool hasMainActivity = false;
789 bool hasOtherActivities = false;
790 bool hasOtherReceivers = false;
791 bool hasOtherServices = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700792 bool hasIntentFilter = false;
793
Adam Lesinski282e1812014-01-23 18:17:42 -0800794 bool hasWallpaperService = false;
795 bool hasImeService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700796 bool hasAccessibilityService = false;
797 bool hasPrintService = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800798 bool hasWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700799 bool hasDeviceAdminReceiver = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700800 bool hasPaymentService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700801 bool hasDocumentsProvider = false;
802 bool hasCameraActivity = false;
803 bool hasCameraSecureActivity = false;
804 bool hasLauncher = false;
805 bool hasNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400806 bool hasDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700807
Adam Lesinski282e1812014-01-23 18:17:42 -0800808 bool actMainActivity = false;
809 bool actWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700810 bool actDeviceAdminEnabled = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800811 bool actImeService = false;
812 bool actWallpaperService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700813 bool actAccessibilityService = false;
814 bool actPrintService = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700815 bool actHostApduService = false;
816 bool actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700817 bool actDocumentsProvider = false;
818 bool actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400819 bool actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700820 bool actCamera = false;
821 bool actCameraSecure = false;
822 bool catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700823 bool hasMetaHostPaymentCategory = false;
824 bool hasMetaOffHostPaymentCategory = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700825
826 // These permissions are required by services implementing services
827 // the system binds to (IME, Accessibility, PrintServices, etc.)
828 bool hasBindDeviceAdminPermission = false;
829 bool hasBindInputMethodPermission = false;
830 bool hasBindAccessibilityServicePermission = false;
831 bool hasBindPrintServicePermission = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700832 bool hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700833 bool hasRequiredSafAttributes = false;
834 bool hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400835 bool hasBindDreamServicePermission = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800836
837 // These two implement the implicit permissions that are granted
838 // to pre-1.6 applications.
839 bool hasWriteExternalStoragePermission = false;
840 bool hasReadPhoneStatePermission = false;
841
842 // If an app requests write storage, they will also get read storage.
843 bool hasReadExternalStoragePermission = false;
844
845 // Implement transition to read and write call log.
846 bool hasReadContactsPermission = false;
847 bool hasWriteContactsPermission = false;
848 bool hasReadCallLogPermission = false;
849 bool hasWriteCallLogPermission = false;
850
Adam Lesinskie47fd122014-08-15 22:25:36 -0700851 // If an app declares itself as multiArch, we report the
852 // native libraries differently.
853 bool hasMultiArch = false;
854
Adam Lesinski282e1812014-01-23 18:17:42 -0800855 // This next group of variables is used to implement a group of
856 // backward-compatibility heuristics necessitated by the addition of
857 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
858 // heuristic is "if an app requests a permission but doesn't explicitly
859 // request the corresponding <uses-feature>, presume it's there anyway".
Adam Lesinski2c72b682014-06-24 09:56:01 -0700860
Adam Lesinski282e1812014-01-23 18:17:42 -0800861 // 2.2 also added some other features that apps can request, but that
862 // have no corresponding permission, so we cannot implement any
863 // back-compatibility heuristic for them. The below are thus unnecessary
864 // (but are retained here for documentary purposes.)
865 //bool specCompassFeature = false;
866 //bool specAccelerometerFeature = false;
867 //bool specProximityFeature = false;
868 //bool specAmbientLightFeature = false;
869 //bool specLiveWallpaperFeature = false;
870
871 int targetSdk = 0;
872 int smallScreen = 1;
873 int normalScreen = 1;
874 int largeScreen = 1;
875 int xlargeScreen = 1;
876 int anyDensity = 1;
877 int requiresSmallestWidthDp = 0;
878 int compatibleWidthLimitDp = 0;
879 int largestWidthLimitDp = 0;
880 String8 pkg;
881 String8 activityName;
882 String8 activityLabel;
883 String8 activityIcon;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800884 String8 activityBanner;
Adam Lesinski282e1812014-01-23 18:17:42 -0800885 String8 receiverName;
886 String8 serviceName;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700887 Vector<String8> supportedInput;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700888
889 FeatureGroup commonFeatures;
890 Vector<FeatureGroup> featureGroups;
891 KeyedVector<String8, ImpliedFeature> impliedFeatures;
892
Adam Lesinski282e1812014-01-23 18:17:42 -0800893 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
894 if (code == ResXMLTree::END_TAG) {
895 depth--;
896 if (depth < 2) {
Michael Wrightec4fdec2013-09-06 16:50:52 -0700897 if (withinSupportsInput && !supportedInput.isEmpty()) {
898 printf("supports-input: '");
899 const size_t N = supportedInput.size();
900 for (size_t i=0; i<N; i++) {
Maurice Chu2675f762013-10-22 17:33:11 -0700901 printf("%s", ResTable::normalizeForOutput(
902 supportedInput[i].string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -0700903 if (i != N - 1) {
904 printf("' '");
905 } else {
906 printf("'\n");
907 }
908 }
909 supportedInput.clear();
910 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800911 withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700912 withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700913 withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800914 } else if (depth < 3) {
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800915 if (withinActivity && isMainActivity) {
Maurice Chu2675f762013-10-22 17:33:11 -0700916 String8 aName(getComponentName(pkg, activityName));
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800917 if (isLauncherActivity) {
918 printf("launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800919 if (aName.length() > 0) {
920 printf(" name='%s' ",
921 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800922 }
923 printf(" label='%s' icon='%s'\n",
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800924 ResTable::normalizeForOutput(activityLabel.string()).string(),
925 ResTable::normalizeForOutput(activityIcon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800926 }
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800927 if (isLeanbackLauncherActivity) {
928 printf("leanback-launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800929 if (aName.length() > 0) {
930 printf(" name='%s' ",
931 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800932 }
933 printf(" label='%s' icon='%s' banner='%s'\n",
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800934 ResTable::normalizeForOutput(activityLabel.string()).string(),
935 ResTable::normalizeForOutput(activityIcon.string()).string(),
936 ResTable::normalizeForOutput(activityBanner.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800937 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800938 }
939 if (!hasIntentFilter) {
940 hasOtherActivities |= withinActivity;
941 hasOtherReceivers |= withinReceiver;
942 hasOtherServices |= withinService;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700943 } else {
944 if (withinService) {
945 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
946 hasBindNfcServicePermission);
947 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
948 hasBindNfcServicePermission);
949 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800950 }
951 withinActivity = false;
952 withinService = false;
953 withinReceiver = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700954 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800955 hasIntentFilter = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800956 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800957 } else if (depth < 4) {
958 if (withinIntentFilter) {
959 if (withinActivity) {
960 hasMainActivity |= actMainActivity;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700961 hasLauncher |= catLauncher;
962 hasCameraActivity |= actCamera;
963 hasCameraSecureActivity |= actCameraSecure;
964 hasOtherActivities |= !actMainActivity && !actCamera && !actCameraSecure;
Adam Lesinski282e1812014-01-23 18:17:42 -0800965 } else if (withinReceiver) {
966 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700967 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
968 hasBindDeviceAdminPermission);
969 hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled);
Adam Lesinski282e1812014-01-23 18:17:42 -0800970 } else if (withinService) {
971 hasImeService |= actImeService;
972 hasWallpaperService |= actWallpaperService;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700973 hasAccessibilityService |= (actAccessibilityService &&
974 hasBindAccessibilityServicePermission);
975 hasPrintService |= (actPrintService && hasBindPrintServicePermission);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700976 hasNotificationListenerService |= actNotificationListenerService &&
977 hasBindNotificationListenerServicePermission;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400978 hasDreamService |= actDreamService && hasBindDreamServicePermission;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700979 hasOtherServices |= (!actImeService && !actWallpaperService &&
Adam Lesinski94fc9122013-09-30 17:16:09 -0700980 !actAccessibilityService && !actPrintService &&
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700981 !actHostApduService && !actOffHostApduService &&
982 !actNotificationListenerService);
983 } else if (withinProvider) {
984 hasDocumentsProvider |= actDocumentsProvider && hasRequiredSafAttributes;
Adam Lesinski282e1812014-01-23 18:17:42 -0800985 }
986 }
987 withinIntentFilter = false;
988 }
989 continue;
990 }
991 if (code != ResXMLTree::START_TAG) {
992 continue;
993 }
994 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700995
996 const char16_t* ctag16 = tree.getElementName(&len);
997 if (ctag16 == NULL) {
998 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
999 goto bail;
1000 }
1001 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -08001002 //printf("Depth %d, %s\n", depth, tag.string());
1003 if (depth == 1) {
1004 if (tag != "manifest") {
1005 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
1006 goto bail;
1007 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001008 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -07001009 printf("package: name='%s' ",
1010 ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001011 int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR,
1012 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001013 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001014 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n",
1015 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001016 goto bail;
1017 }
1018 if (versionCode > 0) {
1019 printf("versionCode='%d' ", versionCode);
1020 } else {
1021 printf("versionCode='' ");
1022 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001023 String8 versionName = AaptXml::getResolvedAttribute(res, tree,
1024 VERSION_NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001025 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001026 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n",
1027 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001028 goto bail;
1029 }
Adam Lesinski25d35a92014-08-11 09:41:56 -07001030 printf("versionName='%s'",
Maurice Chu2675f762013-10-22 17:33:11 -07001031 ResTable::normalizeForOutput(versionName.string()).string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001032
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001033 String8 splitName = AaptXml::getAttribute(tree, NULL, "split");
Adam Lesinski25d35a92014-08-11 09:41:56 -07001034 if (!splitName.isEmpty()) {
1035 printf(" split='%s'", ResTable::normalizeForOutput(
1036 splitName.string()).string());
1037 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001038
Adam Lesinski5283fab2014-08-29 11:23:55 -07001039 String8 platformVersionName = AaptXml::getAttribute(tree, NULL,
1040 "platformBuildVersionName");
1041 printf(" platformBuildVersionName='%s'", platformVersionName.string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001042 printf("\n");
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001043
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001044 int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
1045 INSTALL_LOCATION_ATTR, &error);
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001046 if (error != "") {
1047 fprintf(stderr, "ERROR getting 'android:installLocation' attribute: %s\n",
1048 error.string());
1049 goto bail;
1050 }
1051
1052 if (installLocation >= 0) {
1053 printf("install-location:'");
1054 switch (installLocation) {
1055 case 0:
1056 printf("auto");
1057 break;
1058 case 1:
1059 printf("internalOnly");
1060 break;
1061 case 2:
1062 printf("preferExternal");
1063 break;
1064 default:
1065 fprintf(stderr, "Invalid installLocation %d\n", installLocation);
1066 goto bail;
1067 }
1068 printf("'\n");
1069 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001070 } else if (depth == 2) {
1071 withinApplication = false;
1072 if (tag == "application") {
1073 withinApplication = true;
1074
1075 String8 label;
1076 const size_t NL = locales.size();
1077 for (size_t i=0; i<NL; i++) {
1078 const char* localeStr = locales[i].string();
1079 assets.setLocale(localeStr != NULL ? localeStr : "");
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001080 String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1081 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001082 if (llabel != "") {
1083 if (localeStr == NULL || strlen(localeStr) == 0) {
1084 label = llabel;
Maurice Chu2675f762013-10-22 17:33:11 -07001085 printf("application-label:'%s'\n",
1086 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001087 } else {
1088 if (label == "") {
1089 label = llabel;
1090 }
1091 printf("application-label-%s:'%s'\n", localeStr,
Maurice Chu2675f762013-10-22 17:33:11 -07001092 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001093 }
1094 }
1095 }
1096
1097 ResTable_config tmpConfig = config;
1098 const size_t ND = densities.size();
1099 for (size_t i=0; i<ND; i++) {
1100 tmpConfig.density = densities[i];
1101 assets.setConfiguration(tmpConfig);
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001102 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1103 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001104 if (icon != "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001105 printf("application-icon-%d:'%s'\n", densities[i],
1106 ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001107 }
1108 }
1109 assets.setConfiguration(config);
1110
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001111 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001112 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001113 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1114 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001115 goto bail;
1116 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001117 int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0,
1118 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001119 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001120 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n",
1121 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001122 goto bail;
1123 }
Maurice Chu2675f762013-10-22 17:33:11 -07001124 printf("application: label='%s' ",
1125 ResTable::normalizeForOutput(label.string()).string());
1126 printf("icon='%s'\n", ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001127 if (testOnly != 0) {
1128 printf("testOnly='%d'\n", testOnly);
1129 }
1130
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001131 int32_t debuggable = AaptXml::getResolvedIntegerAttribute(res, tree,
1132 DEBUGGABLE_ATTR, 0, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001133 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001134 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n",
1135 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001136 goto bail;
1137 }
1138 if (debuggable != 0) {
1139 printf("application-debuggable\n");
1140 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07001141
1142 // We must search by name because the multiArch flag hasn't been API
1143 // frozen yet.
1144 int32_t multiArchIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
1145 "multiArch");
1146 if (multiArchIndex >= 0) {
1147 Res_value value;
1148 if (tree.getAttributeValue(multiArchIndex, &value) != NO_ERROR) {
1149 if (value.dataType >= Res_value::TYPE_FIRST_INT &&
1150 value.dataType <= Res_value::TYPE_LAST_INT) {
1151 hasMultiArch = value.data;
1152 }
1153 }
1154 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001155 } else if (tag == "uses-sdk") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001156 int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001157 if (error != "") {
1158 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001159 String8 name = AaptXml::getResolvedAttribute(res, tree,
1160 MIN_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001161 if (error != "") {
1162 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
1163 error.string());
1164 goto bail;
1165 }
1166 if (name == "Donut") targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001167 printf("sdkVersion:'%s'\n",
1168 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001169 } else if (code != -1) {
1170 targetSdk = code;
1171 printf("sdkVersion:'%d'\n", code);
1172 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001173 code = AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -08001174 if (code != -1) {
1175 printf("maxSdkVersion:'%d'\n", code);
1176 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001177 code = AaptXml::getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001178 if (error != "") {
1179 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001180 String8 name = AaptXml::getResolvedAttribute(res, tree,
1181 TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001182 if (error != "") {
1183 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
1184 error.string());
1185 goto bail;
1186 }
1187 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001188 printf("targetSdkVersion:'%s'\n",
1189 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001190 } else if (code != -1) {
1191 if (targetSdk < code) {
1192 targetSdk = code;
1193 }
1194 printf("targetSdkVersion:'%d'\n", code);
1195 }
1196 } else if (tag == "uses-configuration") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001197 int32_t reqTouchScreen = AaptXml::getIntegerAttribute(tree,
1198 REQ_TOUCH_SCREEN_ATTR, 0);
1199 int32_t reqKeyboardType = AaptXml::getIntegerAttribute(tree,
1200 REQ_KEYBOARD_TYPE_ATTR, 0);
1201 int32_t reqHardKeyboard = AaptXml::getIntegerAttribute(tree,
1202 REQ_HARD_KEYBOARD_ATTR, 0);
1203 int32_t reqNavigation = AaptXml::getIntegerAttribute(tree,
1204 REQ_NAVIGATION_ATTR, 0);
1205 int32_t reqFiveWayNav = AaptXml::getIntegerAttribute(tree,
1206 REQ_FIVE_WAY_NAV_ATTR, 0);
Adam Lesinski282e1812014-01-23 18:17:42 -08001207 printf("uses-configuration:");
1208 if (reqTouchScreen != 0) {
1209 printf(" reqTouchScreen='%d'", reqTouchScreen);
1210 }
1211 if (reqKeyboardType != 0) {
1212 printf(" reqKeyboardType='%d'", reqKeyboardType);
1213 }
1214 if (reqHardKeyboard != 0) {
1215 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1216 }
1217 if (reqNavigation != 0) {
1218 printf(" reqNavigation='%d'", reqNavigation);
1219 }
1220 if (reqFiveWayNav != 0) {
1221 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1222 }
1223 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001224 } else if (tag == "supports-input") {
1225 withinSupportsInput = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001226 } else if (tag == "supports-screens") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001227 smallScreen = AaptXml::getIntegerAttribute(tree,
1228 SMALL_SCREEN_ATTR, 1);
1229 normalScreen = AaptXml::getIntegerAttribute(tree,
1230 NORMAL_SCREEN_ATTR, 1);
1231 largeScreen = AaptXml::getIntegerAttribute(tree,
1232 LARGE_SCREEN_ATTR, 1);
1233 xlargeScreen = AaptXml::getIntegerAttribute(tree,
1234 XLARGE_SCREEN_ATTR, 1);
1235 anyDensity = AaptXml::getIntegerAttribute(tree,
1236 ANY_DENSITY_ATTR, 1);
1237 requiresSmallestWidthDp = AaptXml::getIntegerAttribute(tree,
1238 REQUIRES_SMALLEST_WIDTH_DP_ATTR, 0);
1239 compatibleWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1240 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, 0);
1241 largestWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1242 LARGEST_WIDTH_LIMIT_DP_ATTR, 0);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001243 } else if (tag == "feature-group") {
1244 withinFeatureGroup = true;
1245 FeatureGroup group;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001246 group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001247 if (error != "") {
1248 fprintf(stderr, "ERROR getting 'android:label' attribute:"
1249 " %s\n", error.string());
1250 goto bail;
1251 }
1252 featureGroups.add(group);
1253
Adam Lesinski282e1812014-01-23 18:17:42 -08001254 } else if (tag == "uses-feature") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001255 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001256 if (name != "" && error == "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001257 int req = AaptXml::getIntegerAttribute(tree,
1258 REQUIRED_ATTR, 1);
Adam Lesinski282e1812014-01-23 18:17:42 -08001259
Adam Lesinski2c72b682014-06-24 09:56:01 -07001260 commonFeatures.features.add(name, req);
1261 if (req) {
1262 addParentFeatures(&commonFeatures, name);
Adam Lesinski282e1812014-01-23 18:17:42 -08001263 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001264 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001265 int vers = AaptXml::getIntegerAttribute(tree,
Adam Lesinski282e1812014-01-23 18:17:42 -08001266 GL_ES_VERSION_ATTR, &error);
1267 if (error == "") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001268 if (vers > commonFeatures.openGLESVersion) {
1269 commonFeatures.openGLESVersion = vers;
1270 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001271 }
1272 }
1273 } else if (tag == "uses-permission") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001274 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001275 if (name != "" && error == "") {
1276 if (name == "android.permission.CAMERA") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001277 addImpliedFeature(&impliedFeatures, "android.hardware.camera",
Adam Lesinski2c72b682014-06-24 09:56:01 -07001278 String8::format("requested %s permission", name.string())
1279 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001280 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001281 addImpliedFeature(&impliedFeatures, "android.hardware.location.gps",
1282 String8::format("requested %s permission", name.string())
1283 .string());
1284 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1285 String8::format("requested %s permission", name.string())
1286 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001287 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001288 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1289 String8::format("requested %s permission", name.string())
1290 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001291 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001292 addImpliedFeature(&impliedFeatures, "android.hardware.location.network",
1293 String8::format("requested %s permission", name.string())
1294 .string());
1295 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.ACCESS_LOCATION_EXTRA_COMMANDS" ||
1299 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001300 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1301 String8::format("requested %s permission", name.string())
1302 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001303 } else if (name == "android.permission.BLUETOOTH" ||
1304 name == "android.permission.BLUETOOTH_ADMIN") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001305 if (targetSdk > 4) {
1306 addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
1307 String8::format("requested %s permission", name.string())
1308 .string());
1309 addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
1310 "targetSdkVersion > 4");
1311 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001312 } else if (name == "android.permission.RECORD_AUDIO") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001313 addImpliedFeature(&impliedFeatures, "android.hardware.microphone",
1314 String8::format("requested %s permission", name.string())
1315 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001316 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1317 name == "android.permission.CHANGE_WIFI_STATE" ||
1318 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001319 addImpliedFeature(&impliedFeatures, "android.hardware.wifi",
1320 String8::format("requested %s permission", name.string())
1321 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001322 } else if (name == "android.permission.CALL_PHONE" ||
1323 name == "android.permission.CALL_PRIVILEGED" ||
1324 name == "android.permission.MODIFY_PHONE_STATE" ||
1325 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1326 name == "android.permission.READ_SMS" ||
1327 name == "android.permission.RECEIVE_SMS" ||
1328 name == "android.permission.RECEIVE_MMS" ||
1329 name == "android.permission.RECEIVE_WAP_PUSH" ||
1330 name == "android.permission.SEND_SMS" ||
1331 name == "android.permission.WRITE_APN_SETTINGS" ||
1332 name == "android.permission.WRITE_SMS") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001333 addImpliedFeature(&impliedFeatures, "android.hardware.telephony",
1334 String8("requested a telephony permission").string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001335 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1336 hasWriteExternalStoragePermission = true;
1337 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1338 hasReadExternalStoragePermission = true;
1339 } else if (name == "android.permission.READ_PHONE_STATE") {
1340 hasReadPhoneStatePermission = true;
1341 } else if (name == "android.permission.READ_CONTACTS") {
1342 hasReadContactsPermission = true;
1343 } else if (name == "android.permission.WRITE_CONTACTS") {
1344 hasWriteContactsPermission = true;
1345 } else if (name == "android.permission.READ_CALL_LOG") {
1346 hasReadCallLogPermission = true;
1347 } else if (name == "android.permission.WRITE_CALL_LOG") {
1348 hasWriteCallLogPermission = true;
1349 }
Adam Lesinski58f1f362013-11-12 12:59:08 -08001350
1351 printUsesPermission(name,
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001352 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
1353 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
Adam Lesinski58f1f362013-11-12 12:59:08 -08001354 } else {
Adam Lesinski282e1812014-01-23 18:17:42 -08001355 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1356 error.string());
1357 goto bail;
1358 }
1359 } else if (tag == "uses-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001360 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001361 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001362 printf("uses-package:'%s'\n",
1363 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001364 } else {
1365 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1366 error.string());
1367 goto bail;
1368 }
1369 } else if (tag == "original-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001370 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001371 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001372 printf("original-package:'%s'\n",
1373 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001374 } else {
1375 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1376 error.string());
1377 goto bail;
1378 }
1379 } else if (tag == "supports-gl-texture") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001380 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001381 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001382 printf("supports-gl-texture:'%s'\n",
1383 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001384 } else {
1385 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1386 error.string());
1387 goto bail;
1388 }
1389 } else if (tag == "compatible-screens") {
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001390 printCompatibleScreens(tree, &error);
1391 if (error != "") {
1392 fprintf(stderr, "ERROR getting compatible screens: %s\n",
1393 error.string());
1394 goto bail;
1395 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001396 depth--;
1397 } else if (tag == "package-verifier") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001398 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001399 if (name != "" && error == "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001400 String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001401 if (publicKey != "" && error == "") {
1402 printf("package-verifier: name='%s' publicKey='%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001403 ResTable::normalizeForOutput(name.string()).string(),
1404 ResTable::normalizeForOutput(publicKey.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001405 }
1406 }
1407 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001408 } else if (depth == 3) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001409 withinActivity = false;
1410 withinReceiver = false;
1411 withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001412 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001413 hasIntentFilter = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001414 hasMetaHostPaymentCategory = false;
1415 hasMetaOffHostPaymentCategory = false;
1416 hasBindDeviceAdminPermission = false;
1417 hasBindInputMethodPermission = false;
1418 hasBindAccessibilityServicePermission = false;
1419 hasBindPrintServicePermission = false;
1420 hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001421 hasRequiredSafAttributes = false;
1422 hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001423 hasBindDreamServicePermission = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001424 if (withinApplication) {
1425 if(tag == "activity") {
1426 withinActivity = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001427 activityName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001428 if (error != "") {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001429 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1430 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001431 goto bail;
1432 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001433
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001434 activityLabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1435 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001436 if (error != "") {
1437 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
1438 error.string());
1439 goto bail;
1440 }
1441
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001442 activityIcon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1443 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001444 if (error != "") {
1445 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1446 error.string());
1447 goto bail;
1448 }
1449
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001450 activityBanner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1451 &error);
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001452 if (error != "") {
1453 fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
1454 error.string());
1455 goto bail;
1456 }
1457
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001458 int32_t orien = AaptXml::getResolvedIntegerAttribute(res, tree,
Michael Wrightec4fdec2013-09-06 16:50:52 -07001459 SCREEN_ORIENTATION_ATTR, &error);
1460 if (error == "") {
1461 if (orien == 0 || orien == 6 || orien == 8) {
1462 // Requests landscape, sensorLandscape, or reverseLandscape.
Adam Lesinski2c72b682014-06-24 09:56:01 -07001463 addImpliedFeature(&impliedFeatures, "android.hardware.screen.landscape",
1464 "one or more activities have specified a landscape orientation");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001465 } else if (orien == 1 || orien == 7 || orien == 9) {
1466 // Requests portrait, sensorPortrait, or reversePortrait.
Adam Lesinski2c72b682014-06-24 09:56:01 -07001467 addImpliedFeature(&impliedFeatures, "android.hardware.screen.portrait",
1468 "one or more activities have specified a portrait orientation");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001469 }
1470 }
1471 } else if (tag == "uses-library") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001472 String8 libraryName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001473 if (error != "") {
1474 fprintf(stderr,
1475 "ERROR getting 'android:name' attribute for uses-library"
1476 " %s\n", error.string());
1477 goto bail;
1478 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001479 int req = AaptXml::getIntegerAttribute(tree,
1480 REQUIRED_ATTR, 1);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001481 printf("uses-library%s:'%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001482 req ? "" : "-not-required", ResTable::normalizeForOutput(
1483 libraryName.string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001484 } else if (tag == "receiver") {
1485 withinReceiver = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001486 receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001487
1488 if (error != "") {
1489 fprintf(stderr,
1490 "ERROR getting 'android:name' attribute for receiver:"
1491 " %s\n", error.string());
1492 goto bail;
1493 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001494
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001495 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1496 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001497 if (error == "") {
1498 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1499 hasBindDeviceAdminPermission = true;
1500 }
1501 } else {
1502 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1503 " receiver '%s': %s\n", receiverName.string(), error.string());
1504 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001505 } else if (tag == "service") {
1506 withinService = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001507 serviceName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001508
1509 if (error != "") {
1510 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1511 "service:%s\n", error.string());
1512 goto bail;
1513 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001514
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001515 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1516 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001517 if (error == "") {
1518 if (permission == "android.permission.BIND_INPUT_METHOD") {
1519 hasBindInputMethodPermission = true;
1520 } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") {
1521 hasBindAccessibilityServicePermission = true;
1522 } else if (permission == "android.permission.BIND_PRINT_SERVICE") {
1523 hasBindPrintServicePermission = true;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001524 } else if (permission == "android.permission.BIND_NFC_SERVICE") {
1525 hasBindNfcServicePermission = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001526 } else if (permission == "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
1527 hasBindNotificationListenerServicePermission = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001528 } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
1529 hasBindDreamServicePermission = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001530 }
1531 } else {
1532 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1533 " service '%s': %s\n", serviceName.string(), error.string());
1534 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001535 } else if (tag == "provider") {
1536 withinProvider = true;
1537
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001538 bool exported = AaptXml::getResolvedIntegerAttribute(res, tree,
1539 EXPORTED_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001540 if (error != "") {
1541 fprintf(stderr, "ERROR getting 'android:exported' attribute for provider:"
1542 " %s\n", error.string());
1543 goto bail;
1544 }
1545
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001546 bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute(
1547 res, tree, GRANT_URI_PERMISSIONS_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001548 if (error != "") {
1549 fprintf(stderr, "ERROR getting 'android:grantUriPermissions' attribute for provider:"
1550 " %s\n", error.string());
1551 goto bail;
1552 }
1553
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001554 String8 permission = AaptXml::getResolvedAttribute(res, tree,
1555 PERMISSION_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001556 if (error != "") {
1557 fprintf(stderr, "ERROR getting 'android:permission' attribute for provider:"
1558 " %s\n", error.string());
1559 goto bail;
1560 }
1561
1562 hasRequiredSafAttributes |= exported && grantUriPermissions &&
1563 permission == "android.permission.MANAGE_DOCUMENTS";
1564
Michael Wrightec4fdec2013-09-06 16:50:52 -07001565 } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001566 String8 metaDataName = AaptXml::getResolvedAttribute(res, tree,
1567 NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001568 if (error != "") {
1569 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1570 "meta-data:%s\n", error.string());
1571 goto bail;
1572 }
Maurice Chu2675f762013-10-22 17:33:11 -07001573 printf("meta-data: name='%s' ",
1574 ResTable::normalizeForOutput(metaDataName.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001575 printResolvedResourceAttribute(res, tree, VALUE_ATTR, String8("value"),
Maurice Chu76327312013-10-16 18:28:46 -07001576 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001577 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001578 // Try looking for a RESOURCE_ATTR
1579 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001580 printResolvedResourceAttribute(res, tree, RESOURCE_ATTR,
Maurice Chu76327312013-10-16 18:28:46 -07001581 String8("resource"), &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001582 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001583 fprintf(stderr, "ERROR getting 'android:value' or "
1584 "'android:resource' attribute for "
1585 "meta-data:%s\n", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001586 goto bail;
1587 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001588 }
Maurice Chu76327312013-10-16 18:28:46 -07001589 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001590 } else if (withinSupportsInput && tag == "input-type") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001591 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001592 if (name != "" && error == "") {
1593 supportedInput.add(name);
1594 } else {
1595 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1596 error.string());
1597 goto bail;
1598 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001599 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001600 } else if (withinFeatureGroup && tag == "uses-feature") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001601 FeatureGroup& top = featureGroups.editTop();
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001602
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001603 String8 name = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001604 if (name != "" && error == "") {
Adam Lesinskid3edfde2014-08-08 17:32:44 -07001605 top.features.add(name, true);
1606 addParentFeatures(&top, name);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001607 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001608 int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR,
1609 &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001610 if (error == "") {
1611 if (vers > top.openGLESVersion) {
1612 top.openGLESVersion = vers;
1613 }
1614 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001615 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001616 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001617 } else if (depth == 4) {
1618 if (tag == "intent-filter") {
1619 hasIntentFilter = true;
1620 withinIntentFilter = true;
1621 actMainActivity = false;
1622 actWidgetReceivers = false;
1623 actImeService = false;
1624 actWallpaperService = false;
1625 actAccessibilityService = false;
1626 actPrintService = false;
1627 actDeviceAdminEnabled = false;
1628 actHostApduService = false;
1629 actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001630 actDocumentsProvider = false;
1631 actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001632 actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001633 actCamera = false;
1634 actCameraSecure = false;
1635 catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001636 } else if (withinService && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001637 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001638 if (error != "") {
1639 fprintf(stderr, "ERROR getting 'android:name' attribute for"
1640 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1641 goto bail;
1642 }
1643
1644 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1645 name == "android.nfc.cardemulation.off_host_apdu_service") {
1646 bool offHost = true;
1647 if (name == "android.nfc.cardemulation.host_apdu_service") {
1648 offHost = false;
1649 }
1650
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001651 String8 xmlPath = AaptXml::getResolvedAttribute(res, tree,
1652 RESOURCE_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001653 if (error != "") {
1654 fprintf(stderr, "ERROR getting 'android:resource' attribute for"
1655 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1656 goto bail;
1657 }
1658
1659 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1660 offHost, &error);
1661 if (error != "") {
1662 fprintf(stderr, "ERROR getting AID category for service '%s'\n",
1663 serviceName.string());
1664 goto bail;
1665 }
1666
1667 const size_t catLen = categories.size();
1668 for (size_t i = 0; i < catLen; i++) {
1669 bool paymentCategory = (categories[i] == "payment");
1670 if (offHost) {
1671 hasMetaOffHostPaymentCategory |= paymentCategory;
1672 } else {
1673 hasMetaHostPaymentCategory |= paymentCategory;
1674 }
1675 }
1676 }
1677 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001678 } else if ((depth == 5) && withinIntentFilter) {
1679 String8 action;
1680 if (tag == "action") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001681 action = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001682 if (error != "") {
1683 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1684 error.string());
1685 goto bail;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001686 }
1687
Adam Lesinskia5018c92013-09-30 16:23:15 -07001688 if (withinActivity) {
1689 if (action == "android.intent.action.MAIN") {
1690 isMainActivity = true;
1691 actMainActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001692 } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
1693 action == "android.media.action.VIDEO_CAMERA") {
1694 actCamera = true;
1695 } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
1696 actCameraSecure = true;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001697 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001698 } else if (withinReceiver) {
1699 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1700 actWidgetReceivers = true;
1701 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1702 actDeviceAdminEnabled = true;
1703 }
1704 } else if (withinService) {
1705 if (action == "android.view.InputMethod") {
1706 actImeService = true;
1707 } else if (action == "android.service.wallpaper.WallpaperService") {
1708 actWallpaperService = true;
1709 } else if (action == "android.accessibilityservice.AccessibilityService") {
1710 actAccessibilityService = true;
1711 } else if (action == "android.printservice.PrintService") {
1712 actPrintService = true;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001713 } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
1714 actHostApduService = true;
1715 } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
1716 actOffHostApduService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001717 } else if (action == "android.service.notification.NotificationListenerService") {
1718 actNotificationListenerService = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001719 } else if (action == "android.service.dreams.DreamService") {
1720 actDreamService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001721 }
1722 } else if (withinProvider) {
1723 if (action == "android.content.action.DOCUMENTS_PROVIDER") {
1724 actDocumentsProvider = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001725 }
1726 }
1727 if (action == "android.intent.action.SEARCH") {
1728 isSearchable = true;
1729 }
1730 }
1731
1732 if (tag == "category") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001733 String8 category = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001734 if (error != "") {
1735 fprintf(stderr, "ERROR getting 'name' attribute: %s\n",
1736 error.string());
1737 goto bail;
1738 }
1739 if (withinActivity) {
1740 if (category == "android.intent.category.LAUNCHER") {
1741 isLauncherActivity = true;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001742 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
1743 isLeanbackLauncherActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001744 } else if (category == "android.intent.category.HOME") {
1745 catLauncher = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001746 }
1747 }
1748 }
1749 }
1750 }
1751
1752 // Pre-1.6 implicitly granted permission compatibility logic
1753 if (targetSdk < 4) {
1754 if (!hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001755 printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
1756 printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
1757 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001758 hasWriteExternalStoragePermission = true;
1759 }
1760 if (!hasReadPhoneStatePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001761 printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
1762 printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
1763 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001764 }
1765 }
1766
1767 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1768 // force them to always take READ_EXTERNAL_STORAGE as well. We always
1769 // do this (regardless of target API version) because we can't have
1770 // an app with write permission but not read permission.
1771 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001772 printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"));
1773 printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
1774 String8("requested WRITE_EXTERNAL_STORAGE"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001775 }
1776
1777 // Pre-JellyBean call log permission compatibility.
1778 if (targetSdk < 16) {
1779 if (!hasReadCallLogPermission && hasReadContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001780 printUsesPermission(String8("android.permission.READ_CALL_LOG"));
1781 printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
1782 String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001783 }
1784 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001785 printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
1786 printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
1787 String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001788 }
1789 }
1790
Adam Lesinski2c72b682014-06-24 09:56:01 -07001791 addImpliedFeature(&impliedFeatures, "android.hardware.touchscreen",
1792 "default feature for all apps");
1793
1794 const size_t numFeatureGroups = featureGroups.size();
1795 if (numFeatureGroups == 0) {
1796 // If no <feature-group> tags were defined, apply auto-implied features.
1797 printFeatureGroup(commonFeatures, &impliedFeatures);
1798
1799 } else {
1800 // <feature-group> tags are defined, so we ignore implied features and
1801 for (size_t i = 0; i < numFeatureGroups; i++) {
1802 FeatureGroup& grp = featureGroups.editItemAt(i);
1803
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001804 if (commonFeatures.openGLESVersion > grp.openGLESVersion) {
1805 grp.openGLESVersion = commonFeatures.openGLESVersion;
1806 }
1807
Adam Lesinski2c72b682014-06-24 09:56:01 -07001808 // Merge the features defined in the top level (not inside a <feature-group>)
1809 // with this feature group.
1810 const size_t numCommonFeatures = commonFeatures.features.size();
1811 for (size_t j = 0; j < numCommonFeatures; j++) {
1812 if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001813 grp.features.add(commonFeatures.features.keyAt(j),
1814 commonFeatures.features[j]);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001815 }
1816 }
1817
1818 if (!grp.features.isEmpty()) {
1819 printFeatureGroup(grp);
Adam Lesinski282e1812014-01-23 18:17:42 -08001820 }
1821 }
1822 }
1823
Adam Lesinski282e1812014-01-23 18:17:42 -08001824
Adam Lesinski282e1812014-01-23 18:17:42 -08001825 if (hasWidgetReceivers) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001826 printComponentPresence("app-widget");
Adam Lesinski282e1812014-01-23 18:17:42 -08001827 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001828 if (hasDeviceAdminReceiver) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001829 printComponentPresence("device-admin");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001830 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001831 if (hasImeService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001832 printComponentPresence("ime");
Adam Lesinski282e1812014-01-23 18:17:42 -08001833 }
1834 if (hasWallpaperService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001835 printComponentPresence("wallpaper");
Adam Lesinski282e1812014-01-23 18:17:42 -08001836 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001837 if (hasAccessibilityService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001838 printComponentPresence("accessibility");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001839 }
1840 if (hasPrintService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001841 printComponentPresence("print-service");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001842 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001843 if (hasPaymentService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001844 printComponentPresence("payment");
1845 }
1846 if (isSearchable) {
1847 printComponentPresence("search");
1848 }
1849 if (hasDocumentsProvider) {
1850 printComponentPresence("document-provider");
1851 }
1852 if (hasLauncher) {
1853 printComponentPresence("launcher");
1854 }
1855 if (hasNotificationListenerService) {
1856 printComponentPresence("notification-listener");
1857 }
John Spurlockeb8d1be2014-06-25 17:46:15 -04001858 if (hasDreamService) {
1859 printComponentPresence("dream");
1860 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001861 if (hasCameraActivity) {
1862 printComponentPresence("camera");
1863 }
1864 if (hasCameraSecureActivity) {
1865 printComponentPresence("camera-secure");
1866 }
1867
1868 if (hasMainActivity) {
1869 printf("main\n");
Adam Lesinski94fc9122013-09-30 17:16:09 -07001870 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001871 if (hasOtherActivities) {
1872 printf("other-activities\n");
1873 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001874 if (hasOtherReceivers) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001875 printf("other-receivers\n");
1876 }
1877 if (hasOtherServices) {
1878 printf("other-services\n");
1879 }
1880
1881 // For modern apps, if screen size buckets haven't been specified
1882 // but the new width ranges have, then infer the buckets from them.
1883 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1884 && requiresSmallestWidthDp > 0) {
1885 int compatWidth = compatibleWidthLimitDp;
1886 if (compatWidth <= 0) {
1887 compatWidth = requiresSmallestWidthDp;
1888 }
1889 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1890 smallScreen = -1;
1891 } else {
1892 smallScreen = 0;
1893 }
1894 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1895 normalScreen = -1;
1896 } else {
1897 normalScreen = 0;
1898 }
1899 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1900 largeScreen = -1;
1901 } else {
1902 largeScreen = 0;
1903 }
1904 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1905 xlargeScreen = -1;
1906 } else {
1907 xlargeScreen = 0;
1908 }
1909 }
1910
1911 // Determine default values for any unspecified screen sizes,
1912 // based on the target SDK of the package. As of 4 (donut)
1913 // the screen size support was introduced, so all default to
1914 // enabled.
1915 if (smallScreen > 0) {
1916 smallScreen = targetSdk >= 4 ? -1 : 0;
1917 }
1918 if (normalScreen > 0) {
1919 normalScreen = -1;
1920 }
1921 if (largeScreen > 0) {
1922 largeScreen = targetSdk >= 4 ? -1 : 0;
1923 }
1924 if (xlargeScreen > 0) {
1925 // Introduced in Gingerbread.
1926 xlargeScreen = targetSdk >= 9 ? -1 : 0;
1927 }
1928 if (anyDensity > 0) {
1929 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1930 || compatibleWidthLimitDp > 0) ? -1 : 0;
1931 }
1932 printf("supports-screens:");
1933 if (smallScreen != 0) {
1934 printf(" 'small'");
1935 }
1936 if (normalScreen != 0) {
1937 printf(" 'normal'");
1938 }
1939 if (largeScreen != 0) {
1940 printf(" 'large'");
1941 }
1942 if (xlargeScreen != 0) {
1943 printf(" 'xlarge'");
1944 }
1945 printf("\n");
1946 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
1947 if (requiresSmallestWidthDp > 0) {
1948 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1949 }
1950 if (compatibleWidthLimitDp > 0) {
1951 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1952 }
1953 if (largestWidthLimitDp > 0) {
1954 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1955 }
1956
1957 printf("locales:");
1958 const size_t NL = locales.size();
1959 for (size_t i=0; i<NL; i++) {
1960 const char* localeStr = locales[i].string();
1961 if (localeStr == NULL || strlen(localeStr) == 0) {
1962 localeStr = "--_--";
1963 }
1964 printf(" '%s'", localeStr);
1965 }
1966 printf("\n");
1967
1968 printf("densities:");
1969 const size_t ND = densities.size();
1970 for (size_t i=0; i<ND; i++) {
1971 printf(" '%d'", densities[i]);
1972 }
1973 printf("\n");
1974
1975 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1976 if (dir != NULL) {
1977 if (dir->getFileCount() > 0) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07001978 SortedVector<String8> architectures;
Adam Lesinski282e1812014-01-23 18:17:42 -08001979 for (size_t i=0; i<dir->getFileCount(); i++) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07001980 architectures.add(ResTable::normalizeForOutput(
1981 dir->getFileName(i).string()));
Adam Lesinski282e1812014-01-23 18:17:42 -08001982 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07001983
1984 bool outputAltNativeCode = false;
1985 // A multiArch package is one that contains 64-bit and
1986 // 32-bit versions of native code and expects 3rd-party
1987 // apps to load these native code libraries. Since most
1988 // 64-bit systems also support 32-bit apps, the apps
1989 // loading this multiArch package's code may be either
1990 // 32-bit or 64-bit.
1991 if (hasMultiArch) {
1992 // If this is a multiArch package, report the 64-bit
1993 // version only. Then as a separate entry, report the
1994 // rest.
1995 //
1996 // If we report the 32-bit architecture, this APK will
1997 // be installed on a 32-bit device, causing a large waste
1998 // of bandwidth and disk space. This assumes that
1999 // the developer of the multiArch package has also
2000 // made a version that is 32-bit only.
2001 String8 intel64("x86_64");
2002 String8 arm64("arm64-v8a");
2003 ssize_t index = architectures.indexOf(intel64);
2004 if (index < 0) {
2005 index = architectures.indexOf(arm64);
2006 }
2007
2008 if (index >= 0) {
2009 printf("native-code: '%s'\n", architectures[index].string());
2010 architectures.removeAt(index);
2011 outputAltNativeCode = true;
2012 }
2013 }
2014
2015 const size_t archCount = architectures.size();
2016 if (archCount > 0) {
2017 if (outputAltNativeCode) {
2018 printf("alt-");
2019 }
2020 printf("native-code:");
2021 for (size_t i = 0; i < archCount; i++) {
2022 printf(" '%s'", architectures[i].string());
2023 }
2024 printf("\n");
2025 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002026 }
2027 delete dir;
2028 }
2029 } else if (strcmp("badger", option) == 0) {
2030 printf("%s", CONSOLE_DATA);
2031 } else if (strcmp("configurations", option) == 0) {
2032 Vector<ResTable_config> configs;
2033 res.getConfigurations(&configs);
2034 const size_t N = configs.size();
2035 for (size_t i=0; i<N; i++) {
2036 printf("%s\n", configs[i].toString().string());
2037 }
2038 } else {
2039 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2040 goto bail;
2041 }
2042 }
2043
2044 result = NO_ERROR;
2045
2046bail:
2047 if (asset) {
2048 delete asset;
2049 }
2050 return (result != NO_ERROR);
2051}
2052
2053
2054/*
2055 * Handle the "add" command, which wants to add files to a new or
2056 * pre-existing archive.
2057 */
2058int doAdd(Bundle* bundle)
2059{
2060 ZipFile* zip = NULL;
2061 status_t result = UNKNOWN_ERROR;
2062 const char* zipFileName;
2063
2064 if (bundle->getUpdate()) {
2065 /* avoid confusion */
2066 fprintf(stderr, "ERROR: can't use '-u' with add\n");
2067 goto bail;
2068 }
2069
2070 if (bundle->getFileSpecCount() < 1) {
2071 fprintf(stderr, "ERROR: must specify zip file name\n");
2072 goto bail;
2073 }
2074 zipFileName = bundle->getFileSpecEntry(0);
2075
2076 if (bundle->getFileSpecCount() < 2) {
2077 fprintf(stderr, "NOTE: nothing to do\n");
2078 goto bail;
2079 }
2080
2081 zip = openReadWrite(zipFileName, true);
2082 if (zip == NULL) {
2083 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2084 goto bail;
2085 }
2086
2087 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2088 const char* fileName = bundle->getFileSpecEntry(i);
2089
2090 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
2091 printf(" '%s'... (from gzip)\n", fileName);
2092 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
2093 } else {
2094 if (bundle->getJunkPath()) {
2095 String8 storageName = String8(fileName).getPathLeaf();
Maurice Chu2675f762013-10-22 17:33:11 -07002096 printf(" '%s' as '%s'...\n", fileName,
2097 ResTable::normalizeForOutput(storageName.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08002098 result = zip->add(fileName, storageName.string(),
2099 bundle->getCompressionMethod(), NULL);
2100 } else {
2101 printf(" '%s'...\n", fileName);
2102 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2103 }
2104 }
2105 if (result != NO_ERROR) {
2106 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2107 if (result == NAME_NOT_FOUND) {
2108 fprintf(stderr, ": file not found\n");
2109 } else if (result == ALREADY_EXISTS) {
2110 fprintf(stderr, ": already exists in archive\n");
2111 } else {
2112 fprintf(stderr, "\n");
2113 }
2114 goto bail;
2115 }
2116 }
2117
2118 result = NO_ERROR;
2119
2120bail:
2121 delete zip;
2122 return (result != NO_ERROR);
2123}
2124
2125
2126/*
2127 * Delete files from an existing archive.
2128 */
2129int doRemove(Bundle* bundle)
2130{
2131 ZipFile* zip = NULL;
2132 status_t result = UNKNOWN_ERROR;
2133 const char* zipFileName;
2134
2135 if (bundle->getFileSpecCount() < 1) {
2136 fprintf(stderr, "ERROR: must specify zip file name\n");
2137 goto bail;
2138 }
2139 zipFileName = bundle->getFileSpecEntry(0);
2140
2141 if (bundle->getFileSpecCount() < 2) {
2142 fprintf(stderr, "NOTE: nothing to do\n");
2143 goto bail;
2144 }
2145
2146 zip = openReadWrite(zipFileName, false);
2147 if (zip == NULL) {
2148 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2149 zipFileName);
2150 goto bail;
2151 }
2152
2153 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2154 const char* fileName = bundle->getFileSpecEntry(i);
2155 ZipEntry* entry;
2156
2157 entry = zip->getEntryByName(fileName);
2158 if (entry == NULL) {
2159 printf(" '%s' NOT FOUND\n", fileName);
2160 continue;
2161 }
2162
2163 result = zip->remove(entry);
2164
2165 if (result != NO_ERROR) {
2166 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2167 bundle->getFileSpecEntry(i), zipFileName);
2168 goto bail;
2169 }
2170 }
2171
2172 /* update the archive */
2173 zip->flush();
2174
2175bail:
2176 delete zip;
2177 return (result != NO_ERROR);
2178}
2179
Adam Lesinski3921e872014-05-13 10:56:25 -07002180static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002181 const size_t numDirs = dir->getDirs().size();
2182 for (size_t i = 0; i < numDirs; i++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002183 bool ignore = ignoreConfig;
2184 const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2185 const char* dirStr = subDir->getLeaf().string();
2186 if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2187 ignore = true;
2188 }
2189 status_t err = addResourcesToBuilder(subDir, builder, ignore);
Adam Lesinskifab50872014-04-16 14:40:42 -07002190 if (err != NO_ERROR) {
2191 return err;
2192 }
2193 }
2194
2195 const size_t numFiles = dir->getFiles().size();
2196 for (size_t i = 0; i < numFiles; i++) {
2197 sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2198 const size_t numConfigs = gp->getFiles().size();
2199 for (size_t j = 0; j < numConfigs; j++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002200 status_t err = NO_ERROR;
2201 if (ignoreConfig) {
2202 err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2203 } else {
2204 err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2205 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002206 if (err != NO_ERROR) {
2207 fprintf(stderr, "Failed to add %s (%s) to builder.\n",
2208 gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
2209 return err;
2210 }
2211 }
2212 }
2213 return NO_ERROR;
2214}
2215
2216static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2217 if (split->isBase()) {
2218 return original;
2219 }
2220
2221 String8 ext(original.getPathExtension());
2222 if (ext == String8(".apk")) {
2223 return String8::format("%s_%s%s",
2224 original.getBasePath().string(),
2225 split->getDirectorySafeName().string(),
2226 ext.string());
2227 }
2228
2229 return String8::format("%s_%s", original.string(),
2230 split->getDirectorySafeName().string());
2231}
Adam Lesinski282e1812014-01-23 18:17:42 -08002232
2233/*
2234 * Package up an asset directory and associated application files.
2235 */
2236int doPackage(Bundle* bundle)
2237{
2238 const char* outputAPKFile;
2239 int retVal = 1;
2240 status_t err;
2241 sp<AaptAssets> assets;
2242 int N;
2243 FILE* fp;
2244 String8 dependencyFile;
Adam Lesinskifab50872014-04-16 14:40:42 -07002245 sp<ApkBuilder> builder;
Adam Lesinski282e1812014-01-23 18:17:42 -08002246
Anton Krumina2ef5c02014-03-12 14:46:44 -07002247 // -c en_XA or/and ar_XB means do pseudolocalization
Adam Lesinskifab50872014-04-16 14:40:42 -07002248 sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2249 err = configFilter->parse(bundle->getConfigurations());
Adam Lesinski282e1812014-01-23 18:17:42 -08002250 if (err != NO_ERROR) {
2251 goto bail;
2252 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002253 if (configFilter->containsPseudo()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002254 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2255 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002256 if (configFilter->containsPseudoBidi()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002257 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
Adam Lesinski282e1812014-01-23 18:17:42 -08002258 }
2259
2260 N = bundle->getFileSpecCount();
2261 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
Adam Lesinski09384302014-01-22 16:07:42 -08002262 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002263 fprintf(stderr, "ERROR: no input files\n");
2264 goto bail;
2265 }
2266
2267 outputAPKFile = bundle->getOutputAPKFile();
2268
2269 // Make sure the filenames provided exist and are of the appropriate type.
2270 if (outputAPKFile) {
2271 FileType type;
2272 type = getFileType(outputAPKFile);
2273 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2274 fprintf(stderr,
2275 "ERROR: output file '%s' exists but is not regular file\n",
2276 outputAPKFile);
2277 goto bail;
2278 }
2279 }
2280
2281 // Load the assets.
2282 assets = new AaptAssets();
2283
2284 // Set up the resource gathering in assets if we're going to generate
2285 // dependency files. Every time we encounter a resource while slurping
2286 // the tree, we'll add it to these stores so we have full resource paths
2287 // to write to a dependency file.
2288 if (bundle->getGenDependencies()) {
2289 sp<FilePathStore> resPathStore = new FilePathStore;
2290 assets->setFullResPaths(resPathStore);
2291 sp<FilePathStore> assetPathStore = new FilePathStore;
2292 assets->setFullAssetPaths(assetPathStore);
2293 }
2294
2295 err = assets->slurpFromArgs(bundle);
2296 if (err < 0) {
2297 goto bail;
2298 }
2299
2300 if (bundle->getVerbose()) {
2301 assets->print(String8());
2302 }
2303
Adam Lesinskifab50872014-04-16 14:40:42 -07002304 // Create the ApkBuilder, which will collect the compiled files
2305 // to write to the final APK (or sets of APKs if we are building
2306 // a Split APK.
2307 builder = new ApkBuilder(configFilter);
2308
2309 // If we are generating a Split APK, find out which configurations to split on.
2310 if (bundle->getSplitConfigurations().size() > 0) {
2311 const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2312 const size_t numSplits = splitStrs.size();
2313 for (size_t i = 0; i < numSplits; i++) {
2314 std::set<ConfigDescription> configs;
2315 if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2316 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2317 goto bail;
2318 }
2319
2320 err = builder->createSplitForConfigs(configs);
2321 if (err != NO_ERROR) {
2322 goto bail;
2323 }
2324 }
2325 }
2326
Adam Lesinski282e1812014-01-23 18:17:42 -08002327 // If they asked for any fileAs that need to be compiled, do so.
2328 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002329 err = buildResources(bundle, assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002330 if (err != 0) {
2331 goto bail;
2332 }
2333 }
2334
2335 // At this point we've read everything and processed everything. From here
2336 // on out it's just writing output files.
2337 if (SourcePos::hasErrors()) {
2338 goto bail;
2339 }
2340
2341 // Update symbols with information about which ones are needed as Java symbols.
2342 assets->applyJavaSymbols();
2343 if (SourcePos::hasErrors()) {
2344 goto bail;
2345 }
2346
2347 // If we've been asked to generate a dependency file, do that here
2348 if (bundle->getGenDependencies()) {
2349 // If this is the packaging step, generate the dependency file next to
2350 // the output apk (e.g. bin/resources.ap_.d)
2351 if (outputAPKFile) {
2352 dependencyFile = String8(outputAPKFile);
2353 // Add the .d extension to the dependency file.
2354 dependencyFile.append(".d");
2355 } else {
2356 // Else if this is the R.java dependency generation step,
2357 // generate the dependency file in the R.java package subdirectory
2358 // e.g. gen/com/foo/app/R.java.d
2359 dependencyFile = String8(bundle->getRClassDir());
2360 dependencyFile.appendPath("R.java.d");
2361 }
2362 // Make sure we have a clean dependency file to start with
2363 fp = fopen(dependencyFile, "w");
2364 fclose(fp);
2365 }
2366
2367 // Write out R.java constants
2368 if (!assets->havePrivateSymbols()) {
2369 if (bundle->getCustomPackage() == NULL) {
2370 // Write the R.java file into the appropriate class directory
2371 // e.g. gen/com/foo/app/R.java
Adam Lesinski1e4663852014-08-15 14:47:28 -07002372 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true,
2373 bundle->getBuildSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002374 } else {
2375 const String8 customPkg(bundle->getCustomPackage());
Adam Lesinski1e4663852014-08-15 14:47:28 -07002376 err = writeResourceSymbols(bundle, assets, customPkg, true,
2377 bundle->getBuildSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002378 }
2379 if (err < 0) {
2380 goto bail;
2381 }
2382 // If we have library files, we're going to write our R.java file into
2383 // the appropriate class directory for those libraries as well.
2384 // e.g. gen/com/foo/app/lib/R.java
2385 if (bundle->getExtraPackages() != NULL) {
2386 // Split on colon
2387 String8 libs(bundle->getExtraPackages());
2388 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2389 while (packageString != NULL) {
2390 // Write the R.java file out with the correct package name
Marcin Kosiba0f3a5a62014-09-11 13:48:48 +01002391 err = writeResourceSymbols(bundle, assets, String8(packageString), true,
2392 bundle->getBuildSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002393 if (err < 0) {
2394 goto bail;
2395 }
2396 packageString = strtok(NULL, ":");
2397 }
2398 libs.unlockBuffer();
2399 }
2400 } else {
Adam Lesinski1e4663852014-08-15 14:47:28 -07002401 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002402 if (err < 0) {
2403 goto bail;
2404 }
Adam Lesinski1e4663852014-08-15 14:47:28 -07002405 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002406 if (err < 0) {
2407 goto bail;
2408 }
2409 }
2410
2411 // Write out the ProGuard file
2412 err = writeProguardFile(bundle, assets);
2413 if (err < 0) {
2414 goto bail;
2415 }
2416
2417 // Write the apk
2418 if (outputAPKFile) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002419 // Gather all resources and add them to the APK Builder. The builder will then
2420 // figure out which Split they belong in.
2421 err = addResourcesToBuilder(assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002422 if (err != NO_ERROR) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002423 goto bail;
2424 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002425
2426 const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2427 const size_t numSplits = splits.size();
2428 for (size_t i = 0; i < numSplits; i++) {
2429 const sp<ApkSplit>& split = splits[i];
2430 String8 outputPath = buildApkName(String8(outputAPKFile), split);
2431 err = writeAPK(bundle, outputPath, split);
2432 if (err != NO_ERROR) {
2433 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2434 goto bail;
2435 }
2436 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002437 }
2438
2439 // If we've been asked to generate a dependency file, we need to finish up here.
2440 // the writeResourceSymbols and writeAPK functions have already written the target
2441 // half of the dependency file, now we need to write the prerequisites. (files that
2442 // the R.java file or .ap_ file depend on)
2443 if (bundle->getGenDependencies()) {
2444 // Now that writeResourceSymbols or writeAPK has taken care of writing
2445 // the targets to our dependency file, we'll write the prereqs
2446 fp = fopen(dependencyFile, "a+");
2447 fprintf(fp, " : ");
2448 bool includeRaw = (outputAPKFile != NULL);
2449 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2450 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2451 // and therefore was not added to our pathstores during slurping
2452 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2453 fclose(fp);
2454 }
2455
2456 retVal = 0;
2457bail:
2458 if (SourcePos::hasErrors()) {
2459 SourcePos::printErrors(stderr);
2460 }
2461 return retVal;
2462}
2463
2464/*
2465 * Do PNG Crunching
2466 * PRECONDITIONS
2467 * -S flag points to a source directory containing drawable* folders
2468 * -C flag points to destination directory. The folder structure in the
2469 * source directory will be mirrored to the destination (cache) directory
2470 *
2471 * POSTCONDITIONS
2472 * Destination directory will be updated to match the PNG files in
Maurice Chu2675f762013-10-22 17:33:11 -07002473 * the source directory.
Adam Lesinski282e1812014-01-23 18:17:42 -08002474 */
2475int doCrunch(Bundle* bundle)
2476{
2477 fprintf(stdout, "Crunching PNG Files in ");
2478 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2479 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2480
2481 updatePreProcessedCache(bundle);
2482
2483 return NO_ERROR;
2484}
2485
2486/*
2487 * Do PNG Crunching on a single flag
2488 * -i points to a single png file
2489 * -o points to a single png output file
2490 */
2491int doSingleCrunch(Bundle* bundle)
2492{
2493 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2494 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2495
2496 String8 input(bundle->getSingleCrunchInputFile());
2497 String8 output(bundle->getSingleCrunchOutputFile());
2498
2499 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2500 // we can't return the status_t as it gets truncate to the lower 8 bits.
2501 return 42;
2502 }
2503
2504 return NO_ERROR;
2505}
2506
2507char CONSOLE_DATA[2925] = {
2508 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2509 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2510 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2511 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2512 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2513 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2514 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2515 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2516 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2517 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2518 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2519 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2520 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2521 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2522 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2523 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2524 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2525 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2526 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2527 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2528 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2529 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2530 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2531 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2532 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2533 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2534 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2535 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2536 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2537 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2538 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2539 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2540 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2541 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2542 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2543 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2544 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2545 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2546 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2547 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2548 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2549 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2550 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2551 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2552 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2553 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2554 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2555 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2556 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2557 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2558 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2559 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2560 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2561 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2562 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2563 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2564 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2565 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2566 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2567 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2568 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2569 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2570 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2571 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2572 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2573 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2574 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2575 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2576 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2577 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2578 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2579 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2580 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2581 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2582 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2583 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2584 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2585 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2586 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2587 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2588 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2589 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2590 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2591 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2592 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2593 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2594 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2595 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2596 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2597 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2598 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2599 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2600 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2601 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2602 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2603 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2604 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2605 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2606 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2607 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2608 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2609 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2610 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2611 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2612 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2613 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2614 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2615 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2616 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2617 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2618 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2619 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2620 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2621 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2622 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2623 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2624 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2625 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2626 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2627 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2628 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2629 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2630 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2631 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2632 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2633 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2634 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2635 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2636 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2637 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2638 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2639 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2640 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2641 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2642 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2643 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2644 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2645 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2646 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2647 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2648 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2649 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2650 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2651 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2652 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2653 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2654 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2655 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2656 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2657 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2658 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2659 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2660 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2661 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2662 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2663 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2664 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2665 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2666 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2667 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2668 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2669 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2670 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2671 };