blob: 3fa131edd8730319888b6f50054f66b92592f003 [file] [log] [blame]
Adam Lesinski282e1812014-01-23 18:17:42 -08001//
2// Copyright 2006 The Android Open Source Project
3//
4// Android Asset Packaging Tool main entry point.
5//
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07006#include "AaptXml.h"
Adam Lesinskifab50872014-04-16 14:40:42 -07007#include "ApkBuilder.h"
Adam Lesinski282e1812014-01-23 18:17:42 -08008#include "Bundle.h"
Adam Lesinski2c72b682014-06-24 09:56:01 -07009#include "Images.h"
10#include "Main.h"
Adam Lesinski282e1812014-01-23 18:17:42 -080011#include "ResourceFilter.h"
12#include "ResourceTable.h"
Adam Lesinski282e1812014-01-23 18:17:42 -080013#include "XMLNode.h"
14
Adam Lesinski282e1812014-01-23 18:17:42 -080015#include <utils/Errors.h>
Adam Lesinski2c72b682014-06-24 09:56:01 -070016#include <utils/KeyedVector.h>
17#include <utils/List.h>
18#include <utils/Log.h>
19#include <utils/SortedVector.h>
20#include <utils/threads.h>
21#include <utils/Vector.h>
Adam Lesinski282e1812014-01-23 18:17:42 -080022
Adam Lesinski282e1812014-01-23 18:17:42 -080023#include <errno.h>
Adam Lesinski2c72b682014-06-24 09:56:01 -070024#include <fcntl.h>
Adam Lesinski282e1812014-01-23 18:17:42 -080025
Jerome Dochez6f1280c2014-09-26 10:21:21 -070026#include <iostream>
27#include <string>
28#include <sstream>
29
Adam Lesinski282e1812014-01-23 18:17:42 -080030using namespace android;
31
Adam Lesinskiad751222014-08-18 14:06:38 -070032#ifndef AAPT_VERSION
33 #define AAPT_VERSION ""
34#endif
35
Adam Lesinski282e1812014-01-23 18:17:42 -080036/*
37 * Show version info. All the cool kids do it.
38 */
39int doVersion(Bundle* bundle)
40{
41 if (bundle->getFileSpecCount() != 0) {
42 printf("(ignoring extra arguments)\n");
43 }
Adam Lesinskiad751222014-08-18 14:06:38 -070044 printf("Android Asset Packaging Tool, v0.2-" AAPT_VERSION "\n");
Adam Lesinski282e1812014-01-23 18:17:42 -080045
46 return 0;
47}
48
49
50/*
51 * Open the file read only. The call fails if the file doesn't exist.
52 *
53 * Returns NULL on failure.
54 */
55ZipFile* openReadOnly(const char* fileName)
56{
57 ZipFile* zip;
58 status_t result;
59
60 zip = new ZipFile;
61 result = zip->open(fileName, ZipFile::kOpenReadOnly);
62 if (result != NO_ERROR) {
63 if (result == NAME_NOT_FOUND) {
64 fprintf(stderr, "ERROR: '%s' not found\n", fileName);
65 } else if (result == PERMISSION_DENIED) {
66 fprintf(stderr, "ERROR: '%s' access denied\n", fileName);
67 } else {
68 fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n",
69 fileName);
70 }
71 delete zip;
72 return NULL;
73 }
74
75 return zip;
76}
77
78/*
79 * Open the file read-write. The file will be created if it doesn't
80 * already exist and "okayToCreate" is set.
81 *
82 * Returns NULL on failure.
83 */
84ZipFile* openReadWrite(const char* fileName, bool okayToCreate)
85{
86 ZipFile* zip = NULL;
87 status_t result;
88 int flags;
89
90 flags = ZipFile::kOpenReadWrite;
91 if (okayToCreate) {
92 flags |= ZipFile::kOpenCreate;
93 }
94
95 zip = new ZipFile;
96 result = zip->open(fileName, flags);
97 if (result != NO_ERROR) {
98 delete zip;
99 zip = NULL;
100 goto bail;
101 }
102
103bail:
104 return zip;
105}
106
107
108/*
109 * Return a short string describing the compression method.
110 */
111const char* compressionName(int method)
112{
113 if (method == ZipEntry::kCompressStored) {
114 return "Stored";
115 } else if (method == ZipEntry::kCompressDeflated) {
116 return "Deflated";
117 } else {
118 return "Unknown";
119 }
120}
121
122/*
123 * Return the percent reduction in size (0% == no compression).
124 */
125int calcPercent(long uncompressedLen, long compressedLen)
126{
127 if (!uncompressedLen) {
128 return 0;
129 } else {
130 return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5);
131 }
132}
133
134/*
135 * Handle the "list" command, which can be a simple file dump or
136 * a verbose listing.
137 *
138 * The verbose listing closely matches the output of the Info-ZIP "unzip"
139 * command.
140 */
141int doList(Bundle* bundle)
142{
143 int result = 1;
144 ZipFile* zip = NULL;
145 const ZipEntry* entry;
146 long totalUncLen, totalCompLen;
147 const char* zipFileName;
148
149 if (bundle->getFileSpecCount() != 1) {
150 fprintf(stderr, "ERROR: specify zip file name (only)\n");
151 goto bail;
152 }
153 zipFileName = bundle->getFileSpecEntry(0);
154
155 zip = openReadOnly(zipFileName);
156 if (zip == NULL) {
157 goto bail;
158 }
159
160 int count, i;
161
162 if (bundle->getVerbose()) {
163 printf("Archive: %s\n", zipFileName);
164 printf(
165 " Length Method Size Ratio Offset Date Time CRC-32 Name\n");
166 printf(
167 "-------- ------ ------- ----- ------- ---- ---- ------ ----\n");
168 }
169
170 totalUncLen = totalCompLen = 0;
171
172 count = zip->getNumEntries();
173 for (i = 0; i < count; i++) {
174 entry = zip->getEntryByIndex(i);
175 if (bundle->getVerbose()) {
176 char dateBuf[32];
177 time_t when;
178
179 when = entry->getModWhen();
180 strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M",
181 localtime(&when));
182
183 printf("%8ld %-7.7s %7ld %3d%% %8zd %s %08lx %s\n",
184 (long) entry->getUncompressedLen(),
185 compressionName(entry->getCompressionMethod()),
186 (long) entry->getCompressedLen(),
187 calcPercent(entry->getUncompressedLen(),
188 entry->getCompressedLen()),
189 (size_t) entry->getLFHOffset(),
190 dateBuf,
191 entry->getCRC32(),
192 entry->getFileName());
193 } else {
194 printf("%s\n", entry->getFileName());
195 }
196
197 totalUncLen += entry->getUncompressedLen();
198 totalCompLen += entry->getCompressedLen();
199 }
200
201 if (bundle->getVerbose()) {
202 printf(
203 "-------- ------- --- -------\n");
204 printf("%8ld %7ld %2d%% %d files\n",
205 totalUncLen,
206 totalCompLen,
207 calcPercent(totalUncLen, totalCompLen),
208 zip->getNumEntries());
209 }
210
211 if (bundle->getAndroidList()) {
212 AssetManager assets;
213 if (!assets.addAssetPath(String8(zipFileName), NULL)) {
214 fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n");
215 goto bail;
216 }
217
218 const ResTable& res = assets.getResources(false);
219 if (&res == NULL) {
220 printf("\nNo resource table found.\n");
221 } else {
222#ifndef HAVE_ANDROID_OS
223 printf("\nResource table:\n");
224 res.print(false);
225#endif
226 }
227
228 Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
229 Asset::ACCESS_BUFFER);
230 if (manifestAsset == NULL) {
231 printf("\nNo AndroidManifest.xml found.\n");
232 } else {
233 printf("\nAndroid manifest:\n");
234 ResXMLTree tree;
235 tree.setTo(manifestAsset->getBuffer(true),
236 manifestAsset->getLength());
237 printXMLBlock(&tree);
238 }
239 delete manifestAsset;
240 }
241
242 result = 0;
243
244bail:
245 delete zip;
246 return result;
247}
248
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700249static void printResolvedResourceAttribute(const ResTable& resTable, const ResXMLTree& tree,
Maurice Chu76327312013-10-16 18:28:46 -0700250 uint32_t attrRes, String8 attrLabel, String8* outError)
251{
252 Res_value value;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700253 AaptXml::getResolvedResourceAttribute(resTable, tree, attrRes, &value, outError);
Maurice Chu76327312013-10-16 18:28:46 -0700254 if (*outError != "") {
255 *outError = "error print resolved resource attribute";
256 return;
257 }
258 if (value.dataType == Res_value::TYPE_STRING) {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700259 String8 result = AaptXml::getResolvedAttribute(resTable, tree, attrRes, outError);
Maurice Chu2675f762013-10-22 17:33:11 -0700260 printf("%s='%s'", attrLabel.string(),
261 ResTable::normalizeForOutput(result.string()).string());
Maurice Chu76327312013-10-16 18:28:46 -0700262 } else if (Res_value::TYPE_FIRST_INT <= value.dataType &&
263 value.dataType <= Res_value::TYPE_LAST_INT) {
264 printf("%s='%d'", attrLabel.string(), value.data);
265 } else {
266 printf("%s='0x%x'", attrLabel.string(), (int)value.data);
267 }
268}
269
Adam Lesinski282e1812014-01-23 18:17:42 -0800270// These are attribute resource constants for the platform, as found
271// in android.R.attr
272enum {
273 LABEL_ATTR = 0x01010001,
274 ICON_ATTR = 0x01010002,
275 NAME_ATTR = 0x01010003,
Adam Lesinskia5018c92013-09-30 16:23:15 -0700276 PERMISSION_ATTR = 0x01010006,
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700277 EXPORTED_ATTR = 0x01010010,
278 GRANT_URI_PERMISSIONS_ATTR = 0x0101001b,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700279 RESOURCE_ATTR = 0x01010025,
Adam Lesinski282e1812014-01-23 18:17:42 -0800280 DEBUGGABLE_ATTR = 0x0101000f,
281 VALUE_ATTR = 0x01010024,
282 VERSION_CODE_ATTR = 0x0101021b,
283 VERSION_NAME_ATTR = 0x0101021c,
284 SCREEN_ORIENTATION_ATTR = 0x0101001e,
285 MIN_SDK_VERSION_ATTR = 0x0101020c,
286 MAX_SDK_VERSION_ATTR = 0x01010271,
287 REQ_TOUCH_SCREEN_ATTR = 0x01010227,
288 REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
289 REQ_HARD_KEYBOARD_ATTR = 0x01010229,
290 REQ_NAVIGATION_ATTR = 0x0101022a,
291 REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
292 TARGET_SDK_VERSION_ATTR = 0x01010270,
293 TEST_ONLY_ATTR = 0x01010272,
294 ANY_DENSITY_ATTR = 0x0101026c,
295 GL_ES_VERSION_ATTR = 0x01010281,
296 SMALL_SCREEN_ATTR = 0x01010284,
297 NORMAL_SCREEN_ATTR = 0x01010285,
298 LARGE_SCREEN_ATTR = 0x01010286,
299 XLARGE_SCREEN_ATTR = 0x010102bf,
300 REQUIRED_ATTR = 0x0101028e,
Adam Lesinskicaf797c2014-08-22 12:56:26 -0700301 INSTALL_LOCATION_ATTR = 0x010102b7,
Adam Lesinski282e1812014-01-23 18:17:42 -0800302 SCREEN_SIZE_ATTR = 0x010102ca,
303 SCREEN_DENSITY_ATTR = 0x010102cb,
304 REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
305 COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
306 LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
307 PUBLIC_KEY_ATTR = 0x010103a6,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700308 CATEGORY_ATTR = 0x010103e8,
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800309 BANNER_ATTR = 0x10103f2,
Tim Kilbournd9b1cad2014-10-24 12:43:41 -0700310 ISGAME_ATTR = 0x10103f4,
Adam Lesinski282e1812014-01-23 18:17:42 -0800311};
312
Maurice Chu2675f762013-10-22 17:33:11 -0700313String8 getComponentName(String8 &pkgName, String8 &componentName) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800314 ssize_t idx = componentName.find(".");
315 String8 retStr(pkgName);
316 if (idx == 0) {
317 retStr += componentName;
318 } else if (idx < 0) {
319 retStr += ".";
320 retStr += componentName;
321 } else {
Maurice Chu2675f762013-10-22 17:33:11 -0700322 return componentName;
Adam Lesinski282e1812014-01-23 18:17:42 -0800323 }
Maurice Chu2675f762013-10-22 17:33:11 -0700324 return retStr;
Adam Lesinski282e1812014-01-23 18:17:42 -0800325}
326
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700327static void printCompatibleScreens(ResXMLTree& tree, String8* outError) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800328 size_t len;
329 ResXMLTree::event_code_t code;
330 int depth = 0;
331 bool first = true;
332 printf("compatible-screens:");
333 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
334 if (code == ResXMLTree::END_TAG) {
335 depth--;
336 if (depth < 0) {
337 break;
338 }
339 continue;
340 }
341 if (code != ResXMLTree::START_TAG) {
342 continue;
343 }
344 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700345 const char16_t* ctag16 = tree.getElementName(&len);
346 if (ctag16 == NULL) {
347 *outError = "failed to get XML element name (bad string pool)";
348 return;
349 }
350 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800351 if (tag == "screen") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700352 int32_t screenSize = AaptXml::getIntegerAttribute(tree,
353 SCREEN_SIZE_ATTR);
354 int32_t screenDensity = AaptXml::getIntegerAttribute(tree,
355 SCREEN_DENSITY_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -0800356 if (screenSize > 0 && screenDensity > 0) {
357 if (!first) {
358 printf(",");
359 }
360 first = false;
361 printf("'%d/%d'", screenSize, screenDensity);
362 }
363 }
364 }
365 printf("\n");
366}
367
Adam Lesinski58f1f362013-11-12 12:59:08 -0800368static void printUsesPermission(const String8& name, bool optional=false, int maxSdkVersion=-1) {
369 printf("uses-permission: name='%s'", ResTable::normalizeForOutput(name.string()).string());
370 if (maxSdkVersion != -1) {
371 printf(" maxSdkVersion='%d'", maxSdkVersion);
372 }
373 printf("\n");
374
375 if (optional) {
376 printf("optional-permission: name='%s'",
377 ResTable::normalizeForOutput(name.string()).string());
378 if (maxSdkVersion != -1) {
379 printf(" maxSdkVersion='%d'", maxSdkVersion);
380 }
381 printf("\n");
382 }
383}
384
385static void printUsesImpliedPermission(const String8& name, const String8& reason) {
386 printf("uses-implied-permission: name='%s' reason='%s'\n",
387 ResTable::normalizeForOutput(name.string()).string(),
388 ResTable::normalizeForOutput(reason.string()).string());
389}
390
Adam Lesinski94fc9122013-09-30 17:16:09 -0700391Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool offHost,
392 String8 *outError = NULL)
393{
394 Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER);
395 if (aidAsset == NULL) {
396 if (outError != NULL) *outError = "xml resource does not exist";
397 return Vector<String8>();
398 }
399
400 const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service");
401
402 bool withinApduService = false;
403 Vector<String8> categories;
404
405 String8 error;
406 ResXMLTree tree;
407 tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength());
408
409 size_t len;
410 int depth = 0;
411 ResXMLTree::event_code_t code;
412 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
413 if (code == ResXMLTree::END_TAG) {
414 depth--;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700415 const char16_t* ctag16 = tree.getElementName(&len);
416 if (ctag16 == NULL) {
417 *outError = "failed to get XML element name (bad string pool)";
418 return Vector<String8>();
419 }
420 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700421
422 if (depth == 0 && tag == serviceTagName) {
423 withinApduService = false;
424 }
425
426 } else if (code == ResXMLTree::START_TAG) {
427 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700428 const char16_t* ctag16 = tree.getElementName(&len);
429 if (ctag16 == NULL) {
430 *outError = "failed to get XML element name (bad string pool)";
431 return Vector<String8>();
432 }
433 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700434
435 if (depth == 1) {
436 if (tag == serviceTagName) {
437 withinApduService = true;
438 }
439 } else if (depth == 2 && withinApduService) {
440 if (tag == "aid-group") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700441 String8 category = AaptXml::getAttribute(tree, CATEGORY_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700442 if (error != "") {
443 if (outError != NULL) *outError = error;
444 return Vector<String8>();
445 }
446
447 categories.add(category);
448 }
449 }
450 }
451 }
452 aidAsset->close();
453 return categories;
454}
455
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700456static void printComponentPresence(const char* componentName) {
457 printf("provides-component:'%s'\n", componentName);
458}
459
Adam Lesinski2c72b682014-06-24 09:56:01 -0700460/**
461 * Represents a feature that has been automatically added due to
462 * a pre-requisite or some other reason.
463 */
464struct ImpliedFeature {
465 /**
466 * Name of the implied feature.
467 */
468 String8 name;
469
470 /**
471 * List of human-readable reasons for why this feature was implied.
472 */
473 SortedVector<String8> reasons;
474};
475
476/**
477 * Represents a <feature-group> tag in the AndroidManifest.xml
478 */
479struct FeatureGroup {
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700480 FeatureGroup() : openGLESVersion(-1) {}
481
Adam Lesinski2c72b682014-06-24 09:56:01 -0700482 /**
483 * Human readable label
484 */
485 String8 label;
486
487 /**
488 * Explicit features defined in the group
489 */
490 KeyedVector<String8, bool> features;
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700491
492 /**
493 * OpenGL ES version required
494 */
495 int openGLESVersion;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700496};
497
498static void addImpliedFeature(KeyedVector<String8, ImpliedFeature>* impliedFeatures,
499 const char* name, const char* reason) {
500 String8 name8(name);
501 ssize_t idx = impliedFeatures->indexOfKey(name8);
502 if (idx < 0) {
503 idx = impliedFeatures->add(name8, ImpliedFeature());
504 impliedFeatures->editValueAt(idx).name = name8;
505 }
506 impliedFeatures->editValueAt(idx).reasons.add(String8(reason));
507}
508
509static void printFeatureGroup(const FeatureGroup& grp,
510 const KeyedVector<String8, ImpliedFeature>* impliedFeatures = NULL) {
511 printf("feature-group: label='%s'\n", grp.label.string());
512
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700513 if (grp.openGLESVersion > 0) {
514 printf(" uses-gl-es: '0x%x'\n", grp.openGLESVersion);
515 }
516
Adam Lesinski2c72b682014-06-24 09:56:01 -0700517 const size_t numFeatures = grp.features.size();
518 for (size_t i = 0; i < numFeatures; i++) {
Adam Lesinski73a05112014-12-08 12:53:17 -0800519 const bool required = grp.features[i];
Adam Lesinski2c72b682014-06-24 09:56:01 -0700520
521 const String8& featureName = grp.features.keyAt(i);
Adam Lesinski73a05112014-12-08 12:53:17 -0800522 printf(" uses-feature%s: name='%s'\n", (required ? "" : "-not-required"),
Adam Lesinski2c72b682014-06-24 09:56:01 -0700523 ResTable::normalizeForOutput(featureName.string()).string());
524 }
525
526 const size_t numImpliedFeatures =
527 (impliedFeatures != NULL) ? impliedFeatures->size() : 0;
528 for (size_t i = 0; i < numImpliedFeatures; i++) {
529 const ImpliedFeature& impliedFeature = impliedFeatures->valueAt(i);
530 if (grp.features.indexOfKey(impliedFeature.name) >= 0) {
531 // The feature is explicitly set, no need to use implied
532 // definition.
533 continue;
534 }
535
536 String8 printableFeatureName(ResTable::normalizeForOutput(
537 impliedFeature.name.string()));
538 printf(" uses-feature: name='%s'\n", printableFeatureName.string());
539 printf(" uses-implied-feature: name='%s' reason='",
540 printableFeatureName.string());
541 const size_t numReasons = impliedFeature.reasons.size();
542 for (size_t j = 0; j < numReasons; j++) {
543 printf("%s", impliedFeature.reasons[j].string());
544 if (j + 2 < numReasons) {
545 printf(", ");
546 } else if (j + 1 < numReasons) {
547 printf(", and ");
548 }
549 }
550 printf("'\n");
551 }
552}
553
554static void addParentFeatures(FeatureGroup* grp, const String8& name) {
555 if (name == "android.hardware.camera.autofocus" ||
556 name == "android.hardware.camera.flash") {
557 grp->features.add(String8("android.hardware.camera"), true);
558 } else if (name == "android.hardware.location.gps" ||
559 name == "android.hardware.location.network") {
560 grp->features.add(String8("android.hardware.location"), true);
561 } else if (name == "android.hardware.touchscreen.multitouch") {
562 grp->features.add(String8("android.hardware.touchscreen"), true);
563 } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
564 grp->features.add(String8("android.hardware.touchscreen.multitouch"), true);
565 grp->features.add(String8("android.hardware.touchscreen"), true);
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700566 } else if (name == "android.hardware.opengles.aep") {
567 const int openGLESVersion31 = 0x00030001;
568 if (openGLESVersion31 > grp->openGLESVersion) {
569 grp->openGLESVersion = openGLESVersion31;
570 }
Adam Lesinski2c72b682014-06-24 09:56:01 -0700571 }
572}
573
Adam Lesinski282e1812014-01-23 18:17:42 -0800574/*
575 * Handle the "dump" command, to extract select data from an archive.
576 */
577extern char CONSOLE_DATA[2925]; // see EOF
578int doDump(Bundle* bundle)
579{
580 status_t result = UNKNOWN_ERROR;
Adam Lesinski282e1812014-01-23 18:17:42 -0800581
582 if (bundle->getFileSpecCount() < 1) {
583 fprintf(stderr, "ERROR: no dump option specified\n");
584 return 1;
585 }
586
587 if (bundle->getFileSpecCount() < 2) {
588 fprintf(stderr, "ERROR: no dump file specified\n");
589 return 1;
590 }
591
592 const char* option = bundle->getFileSpecEntry(0);
593 const char* filename = bundle->getFileSpecEntry(1);
594
595 AssetManager assets;
Narayan Kamathf85e41f2014-01-24 14:14:30 +0000596 int32_t assetsCookie;
Adam Lesinski282e1812014-01-23 18:17:42 -0800597 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
598 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
599 return 1;
600 }
601
602 // Make a dummy config for retrieving resources... we need to supply
603 // non-default values for some configs so that we can retrieve resources
604 // in the app that don't have a default. The most important of these is
605 // the API version because key resources like icons will have an implicit
606 // version if they are using newer config types like density.
607 ResTable_config config;
Narayan Kamath91447d82014-01-21 15:32:36 +0000608 memset(&config, 0, sizeof(ResTable_config));
Adam Lesinski282e1812014-01-23 18:17:42 -0800609 config.language[0] = 'e';
610 config.language[1] = 'n';
611 config.country[0] = 'U';
612 config.country[1] = 'S';
613 config.orientation = ResTable_config::ORIENTATION_PORT;
614 config.density = ResTable_config::DENSITY_MEDIUM;
615 config.sdkVersion = 10000; // Very high.
616 config.screenWidthDp = 320;
617 config.screenHeightDp = 480;
618 config.smallestScreenWidthDp = 320;
Adam Lesinskic2dea8d2014-08-04 16:40:41 -0700619 config.screenLayout |= ResTable_config::SCREENSIZE_NORMAL;
Adam Lesinski282e1812014-01-23 18:17:42 -0800620 assets.setConfiguration(config);
621
622 const ResTable& res = assets.getResources(false);
623 if (&res == NULL) {
624 fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
Adam Lesinski63e646e2014-07-30 11:40:39 -0700625 return 1;
Adam Lesinski25e9d552014-05-19 15:01:43 -0700626 } else if (res.getError() != NO_ERROR) {
627 fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
Adam Lesinski63e646e2014-07-30 11:40:39 -0700628 return 1;
Adam Lesinski282e1812014-01-23 18:17:42 -0800629 }
630
Adam Lesinski2cb761e2014-08-15 13:59:02 -0700631 // The dynamicRefTable can be null if there are no resources for this asset cookie.
632 // This fine.
Adam Lesinski63e646e2014-07-30 11:40:39 -0700633 const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700634
635 Asset* asset = NULL;
636
Adam Lesinski282e1812014-01-23 18:17:42 -0800637 if (strcmp("resources", option) == 0) {
638#ifndef HAVE_ANDROID_OS
639 res.print(bundle->getValues());
640#endif
641
642 } else if (strcmp("strings", option) == 0) {
643 const ResStringPool* pool = res.getTableStringBlock(0);
644 printStringPool(pool);
645
646 } else if (strcmp("xmltree", option) == 0) {
647 if (bundle->getFileSpecCount() < 3) {
648 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
649 goto bail;
650 }
651
652 for (int i=2; i<bundle->getFileSpecCount(); i++) {
653 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700654 ResXMLTree tree(dynamicRefTable);
655 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800656 if (asset == NULL) {
657 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
658 goto bail;
659 }
660
661 if (tree.setTo(asset->getBuffer(true),
662 asset->getLength()) != NO_ERROR) {
663 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
664 goto bail;
665 }
666 tree.restart();
667 printXMLBlock(&tree);
668 tree.uninit();
669 delete asset;
670 asset = NULL;
671 }
672
673 } else if (strcmp("xmlstrings", option) == 0) {
674 if (bundle->getFileSpecCount() < 3) {
675 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
676 goto bail;
677 }
678
679 for (int i=2; i<bundle->getFileSpecCount(); i++) {
680 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700681 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800682 if (asset == NULL) {
683 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
684 goto bail;
685 }
686
Adam Lesinski63e646e2014-07-30 11:40:39 -0700687 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800688 if (tree.setTo(asset->getBuffer(true),
689 asset->getLength()) != NO_ERROR) {
690 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
691 goto bail;
692 }
693 printStringPool(&tree.getStrings());
694 delete asset;
695 asset = NULL;
696 }
697
698 } else {
Adam Lesinski63e646e2014-07-30 11:40:39 -0700699 asset = assets.openNonAsset(assetsCookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800700 if (asset == NULL) {
701 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
702 goto bail;
703 }
704
Adam Lesinski63e646e2014-07-30 11:40:39 -0700705 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800706 if (tree.setTo(asset->getBuffer(true),
707 asset->getLength()) != NO_ERROR) {
708 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
709 goto bail;
710 }
711 tree.restart();
712
713 if (strcmp("permissions", option) == 0) {
714 size_t len;
715 ResXMLTree::event_code_t code;
716 int depth = 0;
717 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
718 if (code == ResXMLTree::END_TAG) {
719 depth--;
720 continue;
721 }
722 if (code != ResXMLTree::START_TAG) {
723 continue;
724 }
725 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700726 const char16_t* ctag16 = tree.getElementName(&len);
727 if (ctag16 == NULL) {
728 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
729 goto bail;
730 }
731 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800732 //printf("Depth %d tag %s\n", depth, tag.string());
733 if (depth == 1) {
734 if (tag != "manifest") {
735 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
736 goto bail;
737 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700738 String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -0700739 printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800740 } else if (depth == 2 && tag == "permission") {
741 String8 error;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700742 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -0800743 if (error != "") {
744 fprintf(stderr, "ERROR: %s\n", error.string());
745 goto bail;
746 }
Maurice Chu2675f762013-10-22 17:33:11 -0700747 printf("permission: %s\n",
748 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800749 } else if (depth == 2 && tag == "uses-permission") {
750 String8 error;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700751 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -0800752 if (error != "") {
753 fprintf(stderr, "ERROR: %s\n", error.string());
754 goto bail;
755 }
Adam Lesinski58f1f362013-11-12 12:59:08 -0800756 printUsesPermission(name,
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700757 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
758 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
Adam Lesinski282e1812014-01-23 18:17:42 -0800759 }
760 }
761 } else if (strcmp("badging", option) == 0) {
762 Vector<String8> locales;
763 res.getLocales(&locales);
764
765 Vector<ResTable_config> configs;
766 res.getConfigurations(&configs);
767 SortedVector<int> densities;
768 const size_t NC = configs.size();
769 for (size_t i=0; i<NC; i++) {
770 int dens = configs[i].density;
771 if (dens == 0) {
772 dens = 160;
773 }
774 densities.add(dens);
775 }
776
777 size_t len;
778 ResXMLTree::event_code_t code;
779 int depth = 0;
780 String8 error;
781 bool withinActivity = false;
782 bool isMainActivity = false;
783 bool isLauncherActivity = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800784 bool isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800785 bool isSearchable = false;
786 bool withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700787 bool withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700788 bool withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800789 bool withinReceiver = false;
790 bool withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700791 bool withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800792 bool withinIntentFilter = false;
793 bool hasMainActivity = false;
794 bool hasOtherActivities = false;
795 bool hasOtherReceivers = false;
796 bool hasOtherServices = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700797 bool hasIntentFilter = false;
798
Adam Lesinski282e1812014-01-23 18:17:42 -0800799 bool hasWallpaperService = false;
800 bool hasImeService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700801 bool hasAccessibilityService = false;
802 bool hasPrintService = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800803 bool hasWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700804 bool hasDeviceAdminReceiver = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700805 bool hasPaymentService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700806 bool hasDocumentsProvider = false;
807 bool hasCameraActivity = false;
808 bool hasCameraSecureActivity = false;
809 bool hasLauncher = false;
810 bool hasNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400811 bool hasDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700812
Adam Lesinski282e1812014-01-23 18:17:42 -0800813 bool actMainActivity = false;
814 bool actWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700815 bool actDeviceAdminEnabled = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800816 bool actImeService = false;
817 bool actWallpaperService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700818 bool actAccessibilityService = false;
819 bool actPrintService = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700820 bool actHostApduService = false;
821 bool actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700822 bool actDocumentsProvider = false;
823 bool actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400824 bool actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700825 bool actCamera = false;
826 bool actCameraSecure = false;
827 bool catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700828 bool hasMetaHostPaymentCategory = false;
829 bool hasMetaOffHostPaymentCategory = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700830
831 // These permissions are required by services implementing services
832 // the system binds to (IME, Accessibility, PrintServices, etc.)
833 bool hasBindDeviceAdminPermission = false;
834 bool hasBindInputMethodPermission = false;
835 bool hasBindAccessibilityServicePermission = false;
836 bool hasBindPrintServicePermission = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700837 bool hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700838 bool hasRequiredSafAttributes = false;
839 bool hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400840 bool hasBindDreamServicePermission = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800841
842 // These two implement the implicit permissions that are granted
843 // to pre-1.6 applications.
844 bool hasWriteExternalStoragePermission = false;
845 bool hasReadPhoneStatePermission = false;
846
847 // If an app requests write storage, they will also get read storage.
848 bool hasReadExternalStoragePermission = false;
849
850 // Implement transition to read and write call log.
851 bool hasReadContactsPermission = false;
852 bool hasWriteContactsPermission = false;
853 bool hasReadCallLogPermission = false;
854 bool hasWriteCallLogPermission = false;
855
Adam Lesinskie47fd122014-08-15 22:25:36 -0700856 // If an app declares itself as multiArch, we report the
857 // native libraries differently.
858 bool hasMultiArch = false;
859
Adam Lesinski282e1812014-01-23 18:17:42 -0800860 // This next group of variables is used to implement a group of
861 // backward-compatibility heuristics necessitated by the addition of
862 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
863 // heuristic is "if an app requests a permission but doesn't explicitly
864 // request the corresponding <uses-feature>, presume it's there anyway".
Adam Lesinski2c72b682014-06-24 09:56:01 -0700865
Adam Lesinski282e1812014-01-23 18:17:42 -0800866 // 2.2 also added some other features that apps can request, but that
867 // have no corresponding permission, so we cannot implement any
868 // back-compatibility heuristic for them. The below are thus unnecessary
869 // (but are retained here for documentary purposes.)
870 //bool specCompassFeature = false;
871 //bool specAccelerometerFeature = false;
872 //bool specProximityFeature = false;
873 //bool specAmbientLightFeature = false;
874 //bool specLiveWallpaperFeature = false;
875
876 int targetSdk = 0;
877 int smallScreen = 1;
878 int normalScreen = 1;
879 int largeScreen = 1;
880 int xlargeScreen = 1;
881 int anyDensity = 1;
882 int requiresSmallestWidthDp = 0;
883 int compatibleWidthLimitDp = 0;
884 int largestWidthLimitDp = 0;
885 String8 pkg;
886 String8 activityName;
887 String8 activityLabel;
888 String8 activityIcon;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800889 String8 activityBanner;
Adam Lesinski282e1812014-01-23 18:17:42 -0800890 String8 receiverName;
891 String8 serviceName;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700892 Vector<String8> supportedInput;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700893
894 FeatureGroup commonFeatures;
895 Vector<FeatureGroup> featureGroups;
896 KeyedVector<String8, ImpliedFeature> impliedFeatures;
897
Adam Lesinski282e1812014-01-23 18:17:42 -0800898 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
899 if (code == ResXMLTree::END_TAG) {
900 depth--;
901 if (depth < 2) {
Michael Wrightec4fdec2013-09-06 16:50:52 -0700902 if (withinSupportsInput && !supportedInput.isEmpty()) {
903 printf("supports-input: '");
904 const size_t N = supportedInput.size();
905 for (size_t i=0; i<N; i++) {
Maurice Chu2675f762013-10-22 17:33:11 -0700906 printf("%s", ResTable::normalizeForOutput(
907 supportedInput[i].string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -0700908 if (i != N - 1) {
909 printf("' '");
910 } else {
911 printf("'\n");
912 }
913 }
914 supportedInput.clear();
915 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800916 withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700917 withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700918 withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800919 } else if (depth < 3) {
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800920 if (withinActivity && isMainActivity) {
Maurice Chu2675f762013-10-22 17:33:11 -0700921 String8 aName(getComponentName(pkg, activityName));
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800922 if (isLauncherActivity) {
923 printf("launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800924 if (aName.length() > 0) {
925 printf(" name='%s' ",
926 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800927 }
928 printf(" label='%s' icon='%s'\n",
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800929 ResTable::normalizeForOutput(activityLabel.string()).string(),
930 ResTable::normalizeForOutput(activityIcon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800931 }
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800932 if (isLeanbackLauncherActivity) {
933 printf("leanback-launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800934 if (aName.length() > 0) {
935 printf(" name='%s' ",
936 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800937 }
938 printf(" label='%s' icon='%s' banner='%s'\n",
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800939 ResTable::normalizeForOutput(activityLabel.string()).string(),
940 ResTable::normalizeForOutput(activityIcon.string()).string(),
941 ResTable::normalizeForOutput(activityBanner.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800942 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800943 }
944 if (!hasIntentFilter) {
945 hasOtherActivities |= withinActivity;
946 hasOtherReceivers |= withinReceiver;
947 hasOtherServices |= withinService;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700948 } else {
949 if (withinService) {
950 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
951 hasBindNfcServicePermission);
952 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
953 hasBindNfcServicePermission);
954 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800955 }
956 withinActivity = false;
957 withinService = false;
958 withinReceiver = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700959 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800960 hasIntentFilter = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800961 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800962 } else if (depth < 4) {
963 if (withinIntentFilter) {
964 if (withinActivity) {
965 hasMainActivity |= actMainActivity;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700966 hasLauncher |= catLauncher;
967 hasCameraActivity |= actCamera;
968 hasCameraSecureActivity |= actCameraSecure;
969 hasOtherActivities |= !actMainActivity && !actCamera && !actCameraSecure;
Adam Lesinski282e1812014-01-23 18:17:42 -0800970 } else if (withinReceiver) {
971 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700972 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
973 hasBindDeviceAdminPermission);
974 hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled);
Adam Lesinski282e1812014-01-23 18:17:42 -0800975 } else if (withinService) {
976 hasImeService |= actImeService;
977 hasWallpaperService |= actWallpaperService;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700978 hasAccessibilityService |= (actAccessibilityService &&
979 hasBindAccessibilityServicePermission);
980 hasPrintService |= (actPrintService && hasBindPrintServicePermission);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700981 hasNotificationListenerService |= actNotificationListenerService &&
982 hasBindNotificationListenerServicePermission;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400983 hasDreamService |= actDreamService && hasBindDreamServicePermission;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700984 hasOtherServices |= (!actImeService && !actWallpaperService &&
Adam Lesinski94fc9122013-09-30 17:16:09 -0700985 !actAccessibilityService && !actPrintService &&
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700986 !actHostApduService && !actOffHostApduService &&
987 !actNotificationListenerService);
988 } else if (withinProvider) {
989 hasDocumentsProvider |= actDocumentsProvider && hasRequiredSafAttributes;
Adam Lesinski282e1812014-01-23 18:17:42 -0800990 }
991 }
992 withinIntentFilter = false;
993 }
994 continue;
995 }
996 if (code != ResXMLTree::START_TAG) {
997 continue;
998 }
999 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001000
1001 const char16_t* ctag16 = tree.getElementName(&len);
1002 if (ctag16 == NULL) {
1003 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
1004 goto bail;
1005 }
1006 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -08001007 //printf("Depth %d, %s\n", depth, tag.string());
1008 if (depth == 1) {
1009 if (tag != "manifest") {
1010 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
1011 goto bail;
1012 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001013 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -07001014 printf("package: name='%s' ",
1015 ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001016 int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR,
1017 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001018 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001019 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n",
1020 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001021 goto bail;
1022 }
1023 if (versionCode > 0) {
1024 printf("versionCode='%d' ", versionCode);
1025 } else {
1026 printf("versionCode='' ");
1027 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001028 String8 versionName = AaptXml::getResolvedAttribute(res, tree,
1029 VERSION_NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001030 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001031 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n",
1032 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001033 goto bail;
1034 }
Adam Lesinski25d35a92014-08-11 09:41:56 -07001035 printf("versionName='%s'",
Maurice Chu2675f762013-10-22 17:33:11 -07001036 ResTable::normalizeForOutput(versionName.string()).string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001037
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001038 String8 splitName = AaptXml::getAttribute(tree, NULL, "split");
Adam Lesinski25d35a92014-08-11 09:41:56 -07001039 if (!splitName.isEmpty()) {
1040 printf(" split='%s'", ResTable::normalizeForOutput(
1041 splitName.string()).string());
1042 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001043
Adam Lesinski5283fab2014-08-29 11:23:55 -07001044 String8 platformVersionName = AaptXml::getAttribute(tree, NULL,
1045 "platformBuildVersionName");
1046 printf(" platformBuildVersionName='%s'", platformVersionName.string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001047 printf("\n");
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001048
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001049 int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
1050 INSTALL_LOCATION_ATTR, &error);
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001051 if (error != "") {
1052 fprintf(stderr, "ERROR getting 'android:installLocation' attribute: %s\n",
1053 error.string());
1054 goto bail;
1055 }
1056
1057 if (installLocation >= 0) {
1058 printf("install-location:'");
1059 switch (installLocation) {
1060 case 0:
1061 printf("auto");
1062 break;
1063 case 1:
1064 printf("internalOnly");
1065 break;
1066 case 2:
1067 printf("preferExternal");
1068 break;
1069 default:
1070 fprintf(stderr, "Invalid installLocation %d\n", installLocation);
1071 goto bail;
1072 }
1073 printf("'\n");
1074 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001075 } else if (depth == 2) {
1076 withinApplication = false;
1077 if (tag == "application") {
1078 withinApplication = true;
1079
1080 String8 label;
1081 const size_t NL = locales.size();
1082 for (size_t i=0; i<NL; i++) {
1083 const char* localeStr = locales[i].string();
1084 assets.setLocale(localeStr != NULL ? localeStr : "");
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001085 String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1086 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001087 if (llabel != "") {
1088 if (localeStr == NULL || strlen(localeStr) == 0) {
1089 label = llabel;
Maurice Chu2675f762013-10-22 17:33:11 -07001090 printf("application-label:'%s'\n",
1091 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001092 } else {
1093 if (label == "") {
1094 label = llabel;
1095 }
1096 printf("application-label-%s:'%s'\n", localeStr,
Maurice Chu2675f762013-10-22 17:33:11 -07001097 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001098 }
1099 }
1100 }
1101
1102 ResTable_config tmpConfig = config;
1103 const size_t ND = densities.size();
1104 for (size_t i=0; i<ND; i++) {
1105 tmpConfig.density = densities[i];
1106 assets.setConfiguration(tmpConfig);
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001107 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1108 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001109 if (icon != "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001110 printf("application-icon-%d:'%s'\n", densities[i],
1111 ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001112 }
1113 }
1114 assets.setConfiguration(config);
1115
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001116 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001117 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001118 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1119 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001120 goto bail;
1121 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001122 int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0,
1123 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001124 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001125 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n",
1126 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001127 goto bail;
1128 }
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001129
1130 String8 banner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR, &error);
1131 if (error != "") {
1132 fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
1133 error.string());
1134 goto bail;
1135 }
Maurice Chu2675f762013-10-22 17:33:11 -07001136 printf("application: label='%s' ",
1137 ResTable::normalizeForOutput(label.string()).string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001138 printf("icon='%s'", ResTable::normalizeForOutput(icon.string()).string());
1139 if (banner != "") {
1140 printf(" banner='%s'", ResTable::normalizeForOutput(banner.string()).string());
1141 }
1142 printf("\n");
Adam Lesinski282e1812014-01-23 18:17:42 -08001143 if (testOnly != 0) {
1144 printf("testOnly='%d'\n", testOnly);
1145 }
1146
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001147 int32_t isGame = AaptXml::getResolvedIntegerAttribute(res, tree,
1148 ISGAME_ATTR, 0, &error);
1149 if (error != "") {
1150 fprintf(stderr, "ERROR getting 'android:isGame' attribute: %s\n",
1151 error.string());
1152 goto bail;
1153 }
1154 if (isGame != 0) {
1155 printf("application-isGame\n");
1156 }
1157
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001158 int32_t debuggable = AaptXml::getResolvedIntegerAttribute(res, tree,
1159 DEBUGGABLE_ATTR, 0, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001160 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001161 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n",
1162 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001163 goto bail;
1164 }
1165 if (debuggable != 0) {
1166 printf("application-debuggable\n");
1167 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07001168
1169 // We must search by name because the multiArch flag hasn't been API
1170 // frozen yet.
1171 int32_t multiArchIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
1172 "multiArch");
1173 if (multiArchIndex >= 0) {
1174 Res_value value;
1175 if (tree.getAttributeValue(multiArchIndex, &value) != NO_ERROR) {
1176 if (value.dataType >= Res_value::TYPE_FIRST_INT &&
1177 value.dataType <= Res_value::TYPE_LAST_INT) {
1178 hasMultiArch = value.data;
1179 }
1180 }
1181 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001182 } else if (tag == "uses-sdk") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001183 int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001184 if (error != "") {
1185 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001186 String8 name = AaptXml::getResolvedAttribute(res, tree,
1187 MIN_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001188 if (error != "") {
1189 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
1190 error.string());
1191 goto bail;
1192 }
1193 if (name == "Donut") targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001194 printf("sdkVersion:'%s'\n",
1195 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001196 } else if (code != -1) {
1197 targetSdk = code;
1198 printf("sdkVersion:'%d'\n", code);
1199 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001200 code = AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -08001201 if (code != -1) {
1202 printf("maxSdkVersion:'%d'\n", code);
1203 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001204 code = AaptXml::getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001205 if (error != "") {
1206 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001207 String8 name = AaptXml::getResolvedAttribute(res, tree,
1208 TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001209 if (error != "") {
1210 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
1211 error.string());
1212 goto bail;
1213 }
1214 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001215 printf("targetSdkVersion:'%s'\n",
1216 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001217 } else if (code != -1) {
1218 if (targetSdk < code) {
1219 targetSdk = code;
1220 }
1221 printf("targetSdkVersion:'%d'\n", code);
1222 }
1223 } else if (tag == "uses-configuration") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001224 int32_t reqTouchScreen = AaptXml::getIntegerAttribute(tree,
1225 REQ_TOUCH_SCREEN_ATTR, 0);
1226 int32_t reqKeyboardType = AaptXml::getIntegerAttribute(tree,
1227 REQ_KEYBOARD_TYPE_ATTR, 0);
1228 int32_t reqHardKeyboard = AaptXml::getIntegerAttribute(tree,
1229 REQ_HARD_KEYBOARD_ATTR, 0);
1230 int32_t reqNavigation = AaptXml::getIntegerAttribute(tree,
1231 REQ_NAVIGATION_ATTR, 0);
1232 int32_t reqFiveWayNav = AaptXml::getIntegerAttribute(tree,
1233 REQ_FIVE_WAY_NAV_ATTR, 0);
Adam Lesinski282e1812014-01-23 18:17:42 -08001234 printf("uses-configuration:");
1235 if (reqTouchScreen != 0) {
1236 printf(" reqTouchScreen='%d'", reqTouchScreen);
1237 }
1238 if (reqKeyboardType != 0) {
1239 printf(" reqKeyboardType='%d'", reqKeyboardType);
1240 }
1241 if (reqHardKeyboard != 0) {
1242 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1243 }
1244 if (reqNavigation != 0) {
1245 printf(" reqNavigation='%d'", reqNavigation);
1246 }
1247 if (reqFiveWayNav != 0) {
1248 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1249 }
1250 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001251 } else if (tag == "supports-input") {
1252 withinSupportsInput = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001253 } else if (tag == "supports-screens") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001254 smallScreen = AaptXml::getIntegerAttribute(tree,
1255 SMALL_SCREEN_ATTR, 1);
1256 normalScreen = AaptXml::getIntegerAttribute(tree,
1257 NORMAL_SCREEN_ATTR, 1);
1258 largeScreen = AaptXml::getIntegerAttribute(tree,
1259 LARGE_SCREEN_ATTR, 1);
1260 xlargeScreen = AaptXml::getIntegerAttribute(tree,
1261 XLARGE_SCREEN_ATTR, 1);
1262 anyDensity = AaptXml::getIntegerAttribute(tree,
1263 ANY_DENSITY_ATTR, 1);
1264 requiresSmallestWidthDp = AaptXml::getIntegerAttribute(tree,
1265 REQUIRES_SMALLEST_WIDTH_DP_ATTR, 0);
1266 compatibleWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1267 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, 0);
1268 largestWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1269 LARGEST_WIDTH_LIMIT_DP_ATTR, 0);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001270 } else if (tag == "feature-group") {
1271 withinFeatureGroup = true;
1272 FeatureGroup group;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001273 group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001274 if (error != "") {
1275 fprintf(stderr, "ERROR getting 'android:label' attribute:"
1276 " %s\n", error.string());
1277 goto bail;
1278 }
1279 featureGroups.add(group);
1280
Adam Lesinski282e1812014-01-23 18:17:42 -08001281 } else if (tag == "uses-feature") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001282 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001283 if (name != "" && error == "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001284 int req = AaptXml::getIntegerAttribute(tree,
1285 REQUIRED_ATTR, 1);
Adam Lesinski282e1812014-01-23 18:17:42 -08001286
Adam Lesinski2c72b682014-06-24 09:56:01 -07001287 commonFeatures.features.add(name, req);
1288 if (req) {
1289 addParentFeatures(&commonFeatures, name);
Adam Lesinski282e1812014-01-23 18:17:42 -08001290 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001291 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001292 int vers = AaptXml::getIntegerAttribute(tree,
Adam Lesinski282e1812014-01-23 18:17:42 -08001293 GL_ES_VERSION_ATTR, &error);
1294 if (error == "") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001295 if (vers > commonFeatures.openGLESVersion) {
1296 commonFeatures.openGLESVersion = vers;
1297 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001298 }
1299 }
1300 } else if (tag == "uses-permission") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001301 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001302 if (name != "" && error == "") {
1303 if (name == "android.permission.CAMERA") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001304 addImpliedFeature(&impliedFeatures, "android.hardware.camera",
Adam Lesinski2c72b682014-06-24 09:56:01 -07001305 String8::format("requested %s permission", name.string())
1306 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001307 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001308 addImpliedFeature(&impliedFeatures, "android.hardware.location.gps",
1309 String8::format("requested %s permission", name.string())
1310 .string());
1311 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1312 String8::format("requested %s permission", name.string())
1313 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001314 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001315 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1316 String8::format("requested %s permission", name.string())
1317 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001318 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001319 addImpliedFeature(&impliedFeatures, "android.hardware.location.network",
1320 String8::format("requested %s permission", name.string())
1321 .string());
1322 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1323 String8::format("requested %s permission", name.string())
1324 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001325 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
1326 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001327 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1328 String8::format("requested %s permission", name.string())
1329 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001330 } else if (name == "android.permission.BLUETOOTH" ||
1331 name == "android.permission.BLUETOOTH_ADMIN") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001332 if (targetSdk > 4) {
1333 addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
1334 String8::format("requested %s permission", name.string())
1335 .string());
1336 addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
1337 "targetSdkVersion > 4");
1338 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001339 } else if (name == "android.permission.RECORD_AUDIO") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001340 addImpliedFeature(&impliedFeatures, "android.hardware.microphone",
1341 String8::format("requested %s permission", name.string())
1342 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001343 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1344 name == "android.permission.CHANGE_WIFI_STATE" ||
1345 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001346 addImpliedFeature(&impliedFeatures, "android.hardware.wifi",
1347 String8::format("requested %s permission", name.string())
1348 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001349 } else if (name == "android.permission.CALL_PHONE" ||
1350 name == "android.permission.CALL_PRIVILEGED" ||
1351 name == "android.permission.MODIFY_PHONE_STATE" ||
1352 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1353 name == "android.permission.READ_SMS" ||
1354 name == "android.permission.RECEIVE_SMS" ||
1355 name == "android.permission.RECEIVE_MMS" ||
1356 name == "android.permission.RECEIVE_WAP_PUSH" ||
1357 name == "android.permission.SEND_SMS" ||
1358 name == "android.permission.WRITE_APN_SETTINGS" ||
1359 name == "android.permission.WRITE_SMS") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001360 addImpliedFeature(&impliedFeatures, "android.hardware.telephony",
1361 String8("requested a telephony permission").string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001362 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1363 hasWriteExternalStoragePermission = true;
1364 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1365 hasReadExternalStoragePermission = true;
1366 } else if (name == "android.permission.READ_PHONE_STATE") {
1367 hasReadPhoneStatePermission = true;
1368 } else if (name == "android.permission.READ_CONTACTS") {
1369 hasReadContactsPermission = true;
1370 } else if (name == "android.permission.WRITE_CONTACTS") {
1371 hasWriteContactsPermission = true;
1372 } else if (name == "android.permission.READ_CALL_LOG") {
1373 hasReadCallLogPermission = true;
1374 } else if (name == "android.permission.WRITE_CALL_LOG") {
1375 hasWriteCallLogPermission = true;
1376 }
Adam Lesinski58f1f362013-11-12 12:59:08 -08001377
1378 printUsesPermission(name,
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001379 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
1380 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
Adam Lesinski58f1f362013-11-12 12:59:08 -08001381 } else {
Adam Lesinski282e1812014-01-23 18:17:42 -08001382 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1383 error.string());
1384 goto bail;
1385 }
1386 } else if (tag == "uses-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001387 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001388 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001389 printf("uses-package:'%s'\n",
1390 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001391 } else {
1392 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1393 error.string());
1394 goto bail;
1395 }
1396 } else if (tag == "original-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001397 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001398 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001399 printf("original-package:'%s'\n",
1400 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001401 } else {
1402 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1403 error.string());
1404 goto bail;
1405 }
1406 } else if (tag == "supports-gl-texture") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001407 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001408 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001409 printf("supports-gl-texture:'%s'\n",
1410 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001411 } else {
1412 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1413 error.string());
1414 goto bail;
1415 }
1416 } else if (tag == "compatible-screens") {
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001417 printCompatibleScreens(tree, &error);
1418 if (error != "") {
1419 fprintf(stderr, "ERROR getting compatible screens: %s\n",
1420 error.string());
1421 goto bail;
1422 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001423 depth--;
1424 } else if (tag == "package-verifier") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001425 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001426 if (name != "" && error == "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001427 String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001428 if (publicKey != "" && error == "") {
1429 printf("package-verifier: name='%s' publicKey='%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001430 ResTable::normalizeForOutput(name.string()).string(),
1431 ResTable::normalizeForOutput(publicKey.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001432 }
1433 }
1434 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001435 } else if (depth == 3) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001436 withinActivity = false;
1437 withinReceiver = false;
1438 withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001439 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001440 hasIntentFilter = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001441 hasMetaHostPaymentCategory = false;
1442 hasMetaOffHostPaymentCategory = false;
1443 hasBindDeviceAdminPermission = false;
1444 hasBindInputMethodPermission = false;
1445 hasBindAccessibilityServicePermission = false;
1446 hasBindPrintServicePermission = false;
1447 hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001448 hasRequiredSafAttributes = false;
1449 hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001450 hasBindDreamServicePermission = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001451 if (withinApplication) {
1452 if(tag == "activity") {
1453 withinActivity = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001454 activityName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001455 if (error != "") {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001456 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1457 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001458 goto bail;
1459 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001460
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001461 activityLabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1462 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001463 if (error != "") {
1464 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
1465 error.string());
1466 goto bail;
1467 }
1468
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001469 activityIcon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1470 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001471 if (error != "") {
1472 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1473 error.string());
1474 goto bail;
1475 }
1476
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001477 activityBanner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1478 &error);
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001479 if (error != "") {
1480 fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
1481 error.string());
1482 goto bail;
1483 }
1484
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001485 int32_t orien = AaptXml::getResolvedIntegerAttribute(res, tree,
Michael Wrightec4fdec2013-09-06 16:50:52 -07001486 SCREEN_ORIENTATION_ATTR, &error);
1487 if (error == "") {
1488 if (orien == 0 || orien == 6 || orien == 8) {
1489 // Requests landscape, sensorLandscape, or reverseLandscape.
Adam Lesinski2c72b682014-06-24 09:56:01 -07001490 addImpliedFeature(&impliedFeatures, "android.hardware.screen.landscape",
1491 "one or more activities have specified a landscape orientation");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001492 } else if (orien == 1 || orien == 7 || orien == 9) {
1493 // Requests portrait, sensorPortrait, or reversePortrait.
Adam Lesinski2c72b682014-06-24 09:56:01 -07001494 addImpliedFeature(&impliedFeatures, "android.hardware.screen.portrait",
1495 "one or more activities have specified a portrait orientation");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001496 }
1497 }
1498 } else if (tag == "uses-library") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001499 String8 libraryName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001500 if (error != "") {
1501 fprintf(stderr,
1502 "ERROR getting 'android:name' attribute for uses-library"
1503 " %s\n", error.string());
1504 goto bail;
1505 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001506 int req = AaptXml::getIntegerAttribute(tree,
1507 REQUIRED_ATTR, 1);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001508 printf("uses-library%s:'%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001509 req ? "" : "-not-required", ResTable::normalizeForOutput(
1510 libraryName.string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001511 } else if (tag == "receiver") {
1512 withinReceiver = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001513 receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001514
1515 if (error != "") {
1516 fprintf(stderr,
1517 "ERROR getting 'android:name' attribute for receiver:"
1518 " %s\n", error.string());
1519 goto bail;
1520 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001521
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001522 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1523 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001524 if (error == "") {
1525 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1526 hasBindDeviceAdminPermission = true;
1527 }
1528 } else {
1529 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1530 " receiver '%s': %s\n", receiverName.string(), error.string());
1531 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001532 } else if (tag == "service") {
1533 withinService = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001534 serviceName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001535
1536 if (error != "") {
1537 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1538 "service:%s\n", error.string());
1539 goto bail;
1540 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001541
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001542 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1543 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001544 if (error == "") {
1545 if (permission == "android.permission.BIND_INPUT_METHOD") {
1546 hasBindInputMethodPermission = true;
1547 } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") {
1548 hasBindAccessibilityServicePermission = true;
1549 } else if (permission == "android.permission.BIND_PRINT_SERVICE") {
1550 hasBindPrintServicePermission = true;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001551 } else if (permission == "android.permission.BIND_NFC_SERVICE") {
1552 hasBindNfcServicePermission = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001553 } else if (permission == "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
1554 hasBindNotificationListenerServicePermission = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001555 } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
1556 hasBindDreamServicePermission = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001557 }
1558 } else {
1559 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1560 " service '%s': %s\n", serviceName.string(), error.string());
1561 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001562 } else if (tag == "provider") {
1563 withinProvider = true;
1564
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001565 bool exported = AaptXml::getResolvedIntegerAttribute(res, tree,
1566 EXPORTED_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001567 if (error != "") {
1568 fprintf(stderr, "ERROR getting 'android:exported' attribute for provider:"
1569 " %s\n", error.string());
1570 goto bail;
1571 }
1572
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001573 bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute(
1574 res, tree, GRANT_URI_PERMISSIONS_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001575 if (error != "") {
1576 fprintf(stderr, "ERROR getting 'android:grantUriPermissions' attribute for provider:"
1577 " %s\n", error.string());
1578 goto bail;
1579 }
1580
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001581 String8 permission = AaptXml::getResolvedAttribute(res, tree,
1582 PERMISSION_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001583 if (error != "") {
1584 fprintf(stderr, "ERROR getting 'android:permission' attribute for provider:"
1585 " %s\n", error.string());
1586 goto bail;
1587 }
1588
1589 hasRequiredSafAttributes |= exported && grantUriPermissions &&
1590 permission == "android.permission.MANAGE_DOCUMENTS";
1591
Michael Wrightec4fdec2013-09-06 16:50:52 -07001592 } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001593 String8 metaDataName = AaptXml::getResolvedAttribute(res, tree,
1594 NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001595 if (error != "") {
1596 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1597 "meta-data:%s\n", error.string());
1598 goto bail;
1599 }
Maurice Chu2675f762013-10-22 17:33:11 -07001600 printf("meta-data: name='%s' ",
1601 ResTable::normalizeForOutput(metaDataName.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001602 printResolvedResourceAttribute(res, tree, VALUE_ATTR, String8("value"),
Maurice Chu76327312013-10-16 18:28:46 -07001603 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001604 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001605 // Try looking for a RESOURCE_ATTR
1606 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001607 printResolvedResourceAttribute(res, tree, RESOURCE_ATTR,
Maurice Chu76327312013-10-16 18:28:46 -07001608 String8("resource"), &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001609 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001610 fprintf(stderr, "ERROR getting 'android:value' or "
1611 "'android:resource' attribute for "
1612 "meta-data:%s\n", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001613 goto bail;
1614 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001615 }
Maurice Chu76327312013-10-16 18:28:46 -07001616 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001617 } else if (withinSupportsInput && tag == "input-type") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001618 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001619 if (name != "" && error == "") {
1620 supportedInput.add(name);
1621 } else {
1622 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1623 error.string());
1624 goto bail;
1625 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001626 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001627 } else if (withinFeatureGroup && tag == "uses-feature") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001628 FeatureGroup& top = featureGroups.editTop();
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001629
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001630 String8 name = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001631 if (name != "" && error == "") {
Adam Lesinskid3edfde2014-08-08 17:32:44 -07001632 top.features.add(name, true);
1633 addParentFeatures(&top, name);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001634 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001635 int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR,
1636 &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001637 if (error == "") {
1638 if (vers > top.openGLESVersion) {
1639 top.openGLESVersion = vers;
1640 }
1641 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001642 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001643 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001644 } else if (depth == 4) {
1645 if (tag == "intent-filter") {
1646 hasIntentFilter = true;
1647 withinIntentFilter = true;
1648 actMainActivity = false;
1649 actWidgetReceivers = false;
1650 actImeService = false;
1651 actWallpaperService = false;
1652 actAccessibilityService = false;
1653 actPrintService = false;
1654 actDeviceAdminEnabled = false;
1655 actHostApduService = false;
1656 actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001657 actDocumentsProvider = false;
1658 actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001659 actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001660 actCamera = false;
1661 actCameraSecure = false;
1662 catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001663 } else if (withinService && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001664 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001665 if (error != "") {
1666 fprintf(stderr, "ERROR getting 'android:name' attribute for"
1667 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1668 goto bail;
1669 }
1670
1671 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1672 name == "android.nfc.cardemulation.off_host_apdu_service") {
1673 bool offHost = true;
1674 if (name == "android.nfc.cardemulation.host_apdu_service") {
1675 offHost = false;
1676 }
1677
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001678 String8 xmlPath = AaptXml::getResolvedAttribute(res, tree,
1679 RESOURCE_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001680 if (error != "") {
1681 fprintf(stderr, "ERROR getting 'android:resource' attribute for"
1682 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1683 goto bail;
1684 }
1685
1686 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1687 offHost, &error);
1688 if (error != "") {
1689 fprintf(stderr, "ERROR getting AID category for service '%s'\n",
1690 serviceName.string());
1691 goto bail;
1692 }
1693
1694 const size_t catLen = categories.size();
1695 for (size_t i = 0; i < catLen; i++) {
1696 bool paymentCategory = (categories[i] == "payment");
1697 if (offHost) {
1698 hasMetaOffHostPaymentCategory |= paymentCategory;
1699 } else {
1700 hasMetaHostPaymentCategory |= paymentCategory;
1701 }
1702 }
1703 }
1704 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001705 } else if ((depth == 5) && withinIntentFilter) {
1706 String8 action;
1707 if (tag == "action") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001708 action = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001709 if (error != "") {
1710 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1711 error.string());
1712 goto bail;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001713 }
1714
Adam Lesinskia5018c92013-09-30 16:23:15 -07001715 if (withinActivity) {
1716 if (action == "android.intent.action.MAIN") {
1717 isMainActivity = true;
1718 actMainActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001719 } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
1720 action == "android.media.action.VIDEO_CAMERA") {
1721 actCamera = true;
1722 } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
1723 actCameraSecure = true;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001724 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001725 } else if (withinReceiver) {
1726 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1727 actWidgetReceivers = true;
1728 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1729 actDeviceAdminEnabled = true;
1730 }
1731 } else if (withinService) {
1732 if (action == "android.view.InputMethod") {
1733 actImeService = true;
1734 } else if (action == "android.service.wallpaper.WallpaperService") {
1735 actWallpaperService = true;
1736 } else if (action == "android.accessibilityservice.AccessibilityService") {
1737 actAccessibilityService = true;
1738 } else if (action == "android.printservice.PrintService") {
1739 actPrintService = true;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001740 } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
1741 actHostApduService = true;
1742 } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
1743 actOffHostApduService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001744 } else if (action == "android.service.notification.NotificationListenerService") {
1745 actNotificationListenerService = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001746 } else if (action == "android.service.dreams.DreamService") {
1747 actDreamService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001748 }
1749 } else if (withinProvider) {
1750 if (action == "android.content.action.DOCUMENTS_PROVIDER") {
1751 actDocumentsProvider = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001752 }
1753 }
1754 if (action == "android.intent.action.SEARCH") {
1755 isSearchable = true;
1756 }
1757 }
1758
1759 if (tag == "category") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001760 String8 category = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001761 if (error != "") {
1762 fprintf(stderr, "ERROR getting 'name' attribute: %s\n",
1763 error.string());
1764 goto bail;
1765 }
1766 if (withinActivity) {
1767 if (category == "android.intent.category.LAUNCHER") {
1768 isLauncherActivity = true;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001769 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
1770 isLeanbackLauncherActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001771 } else if (category == "android.intent.category.HOME") {
1772 catLauncher = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001773 }
1774 }
1775 }
1776 }
1777 }
1778
1779 // Pre-1.6 implicitly granted permission compatibility logic
1780 if (targetSdk < 4) {
1781 if (!hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001782 printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
1783 printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
1784 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001785 hasWriteExternalStoragePermission = true;
1786 }
1787 if (!hasReadPhoneStatePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001788 printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
1789 printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
1790 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001791 }
1792 }
1793
1794 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1795 // force them to always take READ_EXTERNAL_STORAGE as well. We always
1796 // do this (regardless of target API version) because we can't have
1797 // an app with write permission but not read permission.
1798 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001799 printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"));
1800 printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
1801 String8("requested WRITE_EXTERNAL_STORAGE"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001802 }
1803
1804 // Pre-JellyBean call log permission compatibility.
1805 if (targetSdk < 16) {
1806 if (!hasReadCallLogPermission && hasReadContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001807 printUsesPermission(String8("android.permission.READ_CALL_LOG"));
1808 printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
1809 String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001810 }
1811 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001812 printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
1813 printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
1814 String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001815 }
1816 }
1817
Adam Lesinski2c72b682014-06-24 09:56:01 -07001818 addImpliedFeature(&impliedFeatures, "android.hardware.touchscreen",
1819 "default feature for all apps");
1820
1821 const size_t numFeatureGroups = featureGroups.size();
1822 if (numFeatureGroups == 0) {
1823 // If no <feature-group> tags were defined, apply auto-implied features.
1824 printFeatureGroup(commonFeatures, &impliedFeatures);
1825
1826 } else {
1827 // <feature-group> tags are defined, so we ignore implied features and
1828 for (size_t i = 0; i < numFeatureGroups; i++) {
1829 FeatureGroup& grp = featureGroups.editItemAt(i);
1830
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001831 if (commonFeatures.openGLESVersion > grp.openGLESVersion) {
1832 grp.openGLESVersion = commonFeatures.openGLESVersion;
1833 }
1834
Adam Lesinski2c72b682014-06-24 09:56:01 -07001835 // Merge the features defined in the top level (not inside a <feature-group>)
1836 // with this feature group.
1837 const size_t numCommonFeatures = commonFeatures.features.size();
1838 for (size_t j = 0; j < numCommonFeatures; j++) {
1839 if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001840 grp.features.add(commonFeatures.features.keyAt(j),
1841 commonFeatures.features[j]);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001842 }
1843 }
1844
Adam Lesinski73a05112014-12-08 12:53:17 -08001845 if (!grp.features.isEmpty()) {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001846 printFeatureGroup(grp);
Adam Lesinski282e1812014-01-23 18:17:42 -08001847 }
1848 }
1849 }
1850
Adam Lesinski282e1812014-01-23 18:17:42 -08001851
Adam Lesinski282e1812014-01-23 18:17:42 -08001852 if (hasWidgetReceivers) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001853 printComponentPresence("app-widget");
Adam Lesinski282e1812014-01-23 18:17:42 -08001854 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001855 if (hasDeviceAdminReceiver) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001856 printComponentPresence("device-admin");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001857 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001858 if (hasImeService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001859 printComponentPresence("ime");
Adam Lesinski282e1812014-01-23 18:17:42 -08001860 }
1861 if (hasWallpaperService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001862 printComponentPresence("wallpaper");
Adam Lesinski282e1812014-01-23 18:17:42 -08001863 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001864 if (hasAccessibilityService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001865 printComponentPresence("accessibility");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001866 }
1867 if (hasPrintService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001868 printComponentPresence("print-service");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001869 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001870 if (hasPaymentService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001871 printComponentPresence("payment");
1872 }
1873 if (isSearchable) {
1874 printComponentPresence("search");
1875 }
1876 if (hasDocumentsProvider) {
1877 printComponentPresence("document-provider");
1878 }
1879 if (hasLauncher) {
1880 printComponentPresence("launcher");
1881 }
1882 if (hasNotificationListenerService) {
1883 printComponentPresence("notification-listener");
1884 }
John Spurlockeb8d1be2014-06-25 17:46:15 -04001885 if (hasDreamService) {
1886 printComponentPresence("dream");
1887 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001888 if (hasCameraActivity) {
1889 printComponentPresence("camera");
1890 }
1891 if (hasCameraSecureActivity) {
1892 printComponentPresence("camera-secure");
1893 }
1894
1895 if (hasMainActivity) {
1896 printf("main\n");
Adam Lesinski94fc9122013-09-30 17:16:09 -07001897 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001898 if (hasOtherActivities) {
1899 printf("other-activities\n");
1900 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001901 if (hasOtherReceivers) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001902 printf("other-receivers\n");
1903 }
1904 if (hasOtherServices) {
1905 printf("other-services\n");
1906 }
1907
1908 // For modern apps, if screen size buckets haven't been specified
1909 // but the new width ranges have, then infer the buckets from them.
1910 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1911 && requiresSmallestWidthDp > 0) {
1912 int compatWidth = compatibleWidthLimitDp;
1913 if (compatWidth <= 0) {
1914 compatWidth = requiresSmallestWidthDp;
1915 }
1916 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1917 smallScreen = -1;
1918 } else {
1919 smallScreen = 0;
1920 }
1921 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1922 normalScreen = -1;
1923 } else {
1924 normalScreen = 0;
1925 }
1926 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1927 largeScreen = -1;
1928 } else {
1929 largeScreen = 0;
1930 }
1931 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1932 xlargeScreen = -1;
1933 } else {
1934 xlargeScreen = 0;
1935 }
1936 }
1937
1938 // Determine default values for any unspecified screen sizes,
1939 // based on the target SDK of the package. As of 4 (donut)
1940 // the screen size support was introduced, so all default to
1941 // enabled.
1942 if (smallScreen > 0) {
1943 smallScreen = targetSdk >= 4 ? -1 : 0;
1944 }
1945 if (normalScreen > 0) {
1946 normalScreen = -1;
1947 }
1948 if (largeScreen > 0) {
1949 largeScreen = targetSdk >= 4 ? -1 : 0;
1950 }
1951 if (xlargeScreen > 0) {
1952 // Introduced in Gingerbread.
1953 xlargeScreen = targetSdk >= 9 ? -1 : 0;
1954 }
1955 if (anyDensity > 0) {
1956 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1957 || compatibleWidthLimitDp > 0) ? -1 : 0;
1958 }
1959 printf("supports-screens:");
1960 if (smallScreen != 0) {
1961 printf(" 'small'");
1962 }
1963 if (normalScreen != 0) {
1964 printf(" 'normal'");
1965 }
1966 if (largeScreen != 0) {
1967 printf(" 'large'");
1968 }
1969 if (xlargeScreen != 0) {
1970 printf(" 'xlarge'");
1971 }
1972 printf("\n");
1973 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
1974 if (requiresSmallestWidthDp > 0) {
1975 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1976 }
1977 if (compatibleWidthLimitDp > 0) {
1978 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1979 }
1980 if (largestWidthLimitDp > 0) {
1981 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1982 }
1983
1984 printf("locales:");
1985 const size_t NL = locales.size();
1986 for (size_t i=0; i<NL; i++) {
1987 const char* localeStr = locales[i].string();
1988 if (localeStr == NULL || strlen(localeStr) == 0) {
1989 localeStr = "--_--";
1990 }
1991 printf(" '%s'", localeStr);
1992 }
1993 printf("\n");
1994
1995 printf("densities:");
1996 const size_t ND = densities.size();
1997 for (size_t i=0; i<ND; i++) {
1998 printf(" '%d'", densities[i]);
1999 }
2000 printf("\n");
2001
2002 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
2003 if (dir != NULL) {
2004 if (dir->getFileCount() > 0) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002005 SortedVector<String8> architectures;
Adam Lesinski282e1812014-01-23 18:17:42 -08002006 for (size_t i=0; i<dir->getFileCount(); i++) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002007 architectures.add(ResTable::normalizeForOutput(
2008 dir->getFileName(i).string()));
Adam Lesinski282e1812014-01-23 18:17:42 -08002009 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07002010
2011 bool outputAltNativeCode = false;
2012 // A multiArch package is one that contains 64-bit and
2013 // 32-bit versions of native code and expects 3rd-party
2014 // apps to load these native code libraries. Since most
2015 // 64-bit systems also support 32-bit apps, the apps
2016 // loading this multiArch package's code may be either
2017 // 32-bit or 64-bit.
2018 if (hasMultiArch) {
2019 // If this is a multiArch package, report the 64-bit
2020 // version only. Then as a separate entry, report the
2021 // rest.
2022 //
2023 // If we report the 32-bit architecture, this APK will
2024 // be installed on a 32-bit device, causing a large waste
2025 // of bandwidth and disk space. This assumes that
2026 // the developer of the multiArch package has also
2027 // made a version that is 32-bit only.
2028 String8 intel64("x86_64");
2029 String8 arm64("arm64-v8a");
2030 ssize_t index = architectures.indexOf(intel64);
2031 if (index < 0) {
2032 index = architectures.indexOf(arm64);
2033 }
2034
2035 if (index >= 0) {
2036 printf("native-code: '%s'\n", architectures[index].string());
2037 architectures.removeAt(index);
2038 outputAltNativeCode = true;
2039 }
2040 }
2041
2042 const size_t archCount = architectures.size();
2043 if (archCount > 0) {
2044 if (outputAltNativeCode) {
2045 printf("alt-");
2046 }
2047 printf("native-code:");
2048 for (size_t i = 0; i < archCount; i++) {
2049 printf(" '%s'", architectures[i].string());
2050 }
2051 printf("\n");
2052 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002053 }
2054 delete dir;
2055 }
2056 } else if (strcmp("badger", option) == 0) {
2057 printf("%s", CONSOLE_DATA);
2058 } else if (strcmp("configurations", option) == 0) {
2059 Vector<ResTable_config> configs;
2060 res.getConfigurations(&configs);
2061 const size_t N = configs.size();
2062 for (size_t i=0; i<N; i++) {
2063 printf("%s\n", configs[i].toString().string());
2064 }
2065 } else {
2066 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2067 goto bail;
2068 }
2069 }
2070
2071 result = NO_ERROR;
2072
2073bail:
2074 if (asset) {
2075 delete asset;
2076 }
2077 return (result != NO_ERROR);
2078}
2079
2080
2081/*
2082 * Handle the "add" command, which wants to add files to a new or
2083 * pre-existing archive.
2084 */
2085int doAdd(Bundle* bundle)
2086{
2087 ZipFile* zip = NULL;
2088 status_t result = UNKNOWN_ERROR;
2089 const char* zipFileName;
2090
2091 if (bundle->getUpdate()) {
2092 /* avoid confusion */
2093 fprintf(stderr, "ERROR: can't use '-u' with add\n");
2094 goto bail;
2095 }
2096
2097 if (bundle->getFileSpecCount() < 1) {
2098 fprintf(stderr, "ERROR: must specify zip file name\n");
2099 goto bail;
2100 }
2101 zipFileName = bundle->getFileSpecEntry(0);
2102
2103 if (bundle->getFileSpecCount() < 2) {
2104 fprintf(stderr, "NOTE: nothing to do\n");
2105 goto bail;
2106 }
2107
2108 zip = openReadWrite(zipFileName, true);
2109 if (zip == NULL) {
2110 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2111 goto bail;
2112 }
2113
2114 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2115 const char* fileName = bundle->getFileSpecEntry(i);
2116
2117 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
2118 printf(" '%s'... (from gzip)\n", fileName);
2119 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
2120 } else {
2121 if (bundle->getJunkPath()) {
2122 String8 storageName = String8(fileName).getPathLeaf();
Maurice Chu2675f762013-10-22 17:33:11 -07002123 printf(" '%s' as '%s'...\n", fileName,
2124 ResTable::normalizeForOutput(storageName.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08002125 result = zip->add(fileName, storageName.string(),
2126 bundle->getCompressionMethod(), NULL);
2127 } else {
2128 printf(" '%s'...\n", fileName);
2129 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2130 }
2131 }
2132 if (result != NO_ERROR) {
2133 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2134 if (result == NAME_NOT_FOUND) {
2135 fprintf(stderr, ": file not found\n");
2136 } else if (result == ALREADY_EXISTS) {
2137 fprintf(stderr, ": already exists in archive\n");
2138 } else {
2139 fprintf(stderr, "\n");
2140 }
2141 goto bail;
2142 }
2143 }
2144
2145 result = NO_ERROR;
2146
2147bail:
2148 delete zip;
2149 return (result != NO_ERROR);
2150}
2151
2152
2153/*
2154 * Delete files from an existing archive.
2155 */
2156int doRemove(Bundle* bundle)
2157{
2158 ZipFile* zip = NULL;
2159 status_t result = UNKNOWN_ERROR;
2160 const char* zipFileName;
2161
2162 if (bundle->getFileSpecCount() < 1) {
2163 fprintf(stderr, "ERROR: must specify zip file name\n");
2164 goto bail;
2165 }
2166 zipFileName = bundle->getFileSpecEntry(0);
2167
2168 if (bundle->getFileSpecCount() < 2) {
2169 fprintf(stderr, "NOTE: nothing to do\n");
2170 goto bail;
2171 }
2172
2173 zip = openReadWrite(zipFileName, false);
2174 if (zip == NULL) {
2175 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2176 zipFileName);
2177 goto bail;
2178 }
2179
2180 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2181 const char* fileName = bundle->getFileSpecEntry(i);
2182 ZipEntry* entry;
2183
2184 entry = zip->getEntryByName(fileName);
2185 if (entry == NULL) {
2186 printf(" '%s' NOT FOUND\n", fileName);
2187 continue;
2188 }
2189
2190 result = zip->remove(entry);
2191
2192 if (result != NO_ERROR) {
2193 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2194 bundle->getFileSpecEntry(i), zipFileName);
2195 goto bail;
2196 }
2197 }
2198
2199 /* update the archive */
2200 zip->flush();
2201
2202bail:
2203 delete zip;
2204 return (result != NO_ERROR);
2205}
2206
Adam Lesinski3921e872014-05-13 10:56:25 -07002207static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002208 const size_t numDirs = dir->getDirs().size();
2209 for (size_t i = 0; i < numDirs; i++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002210 bool ignore = ignoreConfig;
2211 const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2212 const char* dirStr = subDir->getLeaf().string();
2213 if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2214 ignore = true;
2215 }
2216 status_t err = addResourcesToBuilder(subDir, builder, ignore);
Adam Lesinskifab50872014-04-16 14:40:42 -07002217 if (err != NO_ERROR) {
2218 return err;
2219 }
2220 }
2221
2222 const size_t numFiles = dir->getFiles().size();
2223 for (size_t i = 0; i < numFiles; i++) {
2224 sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2225 const size_t numConfigs = gp->getFiles().size();
2226 for (size_t j = 0; j < numConfigs; j++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002227 status_t err = NO_ERROR;
2228 if (ignoreConfig) {
2229 err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2230 } else {
2231 err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2232 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002233 if (err != NO_ERROR) {
2234 fprintf(stderr, "Failed to add %s (%s) to builder.\n",
2235 gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
2236 return err;
2237 }
2238 }
2239 }
2240 return NO_ERROR;
2241}
2242
2243static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2244 if (split->isBase()) {
2245 return original;
2246 }
2247
2248 String8 ext(original.getPathExtension());
2249 if (ext == String8(".apk")) {
2250 return String8::format("%s_%s%s",
2251 original.getBasePath().string(),
2252 split->getDirectorySafeName().string(),
2253 ext.string());
2254 }
2255
2256 return String8::format("%s_%s", original.string(),
2257 split->getDirectorySafeName().string());
2258}
Adam Lesinski282e1812014-01-23 18:17:42 -08002259
2260/*
2261 * Package up an asset directory and associated application files.
2262 */
2263int doPackage(Bundle* bundle)
2264{
2265 const char* outputAPKFile;
2266 int retVal = 1;
2267 status_t err;
2268 sp<AaptAssets> assets;
2269 int N;
2270 FILE* fp;
2271 String8 dependencyFile;
Adam Lesinskifab50872014-04-16 14:40:42 -07002272 sp<ApkBuilder> builder;
Adam Lesinski282e1812014-01-23 18:17:42 -08002273
Anton Krumina2ef5c02014-03-12 14:46:44 -07002274 // -c en_XA or/and ar_XB means do pseudolocalization
Adam Lesinskifab50872014-04-16 14:40:42 -07002275 sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2276 err = configFilter->parse(bundle->getConfigurations());
Adam Lesinski282e1812014-01-23 18:17:42 -08002277 if (err != NO_ERROR) {
2278 goto bail;
2279 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002280 if (configFilter->containsPseudo()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002281 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2282 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002283 if (configFilter->containsPseudoBidi()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002284 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
Adam Lesinski282e1812014-01-23 18:17:42 -08002285 }
2286
2287 N = bundle->getFileSpecCount();
2288 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
Adam Lesinski09384302014-01-22 16:07:42 -08002289 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002290 fprintf(stderr, "ERROR: no input files\n");
2291 goto bail;
2292 }
2293
2294 outputAPKFile = bundle->getOutputAPKFile();
2295
2296 // Make sure the filenames provided exist and are of the appropriate type.
2297 if (outputAPKFile) {
2298 FileType type;
2299 type = getFileType(outputAPKFile);
2300 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2301 fprintf(stderr,
2302 "ERROR: output file '%s' exists but is not regular file\n",
2303 outputAPKFile);
2304 goto bail;
2305 }
2306 }
2307
2308 // Load the assets.
2309 assets = new AaptAssets();
2310
2311 // Set up the resource gathering in assets if we're going to generate
2312 // dependency files. Every time we encounter a resource while slurping
2313 // the tree, we'll add it to these stores so we have full resource paths
2314 // to write to a dependency file.
2315 if (bundle->getGenDependencies()) {
2316 sp<FilePathStore> resPathStore = new FilePathStore;
2317 assets->setFullResPaths(resPathStore);
2318 sp<FilePathStore> assetPathStore = new FilePathStore;
2319 assets->setFullAssetPaths(assetPathStore);
2320 }
2321
2322 err = assets->slurpFromArgs(bundle);
2323 if (err < 0) {
2324 goto bail;
2325 }
2326
2327 if (bundle->getVerbose()) {
2328 assets->print(String8());
2329 }
2330
Adam Lesinskifab50872014-04-16 14:40:42 -07002331 // Create the ApkBuilder, which will collect the compiled files
2332 // to write to the final APK (or sets of APKs if we are building
2333 // a Split APK.
2334 builder = new ApkBuilder(configFilter);
2335
2336 // If we are generating a Split APK, find out which configurations to split on.
2337 if (bundle->getSplitConfigurations().size() > 0) {
2338 const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2339 const size_t numSplits = splitStrs.size();
2340 for (size_t i = 0; i < numSplits; i++) {
2341 std::set<ConfigDescription> configs;
2342 if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2343 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2344 goto bail;
2345 }
2346
2347 err = builder->createSplitForConfigs(configs);
2348 if (err != NO_ERROR) {
2349 goto bail;
2350 }
2351 }
2352 }
2353
Adam Lesinski282e1812014-01-23 18:17:42 -08002354 // If they asked for any fileAs that need to be compiled, do so.
2355 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002356 err = buildResources(bundle, assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002357 if (err != 0) {
2358 goto bail;
2359 }
2360 }
2361
2362 // At this point we've read everything and processed everything. From here
2363 // on out it's just writing output files.
2364 if (SourcePos::hasErrors()) {
2365 goto bail;
2366 }
2367
2368 // Update symbols with information about which ones are needed as Java symbols.
2369 assets->applyJavaSymbols();
2370 if (SourcePos::hasErrors()) {
2371 goto bail;
2372 }
2373
2374 // If we've been asked to generate a dependency file, do that here
2375 if (bundle->getGenDependencies()) {
2376 // If this is the packaging step, generate the dependency file next to
2377 // the output apk (e.g. bin/resources.ap_.d)
2378 if (outputAPKFile) {
2379 dependencyFile = String8(outputAPKFile);
2380 // Add the .d extension to the dependency file.
2381 dependencyFile.append(".d");
2382 } else {
2383 // Else if this is the R.java dependency generation step,
2384 // generate the dependency file in the R.java package subdirectory
2385 // e.g. gen/com/foo/app/R.java.d
2386 dependencyFile = String8(bundle->getRClassDir());
2387 dependencyFile.appendPath("R.java.d");
2388 }
2389 // Make sure we have a clean dependency file to start with
2390 fp = fopen(dependencyFile, "w");
2391 fclose(fp);
2392 }
2393
2394 // Write out R.java constants
2395 if (!assets->havePrivateSymbols()) {
2396 if (bundle->getCustomPackage() == NULL) {
2397 // Write the R.java file into the appropriate class directory
2398 // e.g. gen/com/foo/app/R.java
Adam Lesinski1e4663852014-08-15 14:47:28 -07002399 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true,
2400 bundle->getBuildSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002401 } else {
2402 const String8 customPkg(bundle->getCustomPackage());
Adam Lesinski1e4663852014-08-15 14:47:28 -07002403 err = writeResourceSymbols(bundle, assets, customPkg, true,
2404 bundle->getBuildSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002405 }
2406 if (err < 0) {
2407 goto bail;
2408 }
2409 // If we have library files, we're going to write our R.java file into
2410 // the appropriate class directory for those libraries as well.
2411 // e.g. gen/com/foo/app/lib/R.java
2412 if (bundle->getExtraPackages() != NULL) {
2413 // Split on colon
2414 String8 libs(bundle->getExtraPackages());
2415 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2416 while (packageString != NULL) {
2417 // Write the R.java file out with the correct package name
Marcin Kosiba0f3a5a62014-09-11 13:48:48 +01002418 err = writeResourceSymbols(bundle, assets, String8(packageString), true,
2419 bundle->getBuildSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002420 if (err < 0) {
2421 goto bail;
2422 }
2423 packageString = strtok(NULL, ":");
2424 }
2425 libs.unlockBuffer();
2426 }
2427 } else {
Adam Lesinski1e4663852014-08-15 14:47:28 -07002428 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002429 if (err < 0) {
2430 goto bail;
2431 }
Adam Lesinski1e4663852014-08-15 14:47:28 -07002432 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002433 if (err < 0) {
2434 goto bail;
2435 }
2436 }
2437
2438 // Write out the ProGuard file
2439 err = writeProguardFile(bundle, assets);
2440 if (err < 0) {
2441 goto bail;
2442 }
2443
2444 // Write the apk
2445 if (outputAPKFile) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002446 // Gather all resources and add them to the APK Builder. The builder will then
2447 // figure out which Split they belong in.
2448 err = addResourcesToBuilder(assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002449 if (err != NO_ERROR) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002450 goto bail;
2451 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002452
2453 const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2454 const size_t numSplits = splits.size();
2455 for (size_t i = 0; i < numSplits; i++) {
2456 const sp<ApkSplit>& split = splits[i];
2457 String8 outputPath = buildApkName(String8(outputAPKFile), split);
2458 err = writeAPK(bundle, outputPath, split);
2459 if (err != NO_ERROR) {
2460 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2461 goto bail;
2462 }
2463 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002464 }
2465
2466 // If we've been asked to generate a dependency file, we need to finish up here.
2467 // the writeResourceSymbols and writeAPK functions have already written the target
2468 // half of the dependency file, now we need to write the prerequisites. (files that
2469 // the R.java file or .ap_ file depend on)
2470 if (bundle->getGenDependencies()) {
2471 // Now that writeResourceSymbols or writeAPK has taken care of writing
2472 // the targets to our dependency file, we'll write the prereqs
2473 fp = fopen(dependencyFile, "a+");
2474 fprintf(fp, " : ");
2475 bool includeRaw = (outputAPKFile != NULL);
2476 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2477 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2478 // and therefore was not added to our pathstores during slurping
2479 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2480 fclose(fp);
2481 }
2482
2483 retVal = 0;
2484bail:
2485 if (SourcePos::hasErrors()) {
2486 SourcePos::printErrors(stderr);
2487 }
2488 return retVal;
2489}
2490
2491/*
2492 * Do PNG Crunching
2493 * PRECONDITIONS
2494 * -S flag points to a source directory containing drawable* folders
2495 * -C flag points to destination directory. The folder structure in the
2496 * source directory will be mirrored to the destination (cache) directory
2497 *
2498 * POSTCONDITIONS
2499 * Destination directory will be updated to match the PNG files in
Maurice Chu2675f762013-10-22 17:33:11 -07002500 * the source directory.
Adam Lesinski282e1812014-01-23 18:17:42 -08002501 */
2502int doCrunch(Bundle* bundle)
2503{
2504 fprintf(stdout, "Crunching PNG Files in ");
2505 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2506 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2507
2508 updatePreProcessedCache(bundle);
2509
2510 return NO_ERROR;
2511}
2512
2513/*
2514 * Do PNG Crunching on a single flag
2515 * -i points to a single png file
2516 * -o points to a single png output file
2517 */
2518int doSingleCrunch(Bundle* bundle)
2519{
2520 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2521 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2522
2523 String8 input(bundle->getSingleCrunchInputFile());
2524 String8 output(bundle->getSingleCrunchOutputFile());
2525
2526 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2527 // we can't return the status_t as it gets truncate to the lower 8 bits.
2528 return 42;
2529 }
2530
2531 return NO_ERROR;
2532}
2533
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002534int runInDaemonMode(Bundle* bundle) {
2535 std::cout << "Ready" << std::endl;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002536 for (std::string cmd; std::getline(std::cin, cmd);) {
2537 if (cmd == "quit") {
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002538 return NO_ERROR;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002539 } else if (cmd == "s") {
2540 // Two argument crunch
2541 std::string inputFile, outputFile;
2542 std::getline(std::cin, inputFile);
2543 std::getline(std::cin, outputFile);
2544 bundle->setSingleCrunchInputFile(inputFile.c_str());
2545 bundle->setSingleCrunchOutputFile(outputFile.c_str());
2546 std::cout << "Crunching " << inputFile << std::endl;
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002547 if (doSingleCrunch(bundle) != NO_ERROR) {
2548 std::cout << "Error" << std::endl;
2549 }
2550 std::cout << "Done" << std::endl;
2551 } else {
2552 // in case of invalid command, just bail out.
2553 std::cerr << "Unknown command" << std::endl;
2554 return -1;
2555 }
2556 }
2557 return -1;
2558}
2559
Adam Lesinski282e1812014-01-23 18:17:42 -08002560char CONSOLE_DATA[2925] = {
2561 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2562 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2563 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2564 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2565 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2566 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2567 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2568 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2569 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2570 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2571 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2572 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2573 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2574 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2575 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2576 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2577 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2578 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2579 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2580 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2581 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2582 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2583 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2584 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2585 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2586 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2587 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2588 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2589 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2590 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2591 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2592 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2593 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2594 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2595 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2596 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2597 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2598 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2599 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2600 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2601 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2602 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2603 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2604 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2605 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2606 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2607 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2608 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2609 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2610 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2611 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2612 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2613 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2614 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2615 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2616 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2617 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2618 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2619 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2620 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2621 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2622 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2623 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2624 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2625 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2626 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2627 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2628 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2629 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2630 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2631 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2632 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2633 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2634 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2635 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2636 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2637 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2638 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2639 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2640 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2641 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2642 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2643 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2644 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2645 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2646 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2647 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2648 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2649 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2650 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2651 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2652 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2653 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2654 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2655 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2656 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2657 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2658 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2659 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2660 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2661 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2662 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2663 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2664 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2665 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2666 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2667 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2668 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2669 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2670 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2671 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2672 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2673 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2674 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2675 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2676 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2677 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2678 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2679 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2680 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2681 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2682 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2683 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2684 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2685 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2686 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2687 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2688 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2689 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2690 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2691 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2692 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2693 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2694 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2695 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2696 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2697 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2698 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2699 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2700 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2701 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2702 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2703 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2704 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2705 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2706 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2707 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2708 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2709 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2710 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2711 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2712 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2713 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2714 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2715 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2716 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2717 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2718 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2719 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2720 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2721 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2722 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2723 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2724 };