blob: f5f70c5cf2ed506b82ff652f32abcff17f47e35e [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++) {
519 if (!grp.features[i]) {
520 continue;
521 }
522
523 const String8& featureName = grp.features.keyAt(i);
524 printf(" uses-feature: name='%s'\n",
525 ResTable::normalizeForOutput(featureName.string()).string());
526 }
527
528 const size_t numImpliedFeatures =
529 (impliedFeatures != NULL) ? impliedFeatures->size() : 0;
530 for (size_t i = 0; i < numImpliedFeatures; i++) {
531 const ImpliedFeature& impliedFeature = impliedFeatures->valueAt(i);
532 if (grp.features.indexOfKey(impliedFeature.name) >= 0) {
533 // The feature is explicitly set, no need to use implied
534 // definition.
535 continue;
536 }
537
538 String8 printableFeatureName(ResTable::normalizeForOutput(
539 impliedFeature.name.string()));
540 printf(" uses-feature: name='%s'\n", printableFeatureName.string());
541 printf(" uses-implied-feature: name='%s' reason='",
542 printableFeatureName.string());
543 const size_t numReasons = impliedFeature.reasons.size();
544 for (size_t j = 0; j < numReasons; j++) {
545 printf("%s", impliedFeature.reasons[j].string());
546 if (j + 2 < numReasons) {
547 printf(", ");
548 } else if (j + 1 < numReasons) {
549 printf(", and ");
550 }
551 }
552 printf("'\n");
553 }
554}
555
556static void addParentFeatures(FeatureGroup* grp, const String8& name) {
557 if (name == "android.hardware.camera.autofocus" ||
558 name == "android.hardware.camera.flash") {
559 grp->features.add(String8("android.hardware.camera"), true);
560 } else if (name == "android.hardware.location.gps" ||
561 name == "android.hardware.location.network") {
562 grp->features.add(String8("android.hardware.location"), true);
563 } else if (name == "android.hardware.touchscreen.multitouch") {
564 grp->features.add(String8("android.hardware.touchscreen"), true);
565 } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
566 grp->features.add(String8("android.hardware.touchscreen.multitouch"), true);
567 grp->features.add(String8("android.hardware.touchscreen"), true);
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700568 } else if (name == "android.hardware.opengles.aep") {
569 const int openGLESVersion31 = 0x00030001;
570 if (openGLESVersion31 > grp->openGLESVersion) {
571 grp->openGLESVersion = openGLESVersion31;
572 }
Adam Lesinski2c72b682014-06-24 09:56:01 -0700573 }
574}
575
Adam Lesinski282e1812014-01-23 18:17:42 -0800576/*
577 * Handle the "dump" command, to extract select data from an archive.
578 */
579extern char CONSOLE_DATA[2925]; // see EOF
580int doDump(Bundle* bundle)
581{
582 status_t result = UNKNOWN_ERROR;
Adam Lesinski282e1812014-01-23 18:17:42 -0800583
584 if (bundle->getFileSpecCount() < 1) {
585 fprintf(stderr, "ERROR: no dump option specified\n");
586 return 1;
587 }
588
589 if (bundle->getFileSpecCount() < 2) {
590 fprintf(stderr, "ERROR: no dump file specified\n");
591 return 1;
592 }
593
594 const char* option = bundle->getFileSpecEntry(0);
595 const char* filename = bundle->getFileSpecEntry(1);
596
597 AssetManager assets;
Narayan Kamathf85e41f2014-01-24 14:14:30 +0000598 int32_t assetsCookie;
Adam Lesinski282e1812014-01-23 18:17:42 -0800599 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
600 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
601 return 1;
602 }
603
604 // Make a dummy config for retrieving resources... we need to supply
605 // non-default values for some configs so that we can retrieve resources
606 // in the app that don't have a default. The most important of these is
607 // the API version because key resources like icons will have an implicit
608 // version if they are using newer config types like density.
609 ResTable_config config;
Narayan Kamath91447d82014-01-21 15:32:36 +0000610 memset(&config, 0, sizeof(ResTable_config));
Adam Lesinski282e1812014-01-23 18:17:42 -0800611 config.language[0] = 'e';
612 config.language[1] = 'n';
613 config.country[0] = 'U';
614 config.country[1] = 'S';
615 config.orientation = ResTable_config::ORIENTATION_PORT;
616 config.density = ResTable_config::DENSITY_MEDIUM;
617 config.sdkVersion = 10000; // Very high.
618 config.screenWidthDp = 320;
619 config.screenHeightDp = 480;
620 config.smallestScreenWidthDp = 320;
Adam Lesinskic2dea8d2014-08-04 16:40:41 -0700621 config.screenLayout |= ResTable_config::SCREENSIZE_NORMAL;
Adam Lesinski282e1812014-01-23 18:17:42 -0800622 assets.setConfiguration(config);
623
624 const ResTable& res = assets.getResources(false);
625 if (&res == NULL) {
626 fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
Adam Lesinski63e646e2014-07-30 11:40:39 -0700627 return 1;
Adam Lesinski25e9d552014-05-19 15:01:43 -0700628 } else if (res.getError() != NO_ERROR) {
629 fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
Adam Lesinski63e646e2014-07-30 11:40:39 -0700630 return 1;
Adam Lesinski282e1812014-01-23 18:17:42 -0800631 }
632
Adam Lesinski2cb761e2014-08-15 13:59:02 -0700633 // The dynamicRefTable can be null if there are no resources for this asset cookie.
634 // This fine.
Adam Lesinski63e646e2014-07-30 11:40:39 -0700635 const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700636
637 Asset* asset = NULL;
638
Adam Lesinski282e1812014-01-23 18:17:42 -0800639 if (strcmp("resources", option) == 0) {
640#ifndef HAVE_ANDROID_OS
641 res.print(bundle->getValues());
642#endif
643
644 } else if (strcmp("strings", option) == 0) {
645 const ResStringPool* pool = res.getTableStringBlock(0);
646 printStringPool(pool);
647
648 } else if (strcmp("xmltree", option) == 0) {
649 if (bundle->getFileSpecCount() < 3) {
650 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
651 goto bail;
652 }
653
654 for (int i=2; i<bundle->getFileSpecCount(); i++) {
655 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700656 ResXMLTree tree(dynamicRefTable);
657 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800658 if (asset == NULL) {
659 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
660 goto bail;
661 }
662
663 if (tree.setTo(asset->getBuffer(true),
664 asset->getLength()) != NO_ERROR) {
665 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
666 goto bail;
667 }
668 tree.restart();
669 printXMLBlock(&tree);
670 tree.uninit();
671 delete asset;
672 asset = NULL;
673 }
674
675 } else if (strcmp("xmlstrings", option) == 0) {
676 if (bundle->getFileSpecCount() < 3) {
677 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
678 goto bail;
679 }
680
681 for (int i=2; i<bundle->getFileSpecCount(); i++) {
682 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700683 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800684 if (asset == NULL) {
685 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
686 goto bail;
687 }
688
Adam Lesinski63e646e2014-07-30 11:40:39 -0700689 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800690 if (tree.setTo(asset->getBuffer(true),
691 asset->getLength()) != NO_ERROR) {
692 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
693 goto bail;
694 }
695 printStringPool(&tree.getStrings());
696 delete asset;
697 asset = NULL;
698 }
699
700 } else {
Adam Lesinski63e646e2014-07-30 11:40:39 -0700701 asset = assets.openNonAsset(assetsCookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800702 if (asset == NULL) {
703 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
704 goto bail;
705 }
706
Adam Lesinski63e646e2014-07-30 11:40:39 -0700707 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800708 if (tree.setTo(asset->getBuffer(true),
709 asset->getLength()) != NO_ERROR) {
710 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
711 goto bail;
712 }
713 tree.restart();
714
715 if (strcmp("permissions", option) == 0) {
716 size_t len;
717 ResXMLTree::event_code_t code;
718 int depth = 0;
719 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
720 if (code == ResXMLTree::END_TAG) {
721 depth--;
722 continue;
723 }
724 if (code != ResXMLTree::START_TAG) {
725 continue;
726 }
727 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700728 const char16_t* ctag16 = tree.getElementName(&len);
729 if (ctag16 == NULL) {
730 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
731 goto bail;
732 }
733 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800734 //printf("Depth %d tag %s\n", depth, tag.string());
735 if (depth == 1) {
736 if (tag != "manifest") {
737 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
738 goto bail;
739 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700740 String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -0700741 printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800742 } else if (depth == 2 && tag == "permission") {
743 String8 error;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700744 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -0800745 if (error != "") {
746 fprintf(stderr, "ERROR: %s\n", error.string());
747 goto bail;
748 }
Maurice Chu2675f762013-10-22 17:33:11 -0700749 printf("permission: %s\n",
750 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800751 } else if (depth == 2 && tag == "uses-permission") {
752 String8 error;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700753 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -0800754 if (error != "") {
755 fprintf(stderr, "ERROR: %s\n", error.string());
756 goto bail;
757 }
Adam Lesinski58f1f362013-11-12 12:59:08 -0800758 printUsesPermission(name,
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700759 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
760 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
Adam Lesinski282e1812014-01-23 18:17:42 -0800761 }
762 }
763 } else if (strcmp("badging", option) == 0) {
764 Vector<String8> locales;
765 res.getLocales(&locales);
766
767 Vector<ResTable_config> configs;
768 res.getConfigurations(&configs);
769 SortedVector<int> densities;
770 const size_t NC = configs.size();
771 for (size_t i=0; i<NC; i++) {
772 int dens = configs[i].density;
773 if (dens == 0) {
774 dens = 160;
775 }
776 densities.add(dens);
777 }
778
779 size_t len;
780 ResXMLTree::event_code_t code;
781 int depth = 0;
782 String8 error;
783 bool withinActivity = false;
784 bool isMainActivity = false;
785 bool isLauncherActivity = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800786 bool isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800787 bool isSearchable = false;
788 bool withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700789 bool withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700790 bool withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800791 bool withinReceiver = false;
792 bool withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700793 bool withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800794 bool withinIntentFilter = false;
795 bool hasMainActivity = false;
796 bool hasOtherActivities = false;
797 bool hasOtherReceivers = false;
798 bool hasOtherServices = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700799 bool hasIntentFilter = false;
800
Adam Lesinski282e1812014-01-23 18:17:42 -0800801 bool hasWallpaperService = false;
802 bool hasImeService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700803 bool hasAccessibilityService = false;
804 bool hasPrintService = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800805 bool hasWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700806 bool hasDeviceAdminReceiver = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700807 bool hasPaymentService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700808 bool hasDocumentsProvider = false;
809 bool hasCameraActivity = false;
810 bool hasCameraSecureActivity = false;
811 bool hasLauncher = false;
812 bool hasNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400813 bool hasDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700814
Adam Lesinski282e1812014-01-23 18:17:42 -0800815 bool actMainActivity = false;
816 bool actWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700817 bool actDeviceAdminEnabled = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800818 bool actImeService = false;
819 bool actWallpaperService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700820 bool actAccessibilityService = false;
821 bool actPrintService = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700822 bool actHostApduService = false;
823 bool actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700824 bool actDocumentsProvider = false;
825 bool actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400826 bool actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700827 bool actCamera = false;
828 bool actCameraSecure = false;
829 bool catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700830 bool hasMetaHostPaymentCategory = false;
831 bool hasMetaOffHostPaymentCategory = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700832
833 // These permissions are required by services implementing services
834 // the system binds to (IME, Accessibility, PrintServices, etc.)
835 bool hasBindDeviceAdminPermission = false;
836 bool hasBindInputMethodPermission = false;
837 bool hasBindAccessibilityServicePermission = false;
838 bool hasBindPrintServicePermission = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700839 bool hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700840 bool hasRequiredSafAttributes = false;
841 bool hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400842 bool hasBindDreamServicePermission = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800843
844 // These two implement the implicit permissions that are granted
845 // to pre-1.6 applications.
846 bool hasWriteExternalStoragePermission = false;
847 bool hasReadPhoneStatePermission = false;
848
849 // If an app requests write storage, they will also get read storage.
850 bool hasReadExternalStoragePermission = false;
851
852 // Implement transition to read and write call log.
853 bool hasReadContactsPermission = false;
854 bool hasWriteContactsPermission = false;
855 bool hasReadCallLogPermission = false;
856 bool hasWriteCallLogPermission = false;
857
Adam Lesinskie47fd122014-08-15 22:25:36 -0700858 // If an app declares itself as multiArch, we report the
859 // native libraries differently.
860 bool hasMultiArch = false;
861
Adam Lesinski282e1812014-01-23 18:17:42 -0800862 // This next group of variables is used to implement a group of
863 // backward-compatibility heuristics necessitated by the addition of
864 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
865 // heuristic is "if an app requests a permission but doesn't explicitly
866 // request the corresponding <uses-feature>, presume it's there anyway".
Adam Lesinski2c72b682014-06-24 09:56:01 -0700867
Adam Lesinski282e1812014-01-23 18:17:42 -0800868 // 2.2 also added some other features that apps can request, but that
869 // have no corresponding permission, so we cannot implement any
870 // back-compatibility heuristic for them. The below are thus unnecessary
871 // (but are retained here for documentary purposes.)
872 //bool specCompassFeature = false;
873 //bool specAccelerometerFeature = false;
874 //bool specProximityFeature = false;
875 //bool specAmbientLightFeature = false;
876 //bool specLiveWallpaperFeature = false;
877
878 int targetSdk = 0;
879 int smallScreen = 1;
880 int normalScreen = 1;
881 int largeScreen = 1;
882 int xlargeScreen = 1;
883 int anyDensity = 1;
884 int requiresSmallestWidthDp = 0;
885 int compatibleWidthLimitDp = 0;
886 int largestWidthLimitDp = 0;
887 String8 pkg;
888 String8 activityName;
889 String8 activityLabel;
890 String8 activityIcon;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800891 String8 activityBanner;
Adam Lesinski282e1812014-01-23 18:17:42 -0800892 String8 receiverName;
893 String8 serviceName;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700894 Vector<String8> supportedInput;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700895
896 FeatureGroup commonFeatures;
897 Vector<FeatureGroup> featureGroups;
898 KeyedVector<String8, ImpliedFeature> impliedFeatures;
899
Adam Lesinski282e1812014-01-23 18:17:42 -0800900 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
901 if (code == ResXMLTree::END_TAG) {
902 depth--;
903 if (depth < 2) {
Michael Wrightec4fdec2013-09-06 16:50:52 -0700904 if (withinSupportsInput && !supportedInput.isEmpty()) {
905 printf("supports-input: '");
906 const size_t N = supportedInput.size();
907 for (size_t i=0; i<N; i++) {
Maurice Chu2675f762013-10-22 17:33:11 -0700908 printf("%s", ResTable::normalizeForOutput(
909 supportedInput[i].string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -0700910 if (i != N - 1) {
911 printf("' '");
912 } else {
913 printf("'\n");
914 }
915 }
916 supportedInput.clear();
917 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800918 withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700919 withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700920 withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800921 } else if (depth < 3) {
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800922 if (withinActivity && isMainActivity) {
Maurice Chu2675f762013-10-22 17:33:11 -0700923 String8 aName(getComponentName(pkg, activityName));
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800924 if (isLauncherActivity) {
925 printf("launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800926 if (aName.length() > 0) {
927 printf(" name='%s' ",
928 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800929 }
930 printf(" label='%s' icon='%s'\n",
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800931 ResTable::normalizeForOutput(activityLabel.string()).string(),
932 ResTable::normalizeForOutput(activityIcon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800933 }
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800934 if (isLeanbackLauncherActivity) {
935 printf("leanback-launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800936 if (aName.length() > 0) {
937 printf(" name='%s' ",
938 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800939 }
940 printf(" label='%s' icon='%s' banner='%s'\n",
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800941 ResTable::normalizeForOutput(activityLabel.string()).string(),
942 ResTable::normalizeForOutput(activityIcon.string()).string(),
943 ResTable::normalizeForOutput(activityBanner.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800944 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800945 }
946 if (!hasIntentFilter) {
947 hasOtherActivities |= withinActivity;
948 hasOtherReceivers |= withinReceiver;
949 hasOtherServices |= withinService;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700950 } else {
951 if (withinService) {
952 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
953 hasBindNfcServicePermission);
954 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
955 hasBindNfcServicePermission);
956 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800957 }
958 withinActivity = false;
959 withinService = false;
960 withinReceiver = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700961 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800962 hasIntentFilter = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800963 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800964 } else if (depth < 4) {
965 if (withinIntentFilter) {
966 if (withinActivity) {
967 hasMainActivity |= actMainActivity;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700968 hasLauncher |= catLauncher;
969 hasCameraActivity |= actCamera;
970 hasCameraSecureActivity |= actCameraSecure;
971 hasOtherActivities |= !actMainActivity && !actCamera && !actCameraSecure;
Adam Lesinski282e1812014-01-23 18:17:42 -0800972 } else if (withinReceiver) {
973 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700974 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
975 hasBindDeviceAdminPermission);
976 hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled);
Adam Lesinski282e1812014-01-23 18:17:42 -0800977 } else if (withinService) {
978 hasImeService |= actImeService;
979 hasWallpaperService |= actWallpaperService;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700980 hasAccessibilityService |= (actAccessibilityService &&
981 hasBindAccessibilityServicePermission);
982 hasPrintService |= (actPrintService && hasBindPrintServicePermission);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700983 hasNotificationListenerService |= actNotificationListenerService &&
984 hasBindNotificationListenerServicePermission;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400985 hasDreamService |= actDreamService && hasBindDreamServicePermission;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700986 hasOtherServices |= (!actImeService && !actWallpaperService &&
Adam Lesinski94fc9122013-09-30 17:16:09 -0700987 !actAccessibilityService && !actPrintService &&
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700988 !actHostApduService && !actOffHostApduService &&
989 !actNotificationListenerService);
990 } else if (withinProvider) {
991 hasDocumentsProvider |= actDocumentsProvider && hasRequiredSafAttributes;
Adam Lesinski282e1812014-01-23 18:17:42 -0800992 }
993 }
994 withinIntentFilter = false;
995 }
996 continue;
997 }
998 if (code != ResXMLTree::START_TAG) {
999 continue;
1000 }
1001 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001002
1003 const char16_t* ctag16 = tree.getElementName(&len);
1004 if (ctag16 == NULL) {
1005 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
1006 goto bail;
1007 }
1008 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -08001009 //printf("Depth %d, %s\n", depth, tag.string());
1010 if (depth == 1) {
1011 if (tag != "manifest") {
1012 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
1013 goto bail;
1014 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001015 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -07001016 printf("package: name='%s' ",
1017 ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001018 int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR,
1019 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001020 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001021 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n",
1022 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001023 goto bail;
1024 }
1025 if (versionCode > 0) {
1026 printf("versionCode='%d' ", versionCode);
1027 } else {
1028 printf("versionCode='' ");
1029 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001030 String8 versionName = AaptXml::getResolvedAttribute(res, tree,
1031 VERSION_NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001032 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001033 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n",
1034 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001035 goto bail;
1036 }
Adam Lesinski25d35a92014-08-11 09:41:56 -07001037 printf("versionName='%s'",
Maurice Chu2675f762013-10-22 17:33:11 -07001038 ResTable::normalizeForOutput(versionName.string()).string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001039
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001040 String8 splitName = AaptXml::getAttribute(tree, NULL, "split");
Adam Lesinski25d35a92014-08-11 09:41:56 -07001041 if (!splitName.isEmpty()) {
1042 printf(" split='%s'", ResTable::normalizeForOutput(
1043 splitName.string()).string());
1044 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001045
Adam Lesinski5283fab2014-08-29 11:23:55 -07001046 String8 platformVersionName = AaptXml::getAttribute(tree, NULL,
1047 "platformBuildVersionName");
1048 printf(" platformBuildVersionName='%s'", platformVersionName.string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001049 printf("\n");
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001050
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001051 int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
1052 INSTALL_LOCATION_ATTR, &error);
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001053 if (error != "") {
1054 fprintf(stderr, "ERROR getting 'android:installLocation' attribute: %s\n",
1055 error.string());
1056 goto bail;
1057 }
1058
1059 if (installLocation >= 0) {
1060 printf("install-location:'");
1061 switch (installLocation) {
1062 case 0:
1063 printf("auto");
1064 break;
1065 case 1:
1066 printf("internalOnly");
1067 break;
1068 case 2:
1069 printf("preferExternal");
1070 break;
1071 default:
1072 fprintf(stderr, "Invalid installLocation %d\n", installLocation);
1073 goto bail;
1074 }
1075 printf("'\n");
1076 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001077 } else if (depth == 2) {
1078 withinApplication = false;
1079 if (tag == "application") {
1080 withinApplication = true;
1081
1082 String8 label;
1083 const size_t NL = locales.size();
1084 for (size_t i=0; i<NL; i++) {
1085 const char* localeStr = locales[i].string();
1086 assets.setLocale(localeStr != NULL ? localeStr : "");
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001087 String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1088 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001089 if (llabel != "") {
1090 if (localeStr == NULL || strlen(localeStr) == 0) {
1091 label = llabel;
Maurice Chu2675f762013-10-22 17:33:11 -07001092 printf("application-label:'%s'\n",
1093 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001094 } else {
1095 if (label == "") {
1096 label = llabel;
1097 }
1098 printf("application-label-%s:'%s'\n", localeStr,
Maurice Chu2675f762013-10-22 17:33:11 -07001099 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001100 }
1101 }
1102 }
1103
1104 ResTable_config tmpConfig = config;
1105 const size_t ND = densities.size();
1106 for (size_t i=0; i<ND; i++) {
1107 tmpConfig.density = densities[i];
1108 assets.setConfiguration(tmpConfig);
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001109 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1110 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001111 if (icon != "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001112 printf("application-icon-%d:'%s'\n", densities[i],
1113 ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001114 }
1115 }
1116 assets.setConfiguration(config);
1117
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001118 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001119 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001120 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1121 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001122 goto bail;
1123 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001124 int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0,
1125 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001126 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001127 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n",
1128 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001129 goto bail;
1130 }
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001131
1132 String8 banner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR, &error);
1133 if (error != "") {
1134 fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
1135 error.string());
1136 goto bail;
1137 }
Maurice Chu2675f762013-10-22 17:33:11 -07001138 printf("application: label='%s' ",
1139 ResTable::normalizeForOutput(label.string()).string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001140 printf("icon='%s'", ResTable::normalizeForOutput(icon.string()).string());
1141 if (banner != "") {
1142 printf(" banner='%s'", ResTable::normalizeForOutput(banner.string()).string());
1143 }
1144 printf("\n");
Adam Lesinski282e1812014-01-23 18:17:42 -08001145 if (testOnly != 0) {
1146 printf("testOnly='%d'\n", testOnly);
1147 }
1148
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001149 int32_t isGame = AaptXml::getResolvedIntegerAttribute(res, tree,
1150 ISGAME_ATTR, 0, &error);
1151 if (error != "") {
1152 fprintf(stderr, "ERROR getting 'android:isGame' attribute: %s\n",
1153 error.string());
1154 goto bail;
1155 }
1156 if (isGame != 0) {
1157 printf("application-isGame\n");
1158 }
1159
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001160 int32_t debuggable = AaptXml::getResolvedIntegerAttribute(res, tree,
1161 DEBUGGABLE_ATTR, 0, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001162 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001163 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n",
1164 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001165 goto bail;
1166 }
1167 if (debuggable != 0) {
1168 printf("application-debuggable\n");
1169 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07001170
1171 // We must search by name because the multiArch flag hasn't been API
1172 // frozen yet.
1173 int32_t multiArchIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
1174 "multiArch");
1175 if (multiArchIndex >= 0) {
1176 Res_value value;
1177 if (tree.getAttributeValue(multiArchIndex, &value) != NO_ERROR) {
1178 if (value.dataType >= Res_value::TYPE_FIRST_INT &&
1179 value.dataType <= Res_value::TYPE_LAST_INT) {
1180 hasMultiArch = value.data;
1181 }
1182 }
1183 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001184 } else if (tag == "uses-sdk") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001185 int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001186 if (error != "") {
1187 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001188 String8 name = AaptXml::getResolvedAttribute(res, tree,
1189 MIN_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001190 if (error != "") {
1191 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
1192 error.string());
1193 goto bail;
1194 }
1195 if (name == "Donut") targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001196 printf("sdkVersion:'%s'\n",
1197 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001198 } else if (code != -1) {
1199 targetSdk = code;
1200 printf("sdkVersion:'%d'\n", code);
1201 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001202 code = AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -08001203 if (code != -1) {
1204 printf("maxSdkVersion:'%d'\n", code);
1205 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001206 code = AaptXml::getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001207 if (error != "") {
1208 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001209 String8 name = AaptXml::getResolvedAttribute(res, tree,
1210 TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001211 if (error != "") {
1212 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
1213 error.string());
1214 goto bail;
1215 }
1216 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001217 printf("targetSdkVersion:'%s'\n",
1218 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001219 } else if (code != -1) {
1220 if (targetSdk < code) {
1221 targetSdk = code;
1222 }
1223 printf("targetSdkVersion:'%d'\n", code);
1224 }
1225 } else if (tag == "uses-configuration") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001226 int32_t reqTouchScreen = AaptXml::getIntegerAttribute(tree,
1227 REQ_TOUCH_SCREEN_ATTR, 0);
1228 int32_t reqKeyboardType = AaptXml::getIntegerAttribute(tree,
1229 REQ_KEYBOARD_TYPE_ATTR, 0);
1230 int32_t reqHardKeyboard = AaptXml::getIntegerAttribute(tree,
1231 REQ_HARD_KEYBOARD_ATTR, 0);
1232 int32_t reqNavigation = AaptXml::getIntegerAttribute(tree,
1233 REQ_NAVIGATION_ATTR, 0);
1234 int32_t reqFiveWayNav = AaptXml::getIntegerAttribute(tree,
1235 REQ_FIVE_WAY_NAV_ATTR, 0);
Adam Lesinski282e1812014-01-23 18:17:42 -08001236 printf("uses-configuration:");
1237 if (reqTouchScreen != 0) {
1238 printf(" reqTouchScreen='%d'", reqTouchScreen);
1239 }
1240 if (reqKeyboardType != 0) {
1241 printf(" reqKeyboardType='%d'", reqKeyboardType);
1242 }
1243 if (reqHardKeyboard != 0) {
1244 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1245 }
1246 if (reqNavigation != 0) {
1247 printf(" reqNavigation='%d'", reqNavigation);
1248 }
1249 if (reqFiveWayNav != 0) {
1250 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1251 }
1252 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001253 } else if (tag == "supports-input") {
1254 withinSupportsInput = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001255 } else if (tag == "supports-screens") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001256 smallScreen = AaptXml::getIntegerAttribute(tree,
1257 SMALL_SCREEN_ATTR, 1);
1258 normalScreen = AaptXml::getIntegerAttribute(tree,
1259 NORMAL_SCREEN_ATTR, 1);
1260 largeScreen = AaptXml::getIntegerAttribute(tree,
1261 LARGE_SCREEN_ATTR, 1);
1262 xlargeScreen = AaptXml::getIntegerAttribute(tree,
1263 XLARGE_SCREEN_ATTR, 1);
1264 anyDensity = AaptXml::getIntegerAttribute(tree,
1265 ANY_DENSITY_ATTR, 1);
1266 requiresSmallestWidthDp = AaptXml::getIntegerAttribute(tree,
1267 REQUIRES_SMALLEST_WIDTH_DP_ATTR, 0);
1268 compatibleWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1269 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, 0);
1270 largestWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1271 LARGEST_WIDTH_LIMIT_DP_ATTR, 0);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001272 } else if (tag == "feature-group") {
1273 withinFeatureGroup = true;
1274 FeatureGroup group;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001275 group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001276 if (error != "") {
1277 fprintf(stderr, "ERROR getting 'android:label' attribute:"
1278 " %s\n", error.string());
1279 goto bail;
1280 }
1281 featureGroups.add(group);
1282
Adam Lesinski282e1812014-01-23 18:17:42 -08001283 } else if (tag == "uses-feature") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001284 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001285 if (name != "" && error == "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001286 int req = AaptXml::getIntegerAttribute(tree,
1287 REQUIRED_ATTR, 1);
Adam Lesinski282e1812014-01-23 18:17:42 -08001288
Adam Lesinski2c72b682014-06-24 09:56:01 -07001289 commonFeatures.features.add(name, req);
1290 if (req) {
1291 addParentFeatures(&commonFeatures, name);
Adam Lesinski282e1812014-01-23 18:17:42 -08001292 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001293 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001294 int vers = AaptXml::getIntegerAttribute(tree,
Adam Lesinski282e1812014-01-23 18:17:42 -08001295 GL_ES_VERSION_ATTR, &error);
1296 if (error == "") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001297 if (vers > commonFeatures.openGLESVersion) {
1298 commonFeatures.openGLESVersion = vers;
1299 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001300 }
1301 }
1302 } else if (tag == "uses-permission") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001303 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001304 if (name != "" && error == "") {
1305 if (name == "android.permission.CAMERA") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001306 addImpliedFeature(&impliedFeatures, "android.hardware.camera",
Adam Lesinski2c72b682014-06-24 09:56:01 -07001307 String8::format("requested %s permission", name.string())
1308 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001309 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001310 addImpliedFeature(&impliedFeatures, "android.hardware.location.gps",
1311 String8::format("requested %s permission", name.string())
1312 .string());
1313 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1314 String8::format("requested %s permission", name.string())
1315 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001316 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001317 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1318 String8::format("requested %s permission", name.string())
1319 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001320 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001321 addImpliedFeature(&impliedFeatures, "android.hardware.location.network",
1322 String8::format("requested %s permission", name.string())
1323 .string());
1324 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1325 String8::format("requested %s permission", name.string())
1326 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001327 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
1328 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001329 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1330 String8::format("requested %s permission", name.string())
1331 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001332 } else if (name == "android.permission.BLUETOOTH" ||
1333 name == "android.permission.BLUETOOTH_ADMIN") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001334 if (targetSdk > 4) {
1335 addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
1336 String8::format("requested %s permission", name.string())
1337 .string());
1338 addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
1339 "targetSdkVersion > 4");
1340 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001341 } else if (name == "android.permission.RECORD_AUDIO") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001342 addImpliedFeature(&impliedFeatures, "android.hardware.microphone",
1343 String8::format("requested %s permission", name.string())
1344 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001345 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1346 name == "android.permission.CHANGE_WIFI_STATE" ||
1347 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001348 addImpliedFeature(&impliedFeatures, "android.hardware.wifi",
1349 String8::format("requested %s permission", name.string())
1350 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001351 } else if (name == "android.permission.CALL_PHONE" ||
1352 name == "android.permission.CALL_PRIVILEGED" ||
1353 name == "android.permission.MODIFY_PHONE_STATE" ||
1354 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1355 name == "android.permission.READ_SMS" ||
1356 name == "android.permission.RECEIVE_SMS" ||
1357 name == "android.permission.RECEIVE_MMS" ||
1358 name == "android.permission.RECEIVE_WAP_PUSH" ||
1359 name == "android.permission.SEND_SMS" ||
1360 name == "android.permission.WRITE_APN_SETTINGS" ||
1361 name == "android.permission.WRITE_SMS") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001362 addImpliedFeature(&impliedFeatures, "android.hardware.telephony",
1363 String8("requested a telephony permission").string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001364 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1365 hasWriteExternalStoragePermission = true;
1366 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1367 hasReadExternalStoragePermission = true;
1368 } else if (name == "android.permission.READ_PHONE_STATE") {
1369 hasReadPhoneStatePermission = true;
1370 } else if (name == "android.permission.READ_CONTACTS") {
1371 hasReadContactsPermission = true;
1372 } else if (name == "android.permission.WRITE_CONTACTS") {
1373 hasWriteContactsPermission = true;
1374 } else if (name == "android.permission.READ_CALL_LOG") {
1375 hasReadCallLogPermission = true;
1376 } else if (name == "android.permission.WRITE_CALL_LOG") {
1377 hasWriteCallLogPermission = true;
1378 }
Adam Lesinski58f1f362013-11-12 12:59:08 -08001379
1380 printUsesPermission(name,
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001381 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
1382 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
Adam Lesinski58f1f362013-11-12 12:59:08 -08001383 } else {
Adam Lesinski282e1812014-01-23 18:17:42 -08001384 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1385 error.string());
1386 goto bail;
1387 }
1388 } else if (tag == "uses-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001389 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001390 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001391 printf("uses-package:'%s'\n",
1392 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001393 } else {
1394 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1395 error.string());
1396 goto bail;
1397 }
1398 } else if (tag == "original-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001399 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001400 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001401 printf("original-package:'%s'\n",
1402 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001403 } else {
1404 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1405 error.string());
1406 goto bail;
1407 }
1408 } else if (tag == "supports-gl-texture") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001409 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001410 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001411 printf("supports-gl-texture:'%s'\n",
1412 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001413 } else {
1414 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1415 error.string());
1416 goto bail;
1417 }
1418 } else if (tag == "compatible-screens") {
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001419 printCompatibleScreens(tree, &error);
1420 if (error != "") {
1421 fprintf(stderr, "ERROR getting compatible screens: %s\n",
1422 error.string());
1423 goto bail;
1424 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001425 depth--;
1426 } else if (tag == "package-verifier") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001427 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001428 if (name != "" && error == "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001429 String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001430 if (publicKey != "" && error == "") {
1431 printf("package-verifier: name='%s' publicKey='%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001432 ResTable::normalizeForOutput(name.string()).string(),
1433 ResTable::normalizeForOutput(publicKey.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001434 }
1435 }
1436 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001437 } else if (depth == 3) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001438 withinActivity = false;
1439 withinReceiver = false;
1440 withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001441 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001442 hasIntentFilter = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001443 hasMetaHostPaymentCategory = false;
1444 hasMetaOffHostPaymentCategory = false;
1445 hasBindDeviceAdminPermission = false;
1446 hasBindInputMethodPermission = false;
1447 hasBindAccessibilityServicePermission = false;
1448 hasBindPrintServicePermission = false;
1449 hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001450 hasRequiredSafAttributes = false;
1451 hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001452 hasBindDreamServicePermission = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001453 if (withinApplication) {
1454 if(tag == "activity") {
1455 withinActivity = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001456 activityName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001457 if (error != "") {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001458 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1459 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001460 goto bail;
1461 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001462
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001463 activityLabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1464 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001465 if (error != "") {
1466 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
1467 error.string());
1468 goto bail;
1469 }
1470
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001471 activityIcon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1472 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001473 if (error != "") {
1474 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1475 error.string());
1476 goto bail;
1477 }
1478
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001479 activityBanner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1480 &error);
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001481 if (error != "") {
1482 fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
1483 error.string());
1484 goto bail;
1485 }
1486
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001487 int32_t orien = AaptXml::getResolvedIntegerAttribute(res, tree,
Michael Wrightec4fdec2013-09-06 16:50:52 -07001488 SCREEN_ORIENTATION_ATTR, &error);
1489 if (error == "") {
1490 if (orien == 0 || orien == 6 || orien == 8) {
1491 // Requests landscape, sensorLandscape, or reverseLandscape.
Adam Lesinski2c72b682014-06-24 09:56:01 -07001492 addImpliedFeature(&impliedFeatures, "android.hardware.screen.landscape",
1493 "one or more activities have specified a landscape orientation");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001494 } else if (orien == 1 || orien == 7 || orien == 9) {
1495 // Requests portrait, sensorPortrait, or reversePortrait.
Adam Lesinski2c72b682014-06-24 09:56:01 -07001496 addImpliedFeature(&impliedFeatures, "android.hardware.screen.portrait",
1497 "one or more activities have specified a portrait orientation");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001498 }
1499 }
1500 } else if (tag == "uses-library") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001501 String8 libraryName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001502 if (error != "") {
1503 fprintf(stderr,
1504 "ERROR getting 'android:name' attribute for uses-library"
1505 " %s\n", error.string());
1506 goto bail;
1507 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001508 int req = AaptXml::getIntegerAttribute(tree,
1509 REQUIRED_ATTR, 1);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001510 printf("uses-library%s:'%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001511 req ? "" : "-not-required", ResTable::normalizeForOutput(
1512 libraryName.string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001513 } else if (tag == "receiver") {
1514 withinReceiver = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001515 receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001516
1517 if (error != "") {
1518 fprintf(stderr,
1519 "ERROR getting 'android:name' attribute for receiver:"
1520 " %s\n", error.string());
1521 goto bail;
1522 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001523
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001524 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1525 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001526 if (error == "") {
1527 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1528 hasBindDeviceAdminPermission = true;
1529 }
1530 } else {
1531 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1532 " receiver '%s': %s\n", receiverName.string(), error.string());
1533 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001534 } else if (tag == "service") {
1535 withinService = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001536 serviceName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001537
1538 if (error != "") {
1539 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1540 "service:%s\n", error.string());
1541 goto bail;
1542 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001543
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001544 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1545 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001546 if (error == "") {
1547 if (permission == "android.permission.BIND_INPUT_METHOD") {
1548 hasBindInputMethodPermission = true;
1549 } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") {
1550 hasBindAccessibilityServicePermission = true;
1551 } else if (permission == "android.permission.BIND_PRINT_SERVICE") {
1552 hasBindPrintServicePermission = true;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001553 } else if (permission == "android.permission.BIND_NFC_SERVICE") {
1554 hasBindNfcServicePermission = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001555 } else if (permission == "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
1556 hasBindNotificationListenerServicePermission = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001557 } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
1558 hasBindDreamServicePermission = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001559 }
1560 } else {
1561 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1562 " service '%s': %s\n", serviceName.string(), error.string());
1563 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001564 } else if (tag == "provider") {
1565 withinProvider = true;
1566
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001567 bool exported = AaptXml::getResolvedIntegerAttribute(res, tree,
1568 EXPORTED_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001569 if (error != "") {
1570 fprintf(stderr, "ERROR getting 'android:exported' attribute for provider:"
1571 " %s\n", error.string());
1572 goto bail;
1573 }
1574
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001575 bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute(
1576 res, tree, GRANT_URI_PERMISSIONS_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001577 if (error != "") {
1578 fprintf(stderr, "ERROR getting 'android:grantUriPermissions' attribute for provider:"
1579 " %s\n", error.string());
1580 goto bail;
1581 }
1582
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001583 String8 permission = AaptXml::getResolvedAttribute(res, tree,
1584 PERMISSION_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001585 if (error != "") {
1586 fprintf(stderr, "ERROR getting 'android:permission' attribute for provider:"
1587 " %s\n", error.string());
1588 goto bail;
1589 }
1590
1591 hasRequiredSafAttributes |= exported && grantUriPermissions &&
1592 permission == "android.permission.MANAGE_DOCUMENTS";
1593
Michael Wrightec4fdec2013-09-06 16:50:52 -07001594 } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001595 String8 metaDataName = AaptXml::getResolvedAttribute(res, tree,
1596 NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001597 if (error != "") {
1598 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1599 "meta-data:%s\n", error.string());
1600 goto bail;
1601 }
Maurice Chu2675f762013-10-22 17:33:11 -07001602 printf("meta-data: name='%s' ",
1603 ResTable::normalizeForOutput(metaDataName.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001604 printResolvedResourceAttribute(res, tree, VALUE_ATTR, String8("value"),
Maurice Chu76327312013-10-16 18:28:46 -07001605 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001606 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001607 // Try looking for a RESOURCE_ATTR
1608 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001609 printResolvedResourceAttribute(res, tree, RESOURCE_ATTR,
Maurice Chu76327312013-10-16 18:28:46 -07001610 String8("resource"), &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001611 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001612 fprintf(stderr, "ERROR getting 'android:value' or "
1613 "'android:resource' attribute for "
1614 "meta-data:%s\n", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001615 goto bail;
1616 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001617 }
Maurice Chu76327312013-10-16 18:28:46 -07001618 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001619 } else if (withinSupportsInput && tag == "input-type") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001620 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001621 if (name != "" && error == "") {
1622 supportedInput.add(name);
1623 } else {
1624 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1625 error.string());
1626 goto bail;
1627 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001628 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001629 } else if (withinFeatureGroup && tag == "uses-feature") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001630 FeatureGroup& top = featureGroups.editTop();
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001631
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001632 String8 name = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001633 if (name != "" && error == "") {
Adam Lesinskid3edfde2014-08-08 17:32:44 -07001634 top.features.add(name, true);
1635 addParentFeatures(&top, name);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001636 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001637 int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR,
1638 &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001639 if (error == "") {
1640 if (vers > top.openGLESVersion) {
1641 top.openGLESVersion = vers;
1642 }
1643 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001644 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001645 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001646 } else if (depth == 4) {
1647 if (tag == "intent-filter") {
1648 hasIntentFilter = true;
1649 withinIntentFilter = true;
1650 actMainActivity = false;
1651 actWidgetReceivers = false;
1652 actImeService = false;
1653 actWallpaperService = false;
1654 actAccessibilityService = false;
1655 actPrintService = false;
1656 actDeviceAdminEnabled = false;
1657 actHostApduService = false;
1658 actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001659 actDocumentsProvider = false;
1660 actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001661 actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001662 actCamera = false;
1663 actCameraSecure = false;
1664 catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001665 } else if (withinService && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001666 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001667 if (error != "") {
1668 fprintf(stderr, "ERROR getting 'android:name' attribute for"
1669 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1670 goto bail;
1671 }
1672
1673 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1674 name == "android.nfc.cardemulation.off_host_apdu_service") {
1675 bool offHost = true;
1676 if (name == "android.nfc.cardemulation.host_apdu_service") {
1677 offHost = false;
1678 }
1679
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001680 String8 xmlPath = AaptXml::getResolvedAttribute(res, tree,
1681 RESOURCE_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001682 if (error != "") {
1683 fprintf(stderr, "ERROR getting 'android:resource' attribute for"
1684 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1685 goto bail;
1686 }
1687
1688 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1689 offHost, &error);
1690 if (error != "") {
1691 fprintf(stderr, "ERROR getting AID category for service '%s'\n",
1692 serviceName.string());
1693 goto bail;
1694 }
1695
1696 const size_t catLen = categories.size();
1697 for (size_t i = 0; i < catLen; i++) {
1698 bool paymentCategory = (categories[i] == "payment");
1699 if (offHost) {
1700 hasMetaOffHostPaymentCategory |= paymentCategory;
1701 } else {
1702 hasMetaHostPaymentCategory |= paymentCategory;
1703 }
1704 }
1705 }
1706 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001707 } else if ((depth == 5) && withinIntentFilter) {
1708 String8 action;
1709 if (tag == "action") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001710 action = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001711 if (error != "") {
1712 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1713 error.string());
1714 goto bail;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001715 }
1716
Adam Lesinskia5018c92013-09-30 16:23:15 -07001717 if (withinActivity) {
1718 if (action == "android.intent.action.MAIN") {
1719 isMainActivity = true;
1720 actMainActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001721 } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
1722 action == "android.media.action.VIDEO_CAMERA") {
1723 actCamera = true;
1724 } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
1725 actCameraSecure = true;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001726 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001727 } else if (withinReceiver) {
1728 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1729 actWidgetReceivers = true;
1730 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1731 actDeviceAdminEnabled = true;
1732 }
1733 } else if (withinService) {
1734 if (action == "android.view.InputMethod") {
1735 actImeService = true;
1736 } else if (action == "android.service.wallpaper.WallpaperService") {
1737 actWallpaperService = true;
1738 } else if (action == "android.accessibilityservice.AccessibilityService") {
1739 actAccessibilityService = true;
1740 } else if (action == "android.printservice.PrintService") {
1741 actPrintService = true;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001742 } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
1743 actHostApduService = true;
1744 } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
1745 actOffHostApduService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001746 } else if (action == "android.service.notification.NotificationListenerService") {
1747 actNotificationListenerService = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001748 } else if (action == "android.service.dreams.DreamService") {
1749 actDreamService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001750 }
1751 } else if (withinProvider) {
1752 if (action == "android.content.action.DOCUMENTS_PROVIDER") {
1753 actDocumentsProvider = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001754 }
1755 }
1756 if (action == "android.intent.action.SEARCH") {
1757 isSearchable = true;
1758 }
1759 }
1760
1761 if (tag == "category") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001762 String8 category = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001763 if (error != "") {
1764 fprintf(stderr, "ERROR getting 'name' attribute: %s\n",
1765 error.string());
1766 goto bail;
1767 }
1768 if (withinActivity) {
1769 if (category == "android.intent.category.LAUNCHER") {
1770 isLauncherActivity = true;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001771 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
1772 isLeanbackLauncherActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001773 } else if (category == "android.intent.category.HOME") {
1774 catLauncher = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001775 }
1776 }
1777 }
1778 }
1779 }
1780
1781 // Pre-1.6 implicitly granted permission compatibility logic
1782 if (targetSdk < 4) {
1783 if (!hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001784 printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
1785 printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
1786 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001787 hasWriteExternalStoragePermission = true;
1788 }
1789 if (!hasReadPhoneStatePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001790 printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
1791 printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
1792 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001793 }
1794 }
1795
1796 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1797 // force them to always take READ_EXTERNAL_STORAGE as well. We always
1798 // do this (regardless of target API version) because we can't have
1799 // an app with write permission but not read permission.
1800 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001801 printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"));
1802 printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
1803 String8("requested WRITE_EXTERNAL_STORAGE"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001804 }
1805
1806 // Pre-JellyBean call log permission compatibility.
1807 if (targetSdk < 16) {
1808 if (!hasReadCallLogPermission && hasReadContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001809 printUsesPermission(String8("android.permission.READ_CALL_LOG"));
1810 printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
1811 String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001812 }
1813 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001814 printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
1815 printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
1816 String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001817 }
1818 }
1819
Adam Lesinski2c72b682014-06-24 09:56:01 -07001820 addImpliedFeature(&impliedFeatures, "android.hardware.touchscreen",
1821 "default feature for all apps");
1822
1823 const size_t numFeatureGroups = featureGroups.size();
1824 if (numFeatureGroups == 0) {
1825 // If no <feature-group> tags were defined, apply auto-implied features.
1826 printFeatureGroup(commonFeatures, &impliedFeatures);
1827
1828 } else {
1829 // <feature-group> tags are defined, so we ignore implied features and
1830 for (size_t i = 0; i < numFeatureGroups; i++) {
1831 FeatureGroup& grp = featureGroups.editItemAt(i);
1832
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001833 if (commonFeatures.openGLESVersion > grp.openGLESVersion) {
1834 grp.openGLESVersion = commonFeatures.openGLESVersion;
1835 }
1836
Adam Lesinski2c72b682014-06-24 09:56:01 -07001837 // Merge the features defined in the top level (not inside a <feature-group>)
1838 // with this feature group.
1839 const size_t numCommonFeatures = commonFeatures.features.size();
1840 for (size_t j = 0; j < numCommonFeatures; j++) {
1841 if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001842 grp.features.add(commonFeatures.features.keyAt(j),
1843 commonFeatures.features[j]);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001844 }
1845 }
1846
1847 if (!grp.features.isEmpty()) {
1848 printFeatureGroup(grp);
Adam Lesinski282e1812014-01-23 18:17:42 -08001849 }
1850 }
1851 }
1852
Adam Lesinski282e1812014-01-23 18:17:42 -08001853
Adam Lesinski282e1812014-01-23 18:17:42 -08001854 if (hasWidgetReceivers) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001855 printComponentPresence("app-widget");
Adam Lesinski282e1812014-01-23 18:17:42 -08001856 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001857 if (hasDeviceAdminReceiver) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001858 printComponentPresence("device-admin");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001859 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001860 if (hasImeService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001861 printComponentPresence("ime");
Adam Lesinski282e1812014-01-23 18:17:42 -08001862 }
1863 if (hasWallpaperService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001864 printComponentPresence("wallpaper");
Adam Lesinski282e1812014-01-23 18:17:42 -08001865 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001866 if (hasAccessibilityService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001867 printComponentPresence("accessibility");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001868 }
1869 if (hasPrintService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001870 printComponentPresence("print-service");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001871 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001872 if (hasPaymentService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001873 printComponentPresence("payment");
1874 }
1875 if (isSearchable) {
1876 printComponentPresence("search");
1877 }
1878 if (hasDocumentsProvider) {
1879 printComponentPresence("document-provider");
1880 }
1881 if (hasLauncher) {
1882 printComponentPresence("launcher");
1883 }
1884 if (hasNotificationListenerService) {
1885 printComponentPresence("notification-listener");
1886 }
John Spurlockeb8d1be2014-06-25 17:46:15 -04001887 if (hasDreamService) {
1888 printComponentPresence("dream");
1889 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001890 if (hasCameraActivity) {
1891 printComponentPresence("camera");
1892 }
1893 if (hasCameraSecureActivity) {
1894 printComponentPresence("camera-secure");
1895 }
1896
1897 if (hasMainActivity) {
1898 printf("main\n");
Adam Lesinski94fc9122013-09-30 17:16:09 -07001899 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001900 if (hasOtherActivities) {
1901 printf("other-activities\n");
1902 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001903 if (hasOtherReceivers) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001904 printf("other-receivers\n");
1905 }
1906 if (hasOtherServices) {
1907 printf("other-services\n");
1908 }
1909
1910 // For modern apps, if screen size buckets haven't been specified
1911 // but the new width ranges have, then infer the buckets from them.
1912 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1913 && requiresSmallestWidthDp > 0) {
1914 int compatWidth = compatibleWidthLimitDp;
1915 if (compatWidth <= 0) {
1916 compatWidth = requiresSmallestWidthDp;
1917 }
1918 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1919 smallScreen = -1;
1920 } else {
1921 smallScreen = 0;
1922 }
1923 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1924 normalScreen = -1;
1925 } else {
1926 normalScreen = 0;
1927 }
1928 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1929 largeScreen = -1;
1930 } else {
1931 largeScreen = 0;
1932 }
1933 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1934 xlargeScreen = -1;
1935 } else {
1936 xlargeScreen = 0;
1937 }
1938 }
1939
1940 // Determine default values for any unspecified screen sizes,
1941 // based on the target SDK of the package. As of 4 (donut)
1942 // the screen size support was introduced, so all default to
1943 // enabled.
1944 if (smallScreen > 0) {
1945 smallScreen = targetSdk >= 4 ? -1 : 0;
1946 }
1947 if (normalScreen > 0) {
1948 normalScreen = -1;
1949 }
1950 if (largeScreen > 0) {
1951 largeScreen = targetSdk >= 4 ? -1 : 0;
1952 }
1953 if (xlargeScreen > 0) {
1954 // Introduced in Gingerbread.
1955 xlargeScreen = targetSdk >= 9 ? -1 : 0;
1956 }
1957 if (anyDensity > 0) {
1958 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1959 || compatibleWidthLimitDp > 0) ? -1 : 0;
1960 }
1961 printf("supports-screens:");
1962 if (smallScreen != 0) {
1963 printf(" 'small'");
1964 }
1965 if (normalScreen != 0) {
1966 printf(" 'normal'");
1967 }
1968 if (largeScreen != 0) {
1969 printf(" 'large'");
1970 }
1971 if (xlargeScreen != 0) {
1972 printf(" 'xlarge'");
1973 }
1974 printf("\n");
1975 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
1976 if (requiresSmallestWidthDp > 0) {
1977 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1978 }
1979 if (compatibleWidthLimitDp > 0) {
1980 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1981 }
1982 if (largestWidthLimitDp > 0) {
1983 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1984 }
1985
1986 printf("locales:");
1987 const size_t NL = locales.size();
1988 for (size_t i=0; i<NL; i++) {
1989 const char* localeStr = locales[i].string();
1990 if (localeStr == NULL || strlen(localeStr) == 0) {
1991 localeStr = "--_--";
1992 }
1993 printf(" '%s'", localeStr);
1994 }
1995 printf("\n");
1996
1997 printf("densities:");
1998 const size_t ND = densities.size();
1999 for (size_t i=0; i<ND; i++) {
2000 printf(" '%d'", densities[i]);
2001 }
2002 printf("\n");
2003
2004 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
2005 if (dir != NULL) {
2006 if (dir->getFileCount() > 0) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002007 SortedVector<String8> architectures;
Adam Lesinski282e1812014-01-23 18:17:42 -08002008 for (size_t i=0; i<dir->getFileCount(); i++) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002009 architectures.add(ResTable::normalizeForOutput(
2010 dir->getFileName(i).string()));
Adam Lesinski282e1812014-01-23 18:17:42 -08002011 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07002012
2013 bool outputAltNativeCode = false;
2014 // A multiArch package is one that contains 64-bit and
2015 // 32-bit versions of native code and expects 3rd-party
2016 // apps to load these native code libraries. Since most
2017 // 64-bit systems also support 32-bit apps, the apps
2018 // loading this multiArch package's code may be either
2019 // 32-bit or 64-bit.
2020 if (hasMultiArch) {
2021 // If this is a multiArch package, report the 64-bit
2022 // version only. Then as a separate entry, report the
2023 // rest.
2024 //
2025 // If we report the 32-bit architecture, this APK will
2026 // be installed on a 32-bit device, causing a large waste
2027 // of bandwidth and disk space. This assumes that
2028 // the developer of the multiArch package has also
2029 // made a version that is 32-bit only.
2030 String8 intel64("x86_64");
2031 String8 arm64("arm64-v8a");
2032 ssize_t index = architectures.indexOf(intel64);
2033 if (index < 0) {
2034 index = architectures.indexOf(arm64);
2035 }
2036
2037 if (index >= 0) {
2038 printf("native-code: '%s'\n", architectures[index].string());
2039 architectures.removeAt(index);
2040 outputAltNativeCode = true;
2041 }
2042 }
2043
2044 const size_t archCount = architectures.size();
2045 if (archCount > 0) {
2046 if (outputAltNativeCode) {
2047 printf("alt-");
2048 }
2049 printf("native-code:");
2050 for (size_t i = 0; i < archCount; i++) {
2051 printf(" '%s'", architectures[i].string());
2052 }
2053 printf("\n");
2054 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002055 }
2056 delete dir;
2057 }
2058 } else if (strcmp("badger", option) == 0) {
2059 printf("%s", CONSOLE_DATA);
2060 } else if (strcmp("configurations", option) == 0) {
2061 Vector<ResTable_config> configs;
2062 res.getConfigurations(&configs);
2063 const size_t N = configs.size();
2064 for (size_t i=0; i<N; i++) {
2065 printf("%s\n", configs[i].toString().string());
2066 }
2067 } else {
2068 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2069 goto bail;
2070 }
2071 }
2072
2073 result = NO_ERROR;
2074
2075bail:
2076 if (asset) {
2077 delete asset;
2078 }
2079 return (result != NO_ERROR);
2080}
2081
2082
2083/*
2084 * Handle the "add" command, which wants to add files to a new or
2085 * pre-existing archive.
2086 */
2087int doAdd(Bundle* bundle)
2088{
2089 ZipFile* zip = NULL;
2090 status_t result = UNKNOWN_ERROR;
2091 const char* zipFileName;
2092
2093 if (bundle->getUpdate()) {
2094 /* avoid confusion */
2095 fprintf(stderr, "ERROR: can't use '-u' with add\n");
2096 goto bail;
2097 }
2098
2099 if (bundle->getFileSpecCount() < 1) {
2100 fprintf(stderr, "ERROR: must specify zip file name\n");
2101 goto bail;
2102 }
2103 zipFileName = bundle->getFileSpecEntry(0);
2104
2105 if (bundle->getFileSpecCount() < 2) {
2106 fprintf(stderr, "NOTE: nothing to do\n");
2107 goto bail;
2108 }
2109
2110 zip = openReadWrite(zipFileName, true);
2111 if (zip == NULL) {
2112 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2113 goto bail;
2114 }
2115
2116 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2117 const char* fileName = bundle->getFileSpecEntry(i);
2118
2119 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
2120 printf(" '%s'... (from gzip)\n", fileName);
2121 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
2122 } else {
2123 if (bundle->getJunkPath()) {
2124 String8 storageName = String8(fileName).getPathLeaf();
Maurice Chu2675f762013-10-22 17:33:11 -07002125 printf(" '%s' as '%s'...\n", fileName,
2126 ResTable::normalizeForOutput(storageName.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08002127 result = zip->add(fileName, storageName.string(),
2128 bundle->getCompressionMethod(), NULL);
2129 } else {
2130 printf(" '%s'...\n", fileName);
2131 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2132 }
2133 }
2134 if (result != NO_ERROR) {
2135 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2136 if (result == NAME_NOT_FOUND) {
2137 fprintf(stderr, ": file not found\n");
2138 } else if (result == ALREADY_EXISTS) {
2139 fprintf(stderr, ": already exists in archive\n");
2140 } else {
2141 fprintf(stderr, "\n");
2142 }
2143 goto bail;
2144 }
2145 }
2146
2147 result = NO_ERROR;
2148
2149bail:
2150 delete zip;
2151 return (result != NO_ERROR);
2152}
2153
2154
2155/*
2156 * Delete files from an existing archive.
2157 */
2158int doRemove(Bundle* bundle)
2159{
2160 ZipFile* zip = NULL;
2161 status_t result = UNKNOWN_ERROR;
2162 const char* zipFileName;
2163
2164 if (bundle->getFileSpecCount() < 1) {
2165 fprintf(stderr, "ERROR: must specify zip file name\n");
2166 goto bail;
2167 }
2168 zipFileName = bundle->getFileSpecEntry(0);
2169
2170 if (bundle->getFileSpecCount() < 2) {
2171 fprintf(stderr, "NOTE: nothing to do\n");
2172 goto bail;
2173 }
2174
2175 zip = openReadWrite(zipFileName, false);
2176 if (zip == NULL) {
2177 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2178 zipFileName);
2179 goto bail;
2180 }
2181
2182 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2183 const char* fileName = bundle->getFileSpecEntry(i);
2184 ZipEntry* entry;
2185
2186 entry = zip->getEntryByName(fileName);
2187 if (entry == NULL) {
2188 printf(" '%s' NOT FOUND\n", fileName);
2189 continue;
2190 }
2191
2192 result = zip->remove(entry);
2193
2194 if (result != NO_ERROR) {
2195 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2196 bundle->getFileSpecEntry(i), zipFileName);
2197 goto bail;
2198 }
2199 }
2200
2201 /* update the archive */
2202 zip->flush();
2203
2204bail:
2205 delete zip;
2206 return (result != NO_ERROR);
2207}
2208
Adam Lesinski3921e872014-05-13 10:56:25 -07002209static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002210 const size_t numDirs = dir->getDirs().size();
2211 for (size_t i = 0; i < numDirs; i++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002212 bool ignore = ignoreConfig;
2213 const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2214 const char* dirStr = subDir->getLeaf().string();
2215 if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2216 ignore = true;
2217 }
2218 status_t err = addResourcesToBuilder(subDir, builder, ignore);
Adam Lesinskifab50872014-04-16 14:40:42 -07002219 if (err != NO_ERROR) {
2220 return err;
2221 }
2222 }
2223
2224 const size_t numFiles = dir->getFiles().size();
2225 for (size_t i = 0; i < numFiles; i++) {
2226 sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2227 const size_t numConfigs = gp->getFiles().size();
2228 for (size_t j = 0; j < numConfigs; j++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002229 status_t err = NO_ERROR;
2230 if (ignoreConfig) {
2231 err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2232 } else {
2233 err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2234 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002235 if (err != NO_ERROR) {
2236 fprintf(stderr, "Failed to add %s (%s) to builder.\n",
2237 gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
2238 return err;
2239 }
2240 }
2241 }
2242 return NO_ERROR;
2243}
2244
2245static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2246 if (split->isBase()) {
2247 return original;
2248 }
2249
2250 String8 ext(original.getPathExtension());
2251 if (ext == String8(".apk")) {
2252 return String8::format("%s_%s%s",
2253 original.getBasePath().string(),
2254 split->getDirectorySafeName().string(),
2255 ext.string());
2256 }
2257
2258 return String8::format("%s_%s", original.string(),
2259 split->getDirectorySafeName().string());
2260}
Adam Lesinski282e1812014-01-23 18:17:42 -08002261
2262/*
2263 * Package up an asset directory and associated application files.
2264 */
2265int doPackage(Bundle* bundle)
2266{
2267 const char* outputAPKFile;
2268 int retVal = 1;
2269 status_t err;
2270 sp<AaptAssets> assets;
2271 int N;
2272 FILE* fp;
2273 String8 dependencyFile;
Adam Lesinskifab50872014-04-16 14:40:42 -07002274 sp<ApkBuilder> builder;
Adam Lesinski282e1812014-01-23 18:17:42 -08002275
Anton Krumina2ef5c02014-03-12 14:46:44 -07002276 // -c en_XA or/and ar_XB means do pseudolocalization
Adam Lesinskifab50872014-04-16 14:40:42 -07002277 sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2278 err = configFilter->parse(bundle->getConfigurations());
Adam Lesinski282e1812014-01-23 18:17:42 -08002279 if (err != NO_ERROR) {
2280 goto bail;
2281 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002282 if (configFilter->containsPseudo()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002283 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2284 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002285 if (configFilter->containsPseudoBidi()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002286 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
Adam Lesinski282e1812014-01-23 18:17:42 -08002287 }
2288
2289 N = bundle->getFileSpecCount();
2290 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
Adam Lesinski09384302014-01-22 16:07:42 -08002291 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002292 fprintf(stderr, "ERROR: no input files\n");
2293 goto bail;
2294 }
2295
2296 outputAPKFile = bundle->getOutputAPKFile();
2297
2298 // Make sure the filenames provided exist and are of the appropriate type.
2299 if (outputAPKFile) {
2300 FileType type;
2301 type = getFileType(outputAPKFile);
2302 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2303 fprintf(stderr,
2304 "ERROR: output file '%s' exists but is not regular file\n",
2305 outputAPKFile);
2306 goto bail;
2307 }
2308 }
2309
2310 // Load the assets.
2311 assets = new AaptAssets();
2312
2313 // Set up the resource gathering in assets if we're going to generate
2314 // dependency files. Every time we encounter a resource while slurping
2315 // the tree, we'll add it to these stores so we have full resource paths
2316 // to write to a dependency file.
2317 if (bundle->getGenDependencies()) {
2318 sp<FilePathStore> resPathStore = new FilePathStore;
2319 assets->setFullResPaths(resPathStore);
2320 sp<FilePathStore> assetPathStore = new FilePathStore;
2321 assets->setFullAssetPaths(assetPathStore);
2322 }
2323
2324 err = assets->slurpFromArgs(bundle);
2325 if (err < 0) {
2326 goto bail;
2327 }
2328
2329 if (bundle->getVerbose()) {
2330 assets->print(String8());
2331 }
2332
Adam Lesinskifab50872014-04-16 14:40:42 -07002333 // Create the ApkBuilder, which will collect the compiled files
2334 // to write to the final APK (or sets of APKs if we are building
2335 // a Split APK.
2336 builder = new ApkBuilder(configFilter);
2337
2338 // If we are generating a Split APK, find out which configurations to split on.
2339 if (bundle->getSplitConfigurations().size() > 0) {
2340 const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2341 const size_t numSplits = splitStrs.size();
2342 for (size_t i = 0; i < numSplits; i++) {
2343 std::set<ConfigDescription> configs;
2344 if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2345 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2346 goto bail;
2347 }
2348
2349 err = builder->createSplitForConfigs(configs);
2350 if (err != NO_ERROR) {
2351 goto bail;
2352 }
2353 }
2354 }
2355
Adam Lesinski282e1812014-01-23 18:17:42 -08002356 // If they asked for any fileAs that need to be compiled, do so.
2357 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002358 err = buildResources(bundle, assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002359 if (err != 0) {
2360 goto bail;
2361 }
2362 }
2363
2364 // At this point we've read everything and processed everything. From here
2365 // on out it's just writing output files.
2366 if (SourcePos::hasErrors()) {
2367 goto bail;
2368 }
2369
2370 // Update symbols with information about which ones are needed as Java symbols.
2371 assets->applyJavaSymbols();
2372 if (SourcePos::hasErrors()) {
2373 goto bail;
2374 }
2375
2376 // If we've been asked to generate a dependency file, do that here
2377 if (bundle->getGenDependencies()) {
2378 // If this is the packaging step, generate the dependency file next to
2379 // the output apk (e.g. bin/resources.ap_.d)
2380 if (outputAPKFile) {
2381 dependencyFile = String8(outputAPKFile);
2382 // Add the .d extension to the dependency file.
2383 dependencyFile.append(".d");
2384 } else {
2385 // Else if this is the R.java dependency generation step,
2386 // generate the dependency file in the R.java package subdirectory
2387 // e.g. gen/com/foo/app/R.java.d
2388 dependencyFile = String8(bundle->getRClassDir());
2389 dependencyFile.appendPath("R.java.d");
2390 }
2391 // Make sure we have a clean dependency file to start with
2392 fp = fopen(dependencyFile, "w");
2393 fclose(fp);
2394 }
2395
2396 // Write out R.java constants
2397 if (!assets->havePrivateSymbols()) {
2398 if (bundle->getCustomPackage() == NULL) {
2399 // Write the R.java file into the appropriate class directory
2400 // e.g. gen/com/foo/app/R.java
Adam Lesinski1e4663852014-08-15 14:47:28 -07002401 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true,
2402 bundle->getBuildSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002403 } else {
2404 const String8 customPkg(bundle->getCustomPackage());
Adam Lesinski1e4663852014-08-15 14:47:28 -07002405 err = writeResourceSymbols(bundle, assets, customPkg, true,
2406 bundle->getBuildSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002407 }
2408 if (err < 0) {
2409 goto bail;
2410 }
2411 // If we have library files, we're going to write our R.java file into
2412 // the appropriate class directory for those libraries as well.
2413 // e.g. gen/com/foo/app/lib/R.java
2414 if (bundle->getExtraPackages() != NULL) {
2415 // Split on colon
2416 String8 libs(bundle->getExtraPackages());
2417 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2418 while (packageString != NULL) {
2419 // Write the R.java file out with the correct package name
Marcin Kosiba0f3a5a62014-09-11 13:48:48 +01002420 err = writeResourceSymbols(bundle, assets, String8(packageString), true,
2421 bundle->getBuildSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002422 if (err < 0) {
2423 goto bail;
2424 }
2425 packageString = strtok(NULL, ":");
2426 }
2427 libs.unlockBuffer();
2428 }
2429 } else {
Adam Lesinski1e4663852014-08-15 14:47:28 -07002430 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002431 if (err < 0) {
2432 goto bail;
2433 }
Adam Lesinski1e4663852014-08-15 14:47:28 -07002434 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002435 if (err < 0) {
2436 goto bail;
2437 }
2438 }
2439
2440 // Write out the ProGuard file
2441 err = writeProguardFile(bundle, assets);
2442 if (err < 0) {
2443 goto bail;
2444 }
2445
2446 // Write the apk
2447 if (outputAPKFile) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002448 // Gather all resources and add them to the APK Builder. The builder will then
2449 // figure out which Split they belong in.
2450 err = addResourcesToBuilder(assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002451 if (err != NO_ERROR) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002452 goto bail;
2453 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002454
2455 const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2456 const size_t numSplits = splits.size();
2457 for (size_t i = 0; i < numSplits; i++) {
2458 const sp<ApkSplit>& split = splits[i];
2459 String8 outputPath = buildApkName(String8(outputAPKFile), split);
2460 err = writeAPK(bundle, outputPath, split);
2461 if (err != NO_ERROR) {
2462 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2463 goto bail;
2464 }
2465 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002466 }
2467
2468 // If we've been asked to generate a dependency file, we need to finish up here.
2469 // the writeResourceSymbols and writeAPK functions have already written the target
2470 // half of the dependency file, now we need to write the prerequisites. (files that
2471 // the R.java file or .ap_ file depend on)
2472 if (bundle->getGenDependencies()) {
2473 // Now that writeResourceSymbols or writeAPK has taken care of writing
2474 // the targets to our dependency file, we'll write the prereqs
2475 fp = fopen(dependencyFile, "a+");
2476 fprintf(fp, " : ");
2477 bool includeRaw = (outputAPKFile != NULL);
2478 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2479 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2480 // and therefore was not added to our pathstores during slurping
2481 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2482 fclose(fp);
2483 }
2484
2485 retVal = 0;
2486bail:
2487 if (SourcePos::hasErrors()) {
2488 SourcePos::printErrors(stderr);
2489 }
2490 return retVal;
2491}
2492
2493/*
2494 * Do PNG Crunching
2495 * PRECONDITIONS
2496 * -S flag points to a source directory containing drawable* folders
2497 * -C flag points to destination directory. The folder structure in the
2498 * source directory will be mirrored to the destination (cache) directory
2499 *
2500 * POSTCONDITIONS
2501 * Destination directory will be updated to match the PNG files in
Maurice Chu2675f762013-10-22 17:33:11 -07002502 * the source directory.
Adam Lesinski282e1812014-01-23 18:17:42 -08002503 */
2504int doCrunch(Bundle* bundle)
2505{
2506 fprintf(stdout, "Crunching PNG Files in ");
2507 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2508 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2509
2510 updatePreProcessedCache(bundle);
2511
2512 return NO_ERROR;
2513}
2514
2515/*
2516 * Do PNG Crunching on a single flag
2517 * -i points to a single png file
2518 * -o points to a single png output file
2519 */
2520int doSingleCrunch(Bundle* bundle)
2521{
2522 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2523 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2524
2525 String8 input(bundle->getSingleCrunchInputFile());
2526 String8 output(bundle->getSingleCrunchOutputFile());
2527
2528 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2529 // we can't return the status_t as it gets truncate to the lower 8 bits.
2530 return 42;
2531 }
2532
2533 return NO_ERROR;
2534}
2535
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002536int runInDaemonMode(Bundle* bundle) {
2537 std::cout << "Ready" << std::endl;
2538 for (std::string line; std::getline(std::cin, line);) {
2539 if (line == "quit") {
2540 return NO_ERROR;
2541 }
2542 std::stringstream ss;
2543 ss << line;
2544 std::string s;
2545
2546 std::string command, parameterOne, parameterTwo;
2547 std::getline(ss, command, ' ');
2548 std::getline(ss, parameterOne, ' ');
2549 std::getline(ss, parameterTwo, ' ');
2550 if (command[0] == 's') {
2551 bundle->setSingleCrunchInputFile(parameterOne.c_str());
2552 bundle->setSingleCrunchOutputFile(parameterTwo.c_str());
2553 std::cout << "Crunching " << parameterOne << std::endl;
2554 if (doSingleCrunch(bundle) != NO_ERROR) {
2555 std::cout << "Error" << std::endl;
2556 }
2557 std::cout << "Done" << std::endl;
2558 } else {
2559 // in case of invalid command, just bail out.
2560 std::cerr << "Unknown command" << std::endl;
2561 return -1;
2562 }
2563 }
2564 return -1;
2565}
2566
Adam Lesinski282e1812014-01-23 18:17:42 -08002567char CONSOLE_DATA[2925] = {
2568 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2569 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2570 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2571 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2572 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2573 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2574 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2575 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2576 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2577 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2578 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2579 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2580 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2581 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2582 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2583 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2584 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2585 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2586 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2587 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2588 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2589 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2590 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2591 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2592 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2593 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2594 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2595 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2596 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2597 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2598 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2599 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2600 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2601 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2602 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2603 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2604 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2605 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2606 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2607 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2608 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2609 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2610 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2611 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2612 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2613 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2614 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2615 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2616 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2617 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2618 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2619 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2620 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2621 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2622 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2623 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2624 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2625 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2626 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2627 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2628 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2629 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2630 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2631 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2632 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2633 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2634 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2635 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2636 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2637 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2638 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2639 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2640 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2641 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2642 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2643 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2644 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2645 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2646 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2647 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2648 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2649 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2650 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2651 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2652 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2653 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2654 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2655 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2656 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2657 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2658 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2659 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2660 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2661 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2662 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2663 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2664 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2665 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2666 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2667 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2668 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2669 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2670 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2671 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2672 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2673 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2674 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2675 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2676 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2677 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2678 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2679 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2680 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2681 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2682 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2683 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2684 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2685 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2686 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2687 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2688 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2689 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2690 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2691 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2692 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2693 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2694 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2695 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2696 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2697 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2698 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2699 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2700 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2701 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2702 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2703 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2704 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2705 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2706 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2707 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2708 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2709 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2710 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2711 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2712 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2713 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2714 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2715 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2716 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2717 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2718 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2719 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2720 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2721 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2722 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2723 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2724 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2725 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2726 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2727 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2728 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2729 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2730 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2731 };