blob: 21f47bc28ff35c31d6f8f9e4066d345b96cc7141 [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
Elliott Hughesba3fe562015-08-12 14:49:53 -0700218#ifdef __ANDROID__
Andreas Gampeb8dc7bc2014-10-01 19:07:51 -0700219 static const bool kHaveAndroidOs = true;
220#else
221 static const bool kHaveAndroidOs = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800222#endif
Andreas Gampeb8dc7bc2014-10-01 19:07:51 -0700223 const ResTable& res = assets.getResources(false);
224 if (!kHaveAndroidOs) {
225 printf("\nResource table:\n");
226 res.print(false);
227 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800228
229 Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
230 Asset::ACCESS_BUFFER);
231 if (manifestAsset == NULL) {
232 printf("\nNo AndroidManifest.xml found.\n");
233 } else {
234 printf("\nAndroid manifest:\n");
235 ResXMLTree tree;
236 tree.setTo(manifestAsset->getBuffer(true),
237 manifestAsset->getLength());
238 printXMLBlock(&tree);
239 }
240 delete manifestAsset;
241 }
242
243 result = 0;
244
245bail:
246 delete zip;
247 return result;
248}
249
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700250static void printResolvedResourceAttribute(const ResTable& resTable, const ResXMLTree& tree,
Maurice Chu76327312013-10-16 18:28:46 -0700251 uint32_t attrRes, String8 attrLabel, String8* outError)
252{
253 Res_value value;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700254 AaptXml::getResolvedResourceAttribute(resTable, tree, attrRes, &value, outError);
Maurice Chu76327312013-10-16 18:28:46 -0700255 if (*outError != "") {
256 *outError = "error print resolved resource attribute";
257 return;
258 }
259 if (value.dataType == Res_value::TYPE_STRING) {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700260 String8 result = AaptXml::getResolvedAttribute(resTable, tree, attrRes, outError);
Maurice Chu2675f762013-10-22 17:33:11 -0700261 printf("%s='%s'", attrLabel.string(),
262 ResTable::normalizeForOutput(result.string()).string());
Maurice Chu76327312013-10-16 18:28:46 -0700263 } else if (Res_value::TYPE_FIRST_INT <= value.dataType &&
264 value.dataType <= Res_value::TYPE_LAST_INT) {
265 printf("%s='%d'", attrLabel.string(), value.data);
266 } else {
267 printf("%s='0x%x'", attrLabel.string(), (int)value.data);
268 }
269}
270
Adam Lesinski282e1812014-01-23 18:17:42 -0800271// These are attribute resource constants for the platform, as found
272// in android.R.attr
273enum {
274 LABEL_ATTR = 0x01010001,
275 ICON_ATTR = 0x01010002,
276 NAME_ATTR = 0x01010003,
Adam Lesinskia5018c92013-09-30 16:23:15 -0700277 PERMISSION_ATTR = 0x01010006,
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700278 EXPORTED_ATTR = 0x01010010,
279 GRANT_URI_PERMISSIONS_ATTR = 0x0101001b,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700280 RESOURCE_ATTR = 0x01010025,
Adam Lesinski282e1812014-01-23 18:17:42 -0800281 DEBUGGABLE_ATTR = 0x0101000f,
282 VALUE_ATTR = 0x01010024,
283 VERSION_CODE_ATTR = 0x0101021b,
284 VERSION_NAME_ATTR = 0x0101021c,
285 SCREEN_ORIENTATION_ATTR = 0x0101001e,
286 MIN_SDK_VERSION_ATTR = 0x0101020c,
287 MAX_SDK_VERSION_ATTR = 0x01010271,
288 REQ_TOUCH_SCREEN_ATTR = 0x01010227,
289 REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
290 REQ_HARD_KEYBOARD_ATTR = 0x01010229,
291 REQ_NAVIGATION_ATTR = 0x0101022a,
292 REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
293 TARGET_SDK_VERSION_ATTR = 0x01010270,
294 TEST_ONLY_ATTR = 0x01010272,
295 ANY_DENSITY_ATTR = 0x0101026c,
296 GL_ES_VERSION_ATTR = 0x01010281,
297 SMALL_SCREEN_ATTR = 0x01010284,
298 NORMAL_SCREEN_ATTR = 0x01010285,
299 LARGE_SCREEN_ATTR = 0x01010286,
300 XLARGE_SCREEN_ATTR = 0x010102bf,
301 REQUIRED_ATTR = 0x0101028e,
Adam Lesinskicaf797c2014-08-22 12:56:26 -0700302 INSTALL_LOCATION_ATTR = 0x010102b7,
Adam Lesinski282e1812014-01-23 18:17:42 -0800303 SCREEN_SIZE_ATTR = 0x010102ca,
304 SCREEN_DENSITY_ATTR = 0x010102cb,
305 REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
306 COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
307 LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
308 PUBLIC_KEY_ATTR = 0x010103a6,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700309 CATEGORY_ATTR = 0x010103e8,
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800310 BANNER_ATTR = 0x10103f2,
Tim Kilbournd9b1cad2014-10-24 12:43:41 -0700311 ISGAME_ATTR = 0x10103f4,
Adam Lesinski282e1812014-01-23 18:17:42 -0800312};
313
Maurice Chu2675f762013-10-22 17:33:11 -0700314String8 getComponentName(String8 &pkgName, String8 &componentName) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800315 ssize_t idx = componentName.find(".");
316 String8 retStr(pkgName);
317 if (idx == 0) {
318 retStr += componentName;
319 } else if (idx < 0) {
320 retStr += ".";
321 retStr += componentName;
322 } else {
Maurice Chu2675f762013-10-22 17:33:11 -0700323 return componentName;
Adam Lesinski282e1812014-01-23 18:17:42 -0800324 }
Maurice Chu2675f762013-10-22 17:33:11 -0700325 return retStr;
Adam Lesinski282e1812014-01-23 18:17:42 -0800326}
327
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700328static void printCompatibleScreens(ResXMLTree& tree, String8* outError) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800329 size_t len;
330 ResXMLTree::event_code_t code;
331 int depth = 0;
332 bool first = true;
333 printf("compatible-screens:");
334 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
335 if (code == ResXMLTree::END_TAG) {
336 depth--;
337 if (depth < 0) {
338 break;
339 }
340 continue;
341 }
342 if (code != ResXMLTree::START_TAG) {
343 continue;
344 }
345 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700346 const char16_t* ctag16 = tree.getElementName(&len);
347 if (ctag16 == NULL) {
348 *outError = "failed to get XML element name (bad string pool)";
349 return;
350 }
351 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800352 if (tag == "screen") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700353 int32_t screenSize = AaptXml::getIntegerAttribute(tree,
354 SCREEN_SIZE_ATTR);
355 int32_t screenDensity = AaptXml::getIntegerAttribute(tree,
356 SCREEN_DENSITY_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -0800357 if (screenSize > 0 && screenDensity > 0) {
358 if (!first) {
359 printf(",");
360 }
361 first = false;
362 printf("'%d/%d'", screenSize, screenDensity);
363 }
364 }
365 }
366 printf("\n");
367}
368
Adam Lesinski58f1f362013-11-12 12:59:08 -0800369static void printUsesPermission(const String8& name, bool optional=false, int maxSdkVersion=-1) {
370 printf("uses-permission: name='%s'", ResTable::normalizeForOutput(name.string()).string());
371 if (maxSdkVersion != -1) {
372 printf(" maxSdkVersion='%d'", maxSdkVersion);
373 }
374 printf("\n");
375
376 if (optional) {
377 printf("optional-permission: name='%s'",
378 ResTable::normalizeForOutput(name.string()).string());
379 if (maxSdkVersion != -1) {
380 printf(" maxSdkVersion='%d'", maxSdkVersion);
381 }
382 printf("\n");
383 }
384}
385
386static void printUsesImpliedPermission(const String8& name, const String8& reason) {
387 printf("uses-implied-permission: name='%s' reason='%s'\n",
388 ResTable::normalizeForOutput(name.string()).string(),
389 ResTable::normalizeForOutput(reason.string()).string());
390}
391
Adam Lesinski94fc9122013-09-30 17:16:09 -0700392Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool offHost,
393 String8 *outError = NULL)
394{
395 Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER);
396 if (aidAsset == NULL) {
397 if (outError != NULL) *outError = "xml resource does not exist";
398 return Vector<String8>();
399 }
400
401 const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service");
402
403 bool withinApduService = false;
404 Vector<String8> categories;
405
406 String8 error;
407 ResXMLTree tree;
408 tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength());
409
410 size_t len;
411 int depth = 0;
412 ResXMLTree::event_code_t code;
413 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
414 if (code == ResXMLTree::END_TAG) {
415 depth--;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700416 const char16_t* ctag16 = tree.getElementName(&len);
417 if (ctag16 == NULL) {
418 *outError = "failed to get XML element name (bad string pool)";
419 return Vector<String8>();
420 }
421 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700422
423 if (depth == 0 && tag == serviceTagName) {
424 withinApduService = false;
425 }
426
427 } else if (code == ResXMLTree::START_TAG) {
428 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700429 const char16_t* ctag16 = tree.getElementName(&len);
430 if (ctag16 == NULL) {
431 *outError = "failed to get XML element name (bad string pool)";
432 return Vector<String8>();
433 }
434 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700435
436 if (depth == 1) {
437 if (tag == serviceTagName) {
438 withinApduService = true;
439 }
440 } else if (depth == 2 && withinApduService) {
441 if (tag == "aid-group") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700442 String8 category = AaptXml::getAttribute(tree, CATEGORY_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700443 if (error != "") {
444 if (outError != NULL) *outError = error;
445 return Vector<String8>();
446 }
447
448 categories.add(category);
449 }
450 }
451 }
452 }
453 aidAsset->close();
454 return categories;
455}
456
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700457static void printComponentPresence(const char* componentName) {
458 printf("provides-component:'%s'\n", componentName);
459}
460
Adam Lesinski2c72b682014-06-24 09:56:01 -0700461/**
462 * Represents a feature that has been automatically added due to
463 * a pre-requisite or some other reason.
464 */
465struct ImpliedFeature {
466 /**
467 * Name of the implied feature.
468 */
469 String8 name;
470
471 /**
472 * List of human-readable reasons for why this feature was implied.
473 */
474 SortedVector<String8> reasons;
475};
476
477/**
478 * Represents a <feature-group> tag in the AndroidManifest.xml
479 */
480struct FeatureGroup {
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700481 FeatureGroup() : openGLESVersion(-1) {}
482
Adam Lesinski2c72b682014-06-24 09:56:01 -0700483 /**
484 * Human readable label
485 */
486 String8 label;
487
488 /**
489 * Explicit features defined in the group
490 */
491 KeyedVector<String8, bool> features;
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700492
493 /**
494 * OpenGL ES version required
495 */
496 int openGLESVersion;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700497};
498
499static void addImpliedFeature(KeyedVector<String8, ImpliedFeature>* impliedFeatures,
500 const char* name, const char* reason) {
501 String8 name8(name);
502 ssize_t idx = impliedFeatures->indexOfKey(name8);
503 if (idx < 0) {
504 idx = impliedFeatures->add(name8, ImpliedFeature());
505 impliedFeatures->editValueAt(idx).name = name8;
506 }
507 impliedFeatures->editValueAt(idx).reasons.add(String8(reason));
508}
509
510static void printFeatureGroup(const FeatureGroup& grp,
511 const KeyedVector<String8, ImpliedFeature>* impliedFeatures = NULL) {
512 printf("feature-group: label='%s'\n", grp.label.string());
513
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700514 if (grp.openGLESVersion > 0) {
515 printf(" uses-gl-es: '0x%x'\n", grp.openGLESVersion);
516 }
517
Adam Lesinski2c72b682014-06-24 09:56:01 -0700518 const size_t numFeatures = grp.features.size();
519 for (size_t i = 0; i < numFeatures; i++) {
Adam Lesinski73a05112014-12-08 12:53:17 -0800520 const bool required = grp.features[i];
Adam Lesinski2c72b682014-06-24 09:56:01 -0700521
522 const String8& featureName = grp.features.keyAt(i);
Adam Lesinski73a05112014-12-08 12:53:17 -0800523 printf(" uses-feature%s: name='%s'\n", (required ? "" : "-not-required"),
Adam Lesinski2c72b682014-06-24 09:56:01 -0700524 ResTable::normalizeForOutput(featureName.string()).string());
525 }
526
527 const size_t numImpliedFeatures =
528 (impliedFeatures != NULL) ? impliedFeatures->size() : 0;
529 for (size_t i = 0; i < numImpliedFeatures; i++) {
530 const ImpliedFeature& impliedFeature = impliedFeatures->valueAt(i);
531 if (grp.features.indexOfKey(impliedFeature.name) >= 0) {
532 // The feature is explicitly set, no need to use implied
533 // definition.
534 continue;
535 }
536
537 String8 printableFeatureName(ResTable::normalizeForOutput(
538 impliedFeature.name.string()));
539 printf(" uses-feature: name='%s'\n", printableFeatureName.string());
540 printf(" uses-implied-feature: name='%s' reason='",
541 printableFeatureName.string());
542 const size_t numReasons = impliedFeature.reasons.size();
543 for (size_t j = 0; j < numReasons; j++) {
544 printf("%s", impliedFeature.reasons[j].string());
545 if (j + 2 < numReasons) {
546 printf(", ");
547 } else if (j + 1 < numReasons) {
548 printf(", and ");
549 }
550 }
551 printf("'\n");
552 }
553}
554
555static void addParentFeatures(FeatureGroup* grp, const String8& name) {
556 if (name == "android.hardware.camera.autofocus" ||
557 name == "android.hardware.camera.flash") {
558 grp->features.add(String8("android.hardware.camera"), true);
559 } else if (name == "android.hardware.location.gps" ||
560 name == "android.hardware.location.network") {
561 grp->features.add(String8("android.hardware.location"), true);
562 } else if (name == "android.hardware.touchscreen.multitouch") {
563 grp->features.add(String8("android.hardware.touchscreen"), true);
564 } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
565 grp->features.add(String8("android.hardware.touchscreen.multitouch"), true);
566 grp->features.add(String8("android.hardware.touchscreen"), true);
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700567 } else if (name == "android.hardware.opengles.aep") {
568 const int openGLESVersion31 = 0x00030001;
569 if (openGLESVersion31 > grp->openGLESVersion) {
570 grp->openGLESVersion = openGLESVersion31;
571 }
Adam Lesinski2c72b682014-06-24 09:56:01 -0700572 }
573}
574
Adam Lesinski282e1812014-01-23 18:17:42 -0800575/*
576 * Handle the "dump" command, to extract select data from an archive.
577 */
578extern char CONSOLE_DATA[2925]; // see EOF
579int doDump(Bundle* bundle)
580{
581 status_t result = UNKNOWN_ERROR;
Adam Lesinski282e1812014-01-23 18:17:42 -0800582
583 if (bundle->getFileSpecCount() < 1) {
584 fprintf(stderr, "ERROR: no dump option specified\n");
585 return 1;
586 }
587
588 if (bundle->getFileSpecCount() < 2) {
589 fprintf(stderr, "ERROR: no dump file specified\n");
590 return 1;
591 }
592
593 const char* option = bundle->getFileSpecEntry(0);
594 const char* filename = bundle->getFileSpecEntry(1);
595
596 AssetManager assets;
Narayan Kamathf85e41f2014-01-24 14:14:30 +0000597 int32_t assetsCookie;
Adam Lesinski282e1812014-01-23 18:17:42 -0800598 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
599 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
600 return 1;
601 }
602
603 // Make a dummy config for retrieving resources... we need to supply
604 // non-default values for some configs so that we can retrieve resources
605 // in the app that don't have a default. The most important of these is
606 // the API version because key resources like icons will have an implicit
607 // version if they are using newer config types like density.
608 ResTable_config config;
Narayan Kamath91447d82014-01-21 15:32:36 +0000609 memset(&config, 0, sizeof(ResTable_config));
Adam Lesinski282e1812014-01-23 18:17:42 -0800610 config.language[0] = 'e';
611 config.language[1] = 'n';
612 config.country[0] = 'U';
613 config.country[1] = 'S';
614 config.orientation = ResTable_config::ORIENTATION_PORT;
615 config.density = ResTable_config::DENSITY_MEDIUM;
616 config.sdkVersion = 10000; // Very high.
617 config.screenWidthDp = 320;
618 config.screenHeightDp = 480;
619 config.smallestScreenWidthDp = 320;
Adam Lesinskic2dea8d2014-08-04 16:40:41 -0700620 config.screenLayout |= ResTable_config::SCREENSIZE_NORMAL;
Adam Lesinski282e1812014-01-23 18:17:42 -0800621 assets.setConfiguration(config);
622
623 const ResTable& res = assets.getResources(false);
Dan Albert68001652014-09-09 09:51:01 -0700624 if (res.getError() != NO_ERROR) {
Adam Lesinski25e9d552014-05-19 15:01:43 -0700625 fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
Adam Lesinski63e646e2014-07-30 11:40:39 -0700626 return 1;
Adam Lesinski282e1812014-01-23 18:17:42 -0800627 }
628
Adam Lesinski2cb761e2014-08-15 13:59:02 -0700629 // The dynamicRefTable can be null if there are no resources for this asset cookie.
630 // This fine.
Adam Lesinski63e646e2014-07-30 11:40:39 -0700631 const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700632
633 Asset* asset = NULL;
634
Adam Lesinski282e1812014-01-23 18:17:42 -0800635 if (strcmp("resources", option) == 0) {
Elliott Hughesba3fe562015-08-12 14:49:53 -0700636#ifndef __ANDROID__
Adam Lesinski282e1812014-01-23 18:17:42 -0800637 res.print(bundle->getValues());
638#endif
639
640 } else if (strcmp("strings", option) == 0) {
641 const ResStringPool* pool = res.getTableStringBlock(0);
642 printStringPool(pool);
643
644 } else if (strcmp("xmltree", option) == 0) {
645 if (bundle->getFileSpecCount() < 3) {
646 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
647 goto bail;
648 }
649
650 for (int i=2; i<bundle->getFileSpecCount(); i++) {
651 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700652 ResXMLTree tree(dynamicRefTable);
653 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800654 if (asset == NULL) {
655 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
656 goto bail;
657 }
658
659 if (tree.setTo(asset->getBuffer(true),
660 asset->getLength()) != NO_ERROR) {
661 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
662 goto bail;
663 }
664 tree.restart();
665 printXMLBlock(&tree);
666 tree.uninit();
667 delete asset;
668 asset = NULL;
669 }
670
671 } else if (strcmp("xmlstrings", option) == 0) {
672 if (bundle->getFileSpecCount() < 3) {
673 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
674 goto bail;
675 }
676
677 for (int i=2; i<bundle->getFileSpecCount(); i++) {
678 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700679 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800680 if (asset == NULL) {
681 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
682 goto bail;
683 }
684
Adam Lesinski63e646e2014-07-30 11:40:39 -0700685 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800686 if (tree.setTo(asset->getBuffer(true),
687 asset->getLength()) != NO_ERROR) {
688 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
689 goto bail;
690 }
691 printStringPool(&tree.getStrings());
692 delete asset;
693 asset = NULL;
694 }
695
696 } else {
Adam Lesinski63e646e2014-07-30 11:40:39 -0700697 asset = assets.openNonAsset(assetsCookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800698 if (asset == NULL) {
699 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
700 goto bail;
701 }
702
Adam Lesinski63e646e2014-07-30 11:40:39 -0700703 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800704 if (tree.setTo(asset->getBuffer(true),
705 asset->getLength()) != NO_ERROR) {
706 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
707 goto bail;
708 }
709 tree.restart();
710
711 if (strcmp("permissions", option) == 0) {
712 size_t len;
713 ResXMLTree::event_code_t code;
714 int depth = 0;
715 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
716 if (code == ResXMLTree::END_TAG) {
717 depth--;
718 continue;
719 }
720 if (code != ResXMLTree::START_TAG) {
721 continue;
722 }
723 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700724 const char16_t* ctag16 = tree.getElementName(&len);
725 if (ctag16 == NULL) {
726 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
727 goto bail;
728 }
729 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800730 //printf("Depth %d tag %s\n", depth, tag.string());
731 if (depth == 1) {
732 if (tag != "manifest") {
733 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
734 goto bail;
735 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700736 String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -0700737 printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800738 } else if (depth == 2 && tag == "permission") {
739 String8 error;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700740 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -0800741 if (error != "") {
742 fprintf(stderr, "ERROR: %s\n", error.string());
743 goto bail;
744 }
Maurice Chu2675f762013-10-22 17:33:11 -0700745 printf("permission: %s\n",
746 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800747 } else if (depth == 2 && tag == "uses-permission") {
748 String8 error;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700749 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -0800750 if (error != "") {
751 fprintf(stderr, "ERROR: %s\n", error.string());
752 goto bail;
753 }
Adam Lesinski58f1f362013-11-12 12:59:08 -0800754 printUsesPermission(name,
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700755 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
756 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
Adam Lesinski282e1812014-01-23 18:17:42 -0800757 }
758 }
759 } else if (strcmp("badging", option) == 0) {
760 Vector<String8> locales;
761 res.getLocales(&locales);
762
763 Vector<ResTable_config> configs;
764 res.getConfigurations(&configs);
765 SortedVector<int> densities;
766 const size_t NC = configs.size();
767 for (size_t i=0; i<NC; i++) {
768 int dens = configs[i].density;
769 if (dens == 0) {
770 dens = 160;
771 }
772 densities.add(dens);
773 }
774
775 size_t len;
776 ResXMLTree::event_code_t code;
777 int depth = 0;
778 String8 error;
779 bool withinActivity = false;
780 bool isMainActivity = false;
781 bool isLauncherActivity = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800782 bool isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800783 bool isSearchable = false;
784 bool withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700785 bool withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700786 bool withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800787 bool withinReceiver = false;
788 bool withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700789 bool withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800790 bool withinIntentFilter = false;
791 bool hasMainActivity = false;
792 bool hasOtherActivities = false;
793 bool hasOtherReceivers = false;
794 bool hasOtherServices = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700795 bool hasIntentFilter = false;
796
Adam Lesinski282e1812014-01-23 18:17:42 -0800797 bool hasWallpaperService = false;
798 bool hasImeService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700799 bool hasAccessibilityService = false;
800 bool hasPrintService = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800801 bool hasWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700802 bool hasDeviceAdminReceiver = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700803 bool hasPaymentService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700804 bool hasDocumentsProvider = false;
805 bool hasCameraActivity = false;
806 bool hasCameraSecureActivity = false;
807 bool hasLauncher = false;
808 bool hasNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400809 bool hasDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700810
Adam Lesinski282e1812014-01-23 18:17:42 -0800811 bool actMainActivity = false;
812 bool actWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700813 bool actDeviceAdminEnabled = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800814 bool actImeService = false;
815 bool actWallpaperService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700816 bool actAccessibilityService = false;
817 bool actPrintService = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700818 bool actHostApduService = false;
819 bool actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700820 bool actDocumentsProvider = false;
821 bool actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400822 bool actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700823 bool actCamera = false;
824 bool actCameraSecure = false;
825 bool catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700826 bool hasMetaHostPaymentCategory = false;
827 bool hasMetaOffHostPaymentCategory = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700828
829 // These permissions are required by services implementing services
830 // the system binds to (IME, Accessibility, PrintServices, etc.)
831 bool hasBindDeviceAdminPermission = false;
832 bool hasBindInputMethodPermission = false;
833 bool hasBindAccessibilityServicePermission = false;
834 bool hasBindPrintServicePermission = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700835 bool hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700836 bool hasRequiredSafAttributes = false;
837 bool hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400838 bool hasBindDreamServicePermission = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800839
840 // These two implement the implicit permissions that are granted
841 // to pre-1.6 applications.
842 bool hasWriteExternalStoragePermission = false;
843 bool hasReadPhoneStatePermission = false;
844
845 // If an app requests write storage, they will also get read storage.
846 bool hasReadExternalStoragePermission = false;
847
848 // Implement transition to read and write call log.
849 bool hasReadContactsPermission = false;
850 bool hasWriteContactsPermission = false;
851 bool hasReadCallLogPermission = false;
852 bool hasWriteCallLogPermission = false;
853
Adam Lesinskie47fd122014-08-15 22:25:36 -0700854 // If an app declares itself as multiArch, we report the
855 // native libraries differently.
856 bool hasMultiArch = false;
857
Adam Lesinski282e1812014-01-23 18:17:42 -0800858 // This next group of variables is used to implement a group of
859 // backward-compatibility heuristics necessitated by the addition of
860 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
861 // heuristic is "if an app requests a permission but doesn't explicitly
862 // request the corresponding <uses-feature>, presume it's there anyway".
Adam Lesinski2c72b682014-06-24 09:56:01 -0700863
Adam Lesinski282e1812014-01-23 18:17:42 -0800864 // 2.2 also added some other features that apps can request, but that
865 // have no corresponding permission, so we cannot implement any
866 // back-compatibility heuristic for them. The below are thus unnecessary
867 // (but are retained here for documentary purposes.)
868 //bool specCompassFeature = false;
869 //bool specAccelerometerFeature = false;
870 //bool specProximityFeature = false;
871 //bool specAmbientLightFeature = false;
872 //bool specLiveWallpaperFeature = false;
873
874 int targetSdk = 0;
875 int smallScreen = 1;
876 int normalScreen = 1;
877 int largeScreen = 1;
878 int xlargeScreen = 1;
879 int anyDensity = 1;
880 int requiresSmallestWidthDp = 0;
881 int compatibleWidthLimitDp = 0;
882 int largestWidthLimitDp = 0;
883 String8 pkg;
884 String8 activityName;
885 String8 activityLabel;
886 String8 activityIcon;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800887 String8 activityBanner;
Adam Lesinski282e1812014-01-23 18:17:42 -0800888 String8 receiverName;
889 String8 serviceName;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700890 Vector<String8> supportedInput;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700891
892 FeatureGroup commonFeatures;
893 Vector<FeatureGroup> featureGroups;
894 KeyedVector<String8, ImpliedFeature> impliedFeatures;
895
Adam Lesinski282e1812014-01-23 18:17:42 -0800896 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
897 if (code == ResXMLTree::END_TAG) {
898 depth--;
899 if (depth < 2) {
Michael Wrightec4fdec2013-09-06 16:50:52 -0700900 if (withinSupportsInput && !supportedInput.isEmpty()) {
901 printf("supports-input: '");
902 const size_t N = supportedInput.size();
903 for (size_t i=0; i<N; i++) {
Maurice Chu2675f762013-10-22 17:33:11 -0700904 printf("%s", ResTable::normalizeForOutput(
905 supportedInput[i].string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -0700906 if (i != N - 1) {
907 printf("' '");
908 } else {
909 printf("'\n");
910 }
911 }
912 supportedInput.clear();
913 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800914 withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700915 withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700916 withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800917 } else if (depth < 3) {
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800918 if (withinActivity && isMainActivity) {
Maurice Chu2675f762013-10-22 17:33:11 -0700919 String8 aName(getComponentName(pkg, activityName));
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800920 if (isLauncherActivity) {
921 printf("launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800922 if (aName.length() > 0) {
923 printf(" name='%s' ",
924 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800925 }
926 printf(" label='%s' icon='%s'\n",
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800927 ResTable::normalizeForOutput(activityLabel.string()).string(),
928 ResTable::normalizeForOutput(activityIcon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800929 }
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800930 if (isLeanbackLauncherActivity) {
931 printf("leanback-launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800932 if (aName.length() > 0) {
933 printf(" name='%s' ",
934 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800935 }
936 printf(" label='%s' icon='%s' banner='%s'\n",
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800937 ResTable::normalizeForOutput(activityLabel.string()).string(),
938 ResTable::normalizeForOutput(activityIcon.string()).string(),
939 ResTable::normalizeForOutput(activityBanner.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800940 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800941 }
942 if (!hasIntentFilter) {
943 hasOtherActivities |= withinActivity;
944 hasOtherReceivers |= withinReceiver;
945 hasOtherServices |= withinService;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700946 } else {
947 if (withinService) {
948 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
949 hasBindNfcServicePermission);
950 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
951 hasBindNfcServicePermission);
952 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800953 }
954 withinActivity = false;
955 withinService = false;
956 withinReceiver = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700957 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800958 hasIntentFilter = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800959 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800960 } else if (depth < 4) {
961 if (withinIntentFilter) {
962 if (withinActivity) {
963 hasMainActivity |= actMainActivity;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700964 hasLauncher |= catLauncher;
965 hasCameraActivity |= actCamera;
966 hasCameraSecureActivity |= actCameraSecure;
967 hasOtherActivities |= !actMainActivity && !actCamera && !actCameraSecure;
Adam Lesinski282e1812014-01-23 18:17:42 -0800968 } else if (withinReceiver) {
969 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700970 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
971 hasBindDeviceAdminPermission);
972 hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled);
Adam Lesinski282e1812014-01-23 18:17:42 -0800973 } else if (withinService) {
974 hasImeService |= actImeService;
975 hasWallpaperService |= actWallpaperService;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700976 hasAccessibilityService |= (actAccessibilityService &&
977 hasBindAccessibilityServicePermission);
978 hasPrintService |= (actPrintService && hasBindPrintServicePermission);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700979 hasNotificationListenerService |= actNotificationListenerService &&
980 hasBindNotificationListenerServicePermission;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400981 hasDreamService |= actDreamService && hasBindDreamServicePermission;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700982 hasOtherServices |= (!actImeService && !actWallpaperService &&
Adam Lesinski94fc9122013-09-30 17:16:09 -0700983 !actAccessibilityService && !actPrintService &&
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700984 !actHostApduService && !actOffHostApduService &&
985 !actNotificationListenerService);
986 } else if (withinProvider) {
987 hasDocumentsProvider |= actDocumentsProvider && hasRequiredSafAttributes;
Adam Lesinski282e1812014-01-23 18:17:42 -0800988 }
989 }
990 withinIntentFilter = false;
991 }
992 continue;
993 }
994 if (code != ResXMLTree::START_TAG) {
995 continue;
996 }
997 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700998
999 const char16_t* ctag16 = tree.getElementName(&len);
1000 if (ctag16 == NULL) {
1001 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
1002 goto bail;
1003 }
1004 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -08001005 //printf("Depth %d, %s\n", depth, tag.string());
1006 if (depth == 1) {
1007 if (tag != "manifest") {
1008 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
1009 goto bail;
1010 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001011 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -07001012 printf("package: name='%s' ",
1013 ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001014 int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR,
1015 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001016 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001017 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n",
1018 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001019 goto bail;
1020 }
1021 if (versionCode > 0) {
1022 printf("versionCode='%d' ", versionCode);
1023 } else {
1024 printf("versionCode='' ");
1025 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001026 String8 versionName = AaptXml::getResolvedAttribute(res, tree,
1027 VERSION_NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001028 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001029 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n",
1030 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001031 goto bail;
1032 }
Adam Lesinski25d35a92014-08-11 09:41:56 -07001033 printf("versionName='%s'",
Maurice Chu2675f762013-10-22 17:33:11 -07001034 ResTable::normalizeForOutput(versionName.string()).string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001035
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001036 String8 splitName = AaptXml::getAttribute(tree, NULL, "split");
Adam Lesinski25d35a92014-08-11 09:41:56 -07001037 if (!splitName.isEmpty()) {
1038 printf(" split='%s'", ResTable::normalizeForOutput(
1039 splitName.string()).string());
1040 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001041
Adam Lesinski5283fab2014-08-29 11:23:55 -07001042 String8 platformVersionName = AaptXml::getAttribute(tree, NULL,
1043 "platformBuildVersionName");
1044 printf(" platformBuildVersionName='%s'", platformVersionName.string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001045 printf("\n");
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001046
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001047 int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
1048 INSTALL_LOCATION_ATTR, &error);
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001049 if (error != "") {
1050 fprintf(stderr, "ERROR getting 'android:installLocation' attribute: %s\n",
1051 error.string());
1052 goto bail;
1053 }
1054
1055 if (installLocation >= 0) {
1056 printf("install-location:'");
1057 switch (installLocation) {
1058 case 0:
1059 printf("auto");
1060 break;
1061 case 1:
1062 printf("internalOnly");
1063 break;
1064 case 2:
1065 printf("preferExternal");
1066 break;
1067 default:
1068 fprintf(stderr, "Invalid installLocation %d\n", installLocation);
1069 goto bail;
1070 }
1071 printf("'\n");
1072 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001073 } else if (depth == 2) {
1074 withinApplication = false;
1075 if (tag == "application") {
1076 withinApplication = true;
1077
1078 String8 label;
1079 const size_t NL = locales.size();
1080 for (size_t i=0; i<NL; i++) {
1081 const char* localeStr = locales[i].string();
1082 assets.setLocale(localeStr != NULL ? localeStr : "");
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001083 String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1084 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001085 if (llabel != "") {
1086 if (localeStr == NULL || strlen(localeStr) == 0) {
1087 label = llabel;
Maurice Chu2675f762013-10-22 17:33:11 -07001088 printf("application-label:'%s'\n",
1089 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001090 } else {
1091 if (label == "") {
1092 label = llabel;
1093 }
1094 printf("application-label-%s:'%s'\n", localeStr,
Maurice Chu2675f762013-10-22 17:33:11 -07001095 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001096 }
1097 }
1098 }
1099
1100 ResTable_config tmpConfig = config;
1101 const size_t ND = densities.size();
1102 for (size_t i=0; i<ND; i++) {
1103 tmpConfig.density = densities[i];
1104 assets.setConfiguration(tmpConfig);
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001105 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1106 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001107 if (icon != "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001108 printf("application-icon-%d:'%s'\n", densities[i],
1109 ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001110 }
1111 }
1112 assets.setConfiguration(config);
1113
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001114 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001115 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001116 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1117 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001118 goto bail;
1119 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001120 int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0,
1121 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001122 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001123 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n",
1124 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001125 goto bail;
1126 }
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001127
1128 String8 banner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR, &error);
1129 if (error != "") {
1130 fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
1131 error.string());
1132 goto bail;
1133 }
Maurice Chu2675f762013-10-22 17:33:11 -07001134 printf("application: label='%s' ",
1135 ResTable::normalizeForOutput(label.string()).string());
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001136 printf("icon='%s'", ResTable::normalizeForOutput(icon.string()).string());
1137 if (banner != "") {
1138 printf(" banner='%s'", ResTable::normalizeForOutput(banner.string()).string());
1139 }
1140 printf("\n");
Adam Lesinski282e1812014-01-23 18:17:42 -08001141 if (testOnly != 0) {
1142 printf("testOnly='%d'\n", testOnly);
1143 }
1144
Tim Kilbournd9b1cad2014-10-24 12:43:41 -07001145 int32_t isGame = AaptXml::getResolvedIntegerAttribute(res, tree,
1146 ISGAME_ATTR, 0, &error);
1147 if (error != "") {
1148 fprintf(stderr, "ERROR getting 'android:isGame' attribute: %s\n",
1149 error.string());
1150 goto bail;
1151 }
1152 if (isGame != 0) {
1153 printf("application-isGame\n");
1154 }
1155
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001156 int32_t debuggable = AaptXml::getResolvedIntegerAttribute(res, tree,
1157 DEBUGGABLE_ATTR, 0, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001158 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001159 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n",
1160 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001161 goto bail;
1162 }
1163 if (debuggable != 0) {
1164 printf("application-debuggable\n");
1165 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07001166
1167 // We must search by name because the multiArch flag hasn't been API
1168 // frozen yet.
1169 int32_t multiArchIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
1170 "multiArch");
1171 if (multiArchIndex >= 0) {
1172 Res_value value;
1173 if (tree.getAttributeValue(multiArchIndex, &value) != NO_ERROR) {
1174 if (value.dataType >= Res_value::TYPE_FIRST_INT &&
1175 value.dataType <= Res_value::TYPE_LAST_INT) {
1176 hasMultiArch = value.data;
1177 }
1178 }
1179 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001180 } else if (tag == "uses-sdk") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001181 int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001182 if (error != "") {
1183 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001184 String8 name = AaptXml::getResolvedAttribute(res, tree,
1185 MIN_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001186 if (error != "") {
1187 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
1188 error.string());
1189 goto bail;
1190 }
1191 if (name == "Donut") targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001192 printf("sdkVersion:'%s'\n",
1193 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001194 } else if (code != -1) {
1195 targetSdk = code;
1196 printf("sdkVersion:'%d'\n", code);
1197 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001198 code = AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -08001199 if (code != -1) {
1200 printf("maxSdkVersion:'%d'\n", code);
1201 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001202 code = AaptXml::getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001203 if (error != "") {
1204 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001205 String8 name = AaptXml::getResolvedAttribute(res, tree,
1206 TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001207 if (error != "") {
1208 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
1209 error.string());
1210 goto bail;
1211 }
1212 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001213 printf("targetSdkVersion:'%s'\n",
1214 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001215 } else if (code != -1) {
1216 if (targetSdk < code) {
1217 targetSdk = code;
1218 }
1219 printf("targetSdkVersion:'%d'\n", code);
1220 }
1221 } else if (tag == "uses-configuration") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001222 int32_t reqTouchScreen = AaptXml::getIntegerAttribute(tree,
1223 REQ_TOUCH_SCREEN_ATTR, 0);
1224 int32_t reqKeyboardType = AaptXml::getIntegerAttribute(tree,
1225 REQ_KEYBOARD_TYPE_ATTR, 0);
1226 int32_t reqHardKeyboard = AaptXml::getIntegerAttribute(tree,
1227 REQ_HARD_KEYBOARD_ATTR, 0);
1228 int32_t reqNavigation = AaptXml::getIntegerAttribute(tree,
1229 REQ_NAVIGATION_ATTR, 0);
1230 int32_t reqFiveWayNav = AaptXml::getIntegerAttribute(tree,
1231 REQ_FIVE_WAY_NAV_ATTR, 0);
Adam Lesinski282e1812014-01-23 18:17:42 -08001232 printf("uses-configuration:");
1233 if (reqTouchScreen != 0) {
1234 printf(" reqTouchScreen='%d'", reqTouchScreen);
1235 }
1236 if (reqKeyboardType != 0) {
1237 printf(" reqKeyboardType='%d'", reqKeyboardType);
1238 }
1239 if (reqHardKeyboard != 0) {
1240 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1241 }
1242 if (reqNavigation != 0) {
1243 printf(" reqNavigation='%d'", reqNavigation);
1244 }
1245 if (reqFiveWayNav != 0) {
1246 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1247 }
1248 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001249 } else if (tag == "supports-input") {
1250 withinSupportsInput = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001251 } else if (tag == "supports-screens") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001252 smallScreen = AaptXml::getIntegerAttribute(tree,
1253 SMALL_SCREEN_ATTR, 1);
1254 normalScreen = AaptXml::getIntegerAttribute(tree,
1255 NORMAL_SCREEN_ATTR, 1);
1256 largeScreen = AaptXml::getIntegerAttribute(tree,
1257 LARGE_SCREEN_ATTR, 1);
1258 xlargeScreen = AaptXml::getIntegerAttribute(tree,
1259 XLARGE_SCREEN_ATTR, 1);
1260 anyDensity = AaptXml::getIntegerAttribute(tree,
1261 ANY_DENSITY_ATTR, 1);
1262 requiresSmallestWidthDp = AaptXml::getIntegerAttribute(tree,
1263 REQUIRES_SMALLEST_WIDTH_DP_ATTR, 0);
1264 compatibleWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1265 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, 0);
1266 largestWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1267 LARGEST_WIDTH_LIMIT_DP_ATTR, 0);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001268 } else if (tag == "feature-group") {
1269 withinFeatureGroup = true;
1270 FeatureGroup group;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001271 group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001272 if (error != "") {
1273 fprintf(stderr, "ERROR getting 'android:label' attribute:"
1274 " %s\n", error.string());
1275 goto bail;
1276 }
1277 featureGroups.add(group);
1278
Adam Lesinski282e1812014-01-23 18:17:42 -08001279 } else if (tag == "uses-feature") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001280 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001281 if (name != "" && error == "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001282 int req = AaptXml::getIntegerAttribute(tree,
1283 REQUIRED_ATTR, 1);
Adam Lesinski282e1812014-01-23 18:17:42 -08001284
Adam Lesinski2c72b682014-06-24 09:56:01 -07001285 commonFeatures.features.add(name, req);
1286 if (req) {
1287 addParentFeatures(&commonFeatures, name);
Adam Lesinski282e1812014-01-23 18:17:42 -08001288 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001289 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001290 int vers = AaptXml::getIntegerAttribute(tree,
Adam Lesinski282e1812014-01-23 18:17:42 -08001291 GL_ES_VERSION_ATTR, &error);
1292 if (error == "") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001293 if (vers > commonFeatures.openGLESVersion) {
1294 commonFeatures.openGLESVersion = vers;
1295 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001296 }
1297 }
1298 } else if (tag == "uses-permission") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001299 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001300 if (name != "" && error == "") {
1301 if (name == "android.permission.CAMERA") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001302 addImpliedFeature(&impliedFeatures, "android.hardware.camera",
Adam Lesinski2c72b682014-06-24 09:56:01 -07001303 String8::format("requested %s permission", name.string())
1304 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001305 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001306 addImpliedFeature(&impliedFeatures, "android.hardware.location.gps",
1307 String8::format("requested %s permission", name.string())
1308 .string());
1309 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1310 String8::format("requested %s permission", name.string())
1311 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001312 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001313 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_COARSE_LOCATION") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001317 addImpliedFeature(&impliedFeatures, "android.hardware.location.network",
1318 String8::format("requested %s permission", name.string())
1319 .string());
1320 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1321 String8::format("requested %s permission", name.string())
1322 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001323 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
1324 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001325 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1326 String8::format("requested %s permission", name.string())
1327 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001328 } else if (name == "android.permission.BLUETOOTH" ||
1329 name == "android.permission.BLUETOOTH_ADMIN") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001330 if (targetSdk > 4) {
1331 addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
1332 String8::format("requested %s permission", name.string())
1333 .string());
1334 addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
1335 "targetSdkVersion > 4");
1336 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001337 } else if (name == "android.permission.RECORD_AUDIO") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001338 addImpliedFeature(&impliedFeatures, "android.hardware.microphone",
1339 String8::format("requested %s permission", name.string())
1340 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001341 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1342 name == "android.permission.CHANGE_WIFI_STATE" ||
1343 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001344 addImpliedFeature(&impliedFeatures, "android.hardware.wifi",
1345 String8::format("requested %s permission", name.string())
1346 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001347 } else if (name == "android.permission.CALL_PHONE" ||
1348 name == "android.permission.CALL_PRIVILEGED" ||
1349 name == "android.permission.MODIFY_PHONE_STATE" ||
1350 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1351 name == "android.permission.READ_SMS" ||
1352 name == "android.permission.RECEIVE_SMS" ||
1353 name == "android.permission.RECEIVE_MMS" ||
1354 name == "android.permission.RECEIVE_WAP_PUSH" ||
1355 name == "android.permission.SEND_SMS" ||
1356 name == "android.permission.WRITE_APN_SETTINGS" ||
1357 name == "android.permission.WRITE_SMS") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001358 addImpliedFeature(&impliedFeatures, "android.hardware.telephony",
1359 String8("requested a telephony permission").string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001360 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1361 hasWriteExternalStoragePermission = true;
1362 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1363 hasReadExternalStoragePermission = true;
1364 } else if (name == "android.permission.READ_PHONE_STATE") {
1365 hasReadPhoneStatePermission = true;
1366 } else if (name == "android.permission.READ_CONTACTS") {
1367 hasReadContactsPermission = true;
1368 } else if (name == "android.permission.WRITE_CONTACTS") {
1369 hasWriteContactsPermission = true;
1370 } else if (name == "android.permission.READ_CALL_LOG") {
1371 hasReadCallLogPermission = true;
1372 } else if (name == "android.permission.WRITE_CALL_LOG") {
1373 hasWriteCallLogPermission = true;
1374 }
Adam Lesinski58f1f362013-11-12 12:59:08 -08001375
1376 printUsesPermission(name,
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001377 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
1378 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
Adam Lesinski58f1f362013-11-12 12:59:08 -08001379 } else {
Adam Lesinski282e1812014-01-23 18:17:42 -08001380 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1381 error.string());
1382 goto bail;
1383 }
1384 } else if (tag == "uses-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001385 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001386 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001387 printf("uses-package:'%s'\n",
1388 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001389 } else {
1390 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1391 error.string());
1392 goto bail;
1393 }
1394 } else if (tag == "original-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001395 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001396 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001397 printf("original-package:'%s'\n",
1398 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001399 } else {
1400 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1401 error.string());
1402 goto bail;
1403 }
1404 } else if (tag == "supports-gl-texture") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001405 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001406 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001407 printf("supports-gl-texture:'%s'\n",
1408 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001409 } else {
1410 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1411 error.string());
1412 goto bail;
1413 }
1414 } else if (tag == "compatible-screens") {
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001415 printCompatibleScreens(tree, &error);
1416 if (error != "") {
1417 fprintf(stderr, "ERROR getting compatible screens: %s\n",
1418 error.string());
1419 goto bail;
1420 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001421 depth--;
1422 } else if (tag == "package-verifier") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001423 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001424 if (name != "" && error == "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001425 String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001426 if (publicKey != "" && error == "") {
1427 printf("package-verifier: name='%s' publicKey='%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001428 ResTable::normalizeForOutput(name.string()).string(),
1429 ResTable::normalizeForOutput(publicKey.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001430 }
1431 }
1432 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001433 } else if (depth == 3) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001434 withinActivity = false;
1435 withinReceiver = false;
1436 withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001437 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001438 hasIntentFilter = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001439 hasMetaHostPaymentCategory = false;
1440 hasMetaOffHostPaymentCategory = false;
1441 hasBindDeviceAdminPermission = false;
1442 hasBindInputMethodPermission = false;
1443 hasBindAccessibilityServicePermission = false;
1444 hasBindPrintServicePermission = false;
1445 hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001446 hasRequiredSafAttributes = false;
1447 hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001448 hasBindDreamServicePermission = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001449 if (withinApplication) {
1450 if(tag == "activity") {
1451 withinActivity = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001452 activityName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001453 if (error != "") {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001454 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1455 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001456 goto bail;
1457 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001458
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001459 activityLabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1460 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001461 if (error != "") {
1462 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
1463 error.string());
1464 goto bail;
1465 }
1466
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001467 activityIcon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1468 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001469 if (error != "") {
1470 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1471 error.string());
1472 goto bail;
1473 }
1474
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001475 activityBanner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1476 &error);
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001477 if (error != "") {
1478 fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
1479 error.string());
1480 goto bail;
1481 }
1482
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001483 int32_t orien = AaptXml::getResolvedIntegerAttribute(res, tree,
Michael Wrightec4fdec2013-09-06 16:50:52 -07001484 SCREEN_ORIENTATION_ATTR, &error);
1485 if (error == "") {
1486 if (orien == 0 || orien == 6 || orien == 8) {
1487 // Requests landscape, sensorLandscape, or reverseLandscape.
Adam Lesinski2c72b682014-06-24 09:56:01 -07001488 addImpliedFeature(&impliedFeatures, "android.hardware.screen.landscape",
1489 "one or more activities have specified a landscape orientation");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001490 } else if (orien == 1 || orien == 7 || orien == 9) {
1491 // Requests portrait, sensorPortrait, or reversePortrait.
Adam Lesinski2c72b682014-06-24 09:56:01 -07001492 addImpliedFeature(&impliedFeatures, "android.hardware.screen.portrait",
1493 "one or more activities have specified a portrait orientation");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001494 }
1495 }
1496 } else if (tag == "uses-library") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001497 String8 libraryName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001498 if (error != "") {
1499 fprintf(stderr,
1500 "ERROR getting 'android:name' attribute for uses-library"
1501 " %s\n", error.string());
1502 goto bail;
1503 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001504 int req = AaptXml::getIntegerAttribute(tree,
1505 REQUIRED_ATTR, 1);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001506 printf("uses-library%s:'%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001507 req ? "" : "-not-required", ResTable::normalizeForOutput(
1508 libraryName.string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001509 } else if (tag == "receiver") {
1510 withinReceiver = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001511 receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001512
1513 if (error != "") {
1514 fprintf(stderr,
1515 "ERROR getting 'android:name' attribute for receiver:"
1516 " %s\n", error.string());
1517 goto bail;
1518 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001519
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001520 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1521 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001522 if (error == "") {
1523 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1524 hasBindDeviceAdminPermission = true;
1525 }
1526 } else {
1527 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1528 " receiver '%s': %s\n", receiverName.string(), error.string());
1529 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001530 } else if (tag == "service") {
1531 withinService = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001532 serviceName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001533
1534 if (error != "") {
1535 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1536 "service:%s\n", error.string());
1537 goto bail;
1538 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001539
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001540 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1541 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001542 if (error == "") {
1543 if (permission == "android.permission.BIND_INPUT_METHOD") {
1544 hasBindInputMethodPermission = true;
1545 } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") {
1546 hasBindAccessibilityServicePermission = true;
1547 } else if (permission == "android.permission.BIND_PRINT_SERVICE") {
1548 hasBindPrintServicePermission = true;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001549 } else if (permission == "android.permission.BIND_NFC_SERVICE") {
1550 hasBindNfcServicePermission = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001551 } else if (permission == "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
1552 hasBindNotificationListenerServicePermission = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001553 } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
1554 hasBindDreamServicePermission = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001555 }
1556 } else {
1557 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1558 " service '%s': %s\n", serviceName.string(), error.string());
1559 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001560 } else if (tag == "provider") {
1561 withinProvider = true;
1562
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001563 bool exported = AaptXml::getResolvedIntegerAttribute(res, tree,
1564 EXPORTED_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001565 if (error != "") {
1566 fprintf(stderr, "ERROR getting 'android:exported' attribute for provider:"
1567 " %s\n", error.string());
1568 goto bail;
1569 }
1570
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001571 bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute(
1572 res, tree, GRANT_URI_PERMISSIONS_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001573 if (error != "") {
1574 fprintf(stderr, "ERROR getting 'android:grantUriPermissions' attribute for provider:"
1575 " %s\n", error.string());
1576 goto bail;
1577 }
1578
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001579 String8 permission = AaptXml::getResolvedAttribute(res, tree,
1580 PERMISSION_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001581 if (error != "") {
1582 fprintf(stderr, "ERROR getting 'android:permission' attribute for provider:"
1583 " %s\n", error.string());
1584 goto bail;
1585 }
1586
1587 hasRequiredSafAttributes |= exported && grantUriPermissions &&
1588 permission == "android.permission.MANAGE_DOCUMENTS";
1589
Michael Wrightec4fdec2013-09-06 16:50:52 -07001590 } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001591 String8 metaDataName = AaptXml::getResolvedAttribute(res, tree,
1592 NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001593 if (error != "") {
1594 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1595 "meta-data:%s\n", error.string());
1596 goto bail;
1597 }
Maurice Chu2675f762013-10-22 17:33:11 -07001598 printf("meta-data: name='%s' ",
1599 ResTable::normalizeForOutput(metaDataName.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001600 printResolvedResourceAttribute(res, tree, VALUE_ATTR, String8("value"),
Maurice Chu76327312013-10-16 18:28:46 -07001601 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001602 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001603 // Try looking for a RESOURCE_ATTR
1604 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001605 printResolvedResourceAttribute(res, tree, RESOURCE_ATTR,
Maurice Chu76327312013-10-16 18:28:46 -07001606 String8("resource"), &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001607 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001608 fprintf(stderr, "ERROR getting 'android:value' or "
1609 "'android:resource' attribute for "
1610 "meta-data:%s\n", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001611 goto bail;
1612 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001613 }
Maurice Chu76327312013-10-16 18:28:46 -07001614 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001615 } else if (withinSupportsInput && tag == "input-type") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001616 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001617 if (name != "" && error == "") {
1618 supportedInput.add(name);
1619 } else {
1620 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1621 error.string());
1622 goto bail;
1623 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001624 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001625 } else if (withinFeatureGroup && tag == "uses-feature") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001626 FeatureGroup& top = featureGroups.editTop();
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001627
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001628 String8 name = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001629 if (name != "" && error == "") {
Adam Lesinskid3edfde2014-08-08 17:32:44 -07001630 top.features.add(name, true);
1631 addParentFeatures(&top, name);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001632 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001633 int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR,
1634 &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001635 if (error == "") {
1636 if (vers > top.openGLESVersion) {
1637 top.openGLESVersion = vers;
1638 }
1639 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001640 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001641 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001642 } else if (depth == 4) {
1643 if (tag == "intent-filter") {
1644 hasIntentFilter = true;
1645 withinIntentFilter = true;
1646 actMainActivity = false;
1647 actWidgetReceivers = false;
1648 actImeService = false;
1649 actWallpaperService = false;
1650 actAccessibilityService = false;
1651 actPrintService = false;
1652 actDeviceAdminEnabled = false;
1653 actHostApduService = false;
1654 actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001655 actDocumentsProvider = false;
1656 actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001657 actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001658 actCamera = false;
1659 actCameraSecure = false;
1660 catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001661 } else if (withinService && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001662 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001663 if (error != "") {
1664 fprintf(stderr, "ERROR getting 'android:name' attribute for"
1665 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1666 goto bail;
1667 }
1668
1669 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1670 name == "android.nfc.cardemulation.off_host_apdu_service") {
1671 bool offHost = true;
1672 if (name == "android.nfc.cardemulation.host_apdu_service") {
1673 offHost = false;
1674 }
1675
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001676 String8 xmlPath = AaptXml::getResolvedAttribute(res, tree,
1677 RESOURCE_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001678 if (error != "") {
1679 fprintf(stderr, "ERROR getting 'android:resource' attribute for"
1680 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1681 goto bail;
1682 }
1683
1684 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1685 offHost, &error);
1686 if (error != "") {
1687 fprintf(stderr, "ERROR getting AID category for service '%s'\n",
1688 serviceName.string());
1689 goto bail;
1690 }
1691
1692 const size_t catLen = categories.size();
1693 for (size_t i = 0; i < catLen; i++) {
1694 bool paymentCategory = (categories[i] == "payment");
1695 if (offHost) {
1696 hasMetaOffHostPaymentCategory |= paymentCategory;
1697 } else {
1698 hasMetaHostPaymentCategory |= paymentCategory;
1699 }
1700 }
1701 }
1702 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001703 } else if ((depth == 5) && withinIntentFilter) {
1704 String8 action;
1705 if (tag == "action") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001706 action = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001707 if (error != "") {
1708 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1709 error.string());
1710 goto bail;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001711 }
1712
Adam Lesinskia5018c92013-09-30 16:23:15 -07001713 if (withinActivity) {
1714 if (action == "android.intent.action.MAIN") {
1715 isMainActivity = true;
1716 actMainActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001717 } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
1718 action == "android.media.action.VIDEO_CAMERA") {
1719 actCamera = true;
1720 } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
1721 actCameraSecure = true;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001722 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001723 } else if (withinReceiver) {
1724 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1725 actWidgetReceivers = true;
1726 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1727 actDeviceAdminEnabled = true;
1728 }
1729 } else if (withinService) {
1730 if (action == "android.view.InputMethod") {
1731 actImeService = true;
1732 } else if (action == "android.service.wallpaper.WallpaperService") {
1733 actWallpaperService = true;
1734 } else if (action == "android.accessibilityservice.AccessibilityService") {
1735 actAccessibilityService = true;
1736 } else if (action == "android.printservice.PrintService") {
1737 actPrintService = true;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001738 } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
1739 actHostApduService = true;
1740 } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
1741 actOffHostApduService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001742 } else if (action == "android.service.notification.NotificationListenerService") {
1743 actNotificationListenerService = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001744 } else if (action == "android.service.dreams.DreamService") {
1745 actDreamService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001746 }
1747 } else if (withinProvider) {
1748 if (action == "android.content.action.DOCUMENTS_PROVIDER") {
1749 actDocumentsProvider = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001750 }
1751 }
1752 if (action == "android.intent.action.SEARCH") {
1753 isSearchable = true;
1754 }
1755 }
1756
1757 if (tag == "category") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001758 String8 category = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001759 if (error != "") {
1760 fprintf(stderr, "ERROR getting 'name' attribute: %s\n",
1761 error.string());
1762 goto bail;
1763 }
1764 if (withinActivity) {
1765 if (category == "android.intent.category.LAUNCHER") {
1766 isLauncherActivity = true;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001767 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
1768 isLeanbackLauncherActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001769 } else if (category == "android.intent.category.HOME") {
1770 catLauncher = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001771 }
1772 }
1773 }
1774 }
1775 }
1776
1777 // Pre-1.6 implicitly granted permission compatibility logic
1778 if (targetSdk < 4) {
1779 if (!hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001780 printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
1781 printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
1782 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001783 hasWriteExternalStoragePermission = true;
1784 }
1785 if (!hasReadPhoneStatePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001786 printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
1787 printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
1788 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001789 }
1790 }
1791
1792 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1793 // force them to always take READ_EXTERNAL_STORAGE as well. We always
1794 // do this (regardless of target API version) because we can't have
1795 // an app with write permission but not read permission.
1796 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001797 printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"));
1798 printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
1799 String8("requested WRITE_EXTERNAL_STORAGE"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001800 }
1801
1802 // Pre-JellyBean call log permission compatibility.
1803 if (targetSdk < 16) {
1804 if (!hasReadCallLogPermission && hasReadContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001805 printUsesPermission(String8("android.permission.READ_CALL_LOG"));
1806 printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
1807 String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001808 }
1809 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001810 printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
1811 printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
1812 String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001813 }
1814 }
1815
Adam Lesinski2c72b682014-06-24 09:56:01 -07001816 addImpliedFeature(&impliedFeatures, "android.hardware.touchscreen",
1817 "default feature for all apps");
1818
1819 const size_t numFeatureGroups = featureGroups.size();
1820 if (numFeatureGroups == 0) {
1821 // If no <feature-group> tags were defined, apply auto-implied features.
1822 printFeatureGroup(commonFeatures, &impliedFeatures);
1823
1824 } else {
1825 // <feature-group> tags are defined, so we ignore implied features and
1826 for (size_t i = 0; i < numFeatureGroups; i++) {
1827 FeatureGroup& grp = featureGroups.editItemAt(i);
1828
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001829 if (commonFeatures.openGLESVersion > grp.openGLESVersion) {
1830 grp.openGLESVersion = commonFeatures.openGLESVersion;
1831 }
1832
Adam Lesinski2c72b682014-06-24 09:56:01 -07001833 // Merge the features defined in the top level (not inside a <feature-group>)
1834 // with this feature group.
1835 const size_t numCommonFeatures = commonFeatures.features.size();
1836 for (size_t j = 0; j < numCommonFeatures; j++) {
1837 if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001838 grp.features.add(commonFeatures.features.keyAt(j),
1839 commonFeatures.features[j]);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001840 }
1841 }
1842
Adam Lesinski73a05112014-12-08 12:53:17 -08001843 if (!grp.features.isEmpty()) {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001844 printFeatureGroup(grp);
Adam Lesinski282e1812014-01-23 18:17:42 -08001845 }
1846 }
1847 }
1848
Adam Lesinski282e1812014-01-23 18:17:42 -08001849
Adam Lesinski282e1812014-01-23 18:17:42 -08001850 if (hasWidgetReceivers) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001851 printComponentPresence("app-widget");
Adam Lesinski282e1812014-01-23 18:17:42 -08001852 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001853 if (hasDeviceAdminReceiver) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001854 printComponentPresence("device-admin");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001855 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001856 if (hasImeService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001857 printComponentPresence("ime");
Adam Lesinski282e1812014-01-23 18:17:42 -08001858 }
1859 if (hasWallpaperService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001860 printComponentPresence("wallpaper");
Adam Lesinski282e1812014-01-23 18:17:42 -08001861 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001862 if (hasAccessibilityService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001863 printComponentPresence("accessibility");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001864 }
1865 if (hasPrintService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001866 printComponentPresence("print-service");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001867 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001868 if (hasPaymentService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001869 printComponentPresence("payment");
1870 }
1871 if (isSearchable) {
1872 printComponentPresence("search");
1873 }
1874 if (hasDocumentsProvider) {
1875 printComponentPresence("document-provider");
1876 }
1877 if (hasLauncher) {
1878 printComponentPresence("launcher");
1879 }
1880 if (hasNotificationListenerService) {
1881 printComponentPresence("notification-listener");
1882 }
John Spurlockeb8d1be2014-06-25 17:46:15 -04001883 if (hasDreamService) {
1884 printComponentPresence("dream");
1885 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001886 if (hasCameraActivity) {
1887 printComponentPresence("camera");
1888 }
1889 if (hasCameraSecureActivity) {
1890 printComponentPresence("camera-secure");
1891 }
1892
1893 if (hasMainActivity) {
1894 printf("main\n");
Adam Lesinski94fc9122013-09-30 17:16:09 -07001895 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001896 if (hasOtherActivities) {
1897 printf("other-activities\n");
1898 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001899 if (hasOtherReceivers) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001900 printf("other-receivers\n");
1901 }
1902 if (hasOtherServices) {
1903 printf("other-services\n");
1904 }
1905
1906 // For modern apps, if screen size buckets haven't been specified
1907 // but the new width ranges have, then infer the buckets from them.
1908 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1909 && requiresSmallestWidthDp > 0) {
1910 int compatWidth = compatibleWidthLimitDp;
1911 if (compatWidth <= 0) {
1912 compatWidth = requiresSmallestWidthDp;
1913 }
1914 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1915 smallScreen = -1;
1916 } else {
1917 smallScreen = 0;
1918 }
1919 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1920 normalScreen = -1;
1921 } else {
1922 normalScreen = 0;
1923 }
1924 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1925 largeScreen = -1;
1926 } else {
1927 largeScreen = 0;
1928 }
1929 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1930 xlargeScreen = -1;
1931 } else {
1932 xlargeScreen = 0;
1933 }
1934 }
1935
1936 // Determine default values for any unspecified screen sizes,
1937 // based on the target SDK of the package. As of 4 (donut)
1938 // the screen size support was introduced, so all default to
1939 // enabled.
1940 if (smallScreen > 0) {
1941 smallScreen = targetSdk >= 4 ? -1 : 0;
1942 }
1943 if (normalScreen > 0) {
1944 normalScreen = -1;
1945 }
1946 if (largeScreen > 0) {
1947 largeScreen = targetSdk >= 4 ? -1 : 0;
1948 }
1949 if (xlargeScreen > 0) {
1950 // Introduced in Gingerbread.
1951 xlargeScreen = targetSdk >= 9 ? -1 : 0;
1952 }
1953 if (anyDensity > 0) {
1954 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1955 || compatibleWidthLimitDp > 0) ? -1 : 0;
1956 }
1957 printf("supports-screens:");
1958 if (smallScreen != 0) {
1959 printf(" 'small'");
1960 }
1961 if (normalScreen != 0) {
1962 printf(" 'normal'");
1963 }
1964 if (largeScreen != 0) {
1965 printf(" 'large'");
1966 }
1967 if (xlargeScreen != 0) {
1968 printf(" 'xlarge'");
1969 }
1970 printf("\n");
1971 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
1972 if (requiresSmallestWidthDp > 0) {
1973 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1974 }
1975 if (compatibleWidthLimitDp > 0) {
1976 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1977 }
1978 if (largestWidthLimitDp > 0) {
1979 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1980 }
1981
1982 printf("locales:");
1983 const size_t NL = locales.size();
1984 for (size_t i=0; i<NL; i++) {
1985 const char* localeStr = locales[i].string();
1986 if (localeStr == NULL || strlen(localeStr) == 0) {
1987 localeStr = "--_--";
1988 }
1989 printf(" '%s'", localeStr);
1990 }
1991 printf("\n");
1992
1993 printf("densities:");
1994 const size_t ND = densities.size();
1995 for (size_t i=0; i<ND; i++) {
1996 printf(" '%d'", densities[i]);
1997 }
1998 printf("\n");
1999
2000 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
2001 if (dir != NULL) {
2002 if (dir->getFileCount() > 0) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002003 SortedVector<String8> architectures;
Adam Lesinski282e1812014-01-23 18:17:42 -08002004 for (size_t i=0; i<dir->getFileCount(); i++) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002005 architectures.add(ResTable::normalizeForOutput(
2006 dir->getFileName(i).string()));
Adam Lesinski282e1812014-01-23 18:17:42 -08002007 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07002008
2009 bool outputAltNativeCode = false;
2010 // A multiArch package is one that contains 64-bit and
2011 // 32-bit versions of native code and expects 3rd-party
2012 // apps to load these native code libraries. Since most
2013 // 64-bit systems also support 32-bit apps, the apps
2014 // loading this multiArch package's code may be either
2015 // 32-bit or 64-bit.
2016 if (hasMultiArch) {
2017 // If this is a multiArch package, report the 64-bit
2018 // version only. Then as a separate entry, report the
2019 // rest.
2020 //
2021 // If we report the 32-bit architecture, this APK will
2022 // be installed on a 32-bit device, causing a large waste
2023 // of bandwidth and disk space. This assumes that
2024 // the developer of the multiArch package has also
2025 // made a version that is 32-bit only.
2026 String8 intel64("x86_64");
2027 String8 arm64("arm64-v8a");
2028 ssize_t index = architectures.indexOf(intel64);
2029 if (index < 0) {
2030 index = architectures.indexOf(arm64);
2031 }
2032
2033 if (index >= 0) {
2034 printf("native-code: '%s'\n", architectures[index].string());
2035 architectures.removeAt(index);
2036 outputAltNativeCode = true;
2037 }
2038 }
2039
2040 const size_t archCount = architectures.size();
2041 if (archCount > 0) {
2042 if (outputAltNativeCode) {
2043 printf("alt-");
2044 }
2045 printf("native-code:");
2046 for (size_t i = 0; i < archCount; i++) {
2047 printf(" '%s'", architectures[i].string());
2048 }
2049 printf("\n");
2050 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002051 }
2052 delete dir;
2053 }
2054 } else if (strcmp("badger", option) == 0) {
2055 printf("%s", CONSOLE_DATA);
2056 } else if (strcmp("configurations", option) == 0) {
2057 Vector<ResTable_config> configs;
2058 res.getConfigurations(&configs);
2059 const size_t N = configs.size();
2060 for (size_t i=0; i<N; i++) {
2061 printf("%s\n", configs[i].toString().string());
2062 }
2063 } else {
2064 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2065 goto bail;
2066 }
2067 }
2068
2069 result = NO_ERROR;
2070
2071bail:
2072 if (asset) {
2073 delete asset;
2074 }
2075 return (result != NO_ERROR);
2076}
2077
2078
2079/*
2080 * Handle the "add" command, which wants to add files to a new or
2081 * pre-existing archive.
2082 */
2083int doAdd(Bundle* bundle)
2084{
2085 ZipFile* zip = NULL;
2086 status_t result = UNKNOWN_ERROR;
2087 const char* zipFileName;
2088
2089 if (bundle->getUpdate()) {
2090 /* avoid confusion */
2091 fprintf(stderr, "ERROR: can't use '-u' with add\n");
2092 goto bail;
2093 }
2094
2095 if (bundle->getFileSpecCount() < 1) {
2096 fprintf(stderr, "ERROR: must specify zip file name\n");
2097 goto bail;
2098 }
2099 zipFileName = bundle->getFileSpecEntry(0);
2100
2101 if (bundle->getFileSpecCount() < 2) {
2102 fprintf(stderr, "NOTE: nothing to do\n");
2103 goto bail;
2104 }
2105
2106 zip = openReadWrite(zipFileName, true);
2107 if (zip == NULL) {
2108 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2109 goto bail;
2110 }
2111
2112 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2113 const char* fileName = bundle->getFileSpecEntry(i);
2114
2115 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
2116 printf(" '%s'... (from gzip)\n", fileName);
2117 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
2118 } else {
2119 if (bundle->getJunkPath()) {
2120 String8 storageName = String8(fileName).getPathLeaf();
Maurice Chu2675f762013-10-22 17:33:11 -07002121 printf(" '%s' as '%s'...\n", fileName,
2122 ResTable::normalizeForOutput(storageName.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08002123 result = zip->add(fileName, storageName.string(),
2124 bundle->getCompressionMethod(), NULL);
2125 } else {
2126 printf(" '%s'...\n", fileName);
2127 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2128 }
2129 }
2130 if (result != NO_ERROR) {
2131 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2132 if (result == NAME_NOT_FOUND) {
2133 fprintf(stderr, ": file not found\n");
2134 } else if (result == ALREADY_EXISTS) {
2135 fprintf(stderr, ": already exists in archive\n");
2136 } else {
2137 fprintf(stderr, "\n");
2138 }
2139 goto bail;
2140 }
2141 }
2142
2143 result = NO_ERROR;
2144
2145bail:
2146 delete zip;
2147 return (result != NO_ERROR);
2148}
2149
2150
2151/*
2152 * Delete files from an existing archive.
2153 */
2154int doRemove(Bundle* bundle)
2155{
2156 ZipFile* zip = NULL;
2157 status_t result = UNKNOWN_ERROR;
2158 const char* zipFileName;
2159
2160 if (bundle->getFileSpecCount() < 1) {
2161 fprintf(stderr, "ERROR: must specify zip file name\n");
2162 goto bail;
2163 }
2164 zipFileName = bundle->getFileSpecEntry(0);
2165
2166 if (bundle->getFileSpecCount() < 2) {
2167 fprintf(stderr, "NOTE: nothing to do\n");
2168 goto bail;
2169 }
2170
2171 zip = openReadWrite(zipFileName, false);
2172 if (zip == NULL) {
2173 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2174 zipFileName);
2175 goto bail;
2176 }
2177
2178 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2179 const char* fileName = bundle->getFileSpecEntry(i);
2180 ZipEntry* entry;
2181
2182 entry = zip->getEntryByName(fileName);
2183 if (entry == NULL) {
2184 printf(" '%s' NOT FOUND\n", fileName);
2185 continue;
2186 }
2187
2188 result = zip->remove(entry);
2189
2190 if (result != NO_ERROR) {
2191 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2192 bundle->getFileSpecEntry(i), zipFileName);
2193 goto bail;
2194 }
2195 }
2196
2197 /* update the archive */
2198 zip->flush();
2199
2200bail:
2201 delete zip;
2202 return (result != NO_ERROR);
2203}
2204
Adam Lesinski3921e872014-05-13 10:56:25 -07002205static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002206 const size_t numDirs = dir->getDirs().size();
2207 for (size_t i = 0; i < numDirs; i++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002208 bool ignore = ignoreConfig;
2209 const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2210 const char* dirStr = subDir->getLeaf().string();
2211 if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2212 ignore = true;
2213 }
2214 status_t err = addResourcesToBuilder(subDir, builder, ignore);
Adam Lesinskifab50872014-04-16 14:40:42 -07002215 if (err != NO_ERROR) {
2216 return err;
2217 }
2218 }
2219
2220 const size_t numFiles = dir->getFiles().size();
2221 for (size_t i = 0; i < numFiles; i++) {
2222 sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2223 const size_t numConfigs = gp->getFiles().size();
2224 for (size_t j = 0; j < numConfigs; j++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002225 status_t err = NO_ERROR;
2226 if (ignoreConfig) {
2227 err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2228 } else {
2229 err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2230 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002231 if (err != NO_ERROR) {
2232 fprintf(stderr, "Failed to add %s (%s) to builder.\n",
2233 gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
2234 return err;
2235 }
2236 }
2237 }
2238 return NO_ERROR;
2239}
2240
2241static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2242 if (split->isBase()) {
2243 return original;
2244 }
2245
2246 String8 ext(original.getPathExtension());
2247 if (ext == String8(".apk")) {
2248 return String8::format("%s_%s%s",
2249 original.getBasePath().string(),
2250 split->getDirectorySafeName().string(),
2251 ext.string());
2252 }
2253
2254 return String8::format("%s_%s", original.string(),
2255 split->getDirectorySafeName().string());
2256}
Adam Lesinski282e1812014-01-23 18:17:42 -08002257
2258/*
2259 * Package up an asset directory and associated application files.
2260 */
2261int doPackage(Bundle* bundle)
2262{
2263 const char* outputAPKFile;
2264 int retVal = 1;
2265 status_t err;
2266 sp<AaptAssets> assets;
2267 int N;
2268 FILE* fp;
2269 String8 dependencyFile;
Adam Lesinskifab50872014-04-16 14:40:42 -07002270 sp<ApkBuilder> builder;
Adam Lesinski282e1812014-01-23 18:17:42 -08002271
Anton Krumina2ef5c02014-03-12 14:46:44 -07002272 // -c en_XA or/and ar_XB means do pseudolocalization
Adam Lesinskifab50872014-04-16 14:40:42 -07002273 sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2274 err = configFilter->parse(bundle->getConfigurations());
Adam Lesinski282e1812014-01-23 18:17:42 -08002275 if (err != NO_ERROR) {
2276 goto bail;
2277 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002278 if (configFilter->containsPseudo()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002279 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2280 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002281 if (configFilter->containsPseudoBidi()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002282 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
Adam Lesinski282e1812014-01-23 18:17:42 -08002283 }
2284
2285 N = bundle->getFileSpecCount();
2286 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
Adam Lesinski09384302014-01-22 16:07:42 -08002287 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002288 fprintf(stderr, "ERROR: no input files\n");
2289 goto bail;
2290 }
2291
2292 outputAPKFile = bundle->getOutputAPKFile();
2293
2294 // Make sure the filenames provided exist and are of the appropriate type.
2295 if (outputAPKFile) {
2296 FileType type;
2297 type = getFileType(outputAPKFile);
2298 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2299 fprintf(stderr,
2300 "ERROR: output file '%s' exists but is not regular file\n",
2301 outputAPKFile);
2302 goto bail;
2303 }
2304 }
2305
2306 // Load the assets.
2307 assets = new AaptAssets();
2308
2309 // Set up the resource gathering in assets if we're going to generate
2310 // dependency files. Every time we encounter a resource while slurping
2311 // the tree, we'll add it to these stores so we have full resource paths
2312 // to write to a dependency file.
2313 if (bundle->getGenDependencies()) {
2314 sp<FilePathStore> resPathStore = new FilePathStore;
2315 assets->setFullResPaths(resPathStore);
2316 sp<FilePathStore> assetPathStore = new FilePathStore;
2317 assets->setFullAssetPaths(assetPathStore);
2318 }
2319
2320 err = assets->slurpFromArgs(bundle);
2321 if (err < 0) {
2322 goto bail;
2323 }
2324
2325 if (bundle->getVerbose()) {
2326 assets->print(String8());
2327 }
2328
Adam Lesinskifab50872014-04-16 14:40:42 -07002329 // Create the ApkBuilder, which will collect the compiled files
2330 // to write to the final APK (or sets of APKs if we are building
2331 // a Split APK.
2332 builder = new ApkBuilder(configFilter);
2333
2334 // If we are generating a Split APK, find out which configurations to split on.
2335 if (bundle->getSplitConfigurations().size() > 0) {
2336 const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2337 const size_t numSplits = splitStrs.size();
2338 for (size_t i = 0; i < numSplits; i++) {
2339 std::set<ConfigDescription> configs;
2340 if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2341 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2342 goto bail;
2343 }
2344
2345 err = builder->createSplitForConfigs(configs);
2346 if (err != NO_ERROR) {
2347 goto bail;
2348 }
2349 }
2350 }
2351
Adam Lesinski282e1812014-01-23 18:17:42 -08002352 // If they asked for any fileAs that need to be compiled, do so.
2353 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002354 err = buildResources(bundle, assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002355 if (err != 0) {
2356 goto bail;
2357 }
2358 }
2359
2360 // At this point we've read everything and processed everything. From here
2361 // on out it's just writing output files.
2362 if (SourcePos::hasErrors()) {
2363 goto bail;
2364 }
2365
2366 // Update symbols with information about which ones are needed as Java symbols.
2367 assets->applyJavaSymbols();
2368 if (SourcePos::hasErrors()) {
2369 goto bail;
2370 }
2371
2372 // If we've been asked to generate a dependency file, do that here
2373 if (bundle->getGenDependencies()) {
2374 // If this is the packaging step, generate the dependency file next to
2375 // the output apk (e.g. bin/resources.ap_.d)
2376 if (outputAPKFile) {
2377 dependencyFile = String8(outputAPKFile);
2378 // Add the .d extension to the dependency file.
2379 dependencyFile.append(".d");
2380 } else {
2381 // Else if this is the R.java dependency generation step,
2382 // generate the dependency file in the R.java package subdirectory
2383 // e.g. gen/com/foo/app/R.java.d
2384 dependencyFile = String8(bundle->getRClassDir());
2385 dependencyFile.appendPath("R.java.d");
2386 }
2387 // Make sure we have a clean dependency file to start with
2388 fp = fopen(dependencyFile, "w");
2389 fclose(fp);
2390 }
2391
2392 // Write out R.java constants
2393 if (!assets->havePrivateSymbols()) {
2394 if (bundle->getCustomPackage() == NULL) {
2395 // Write the R.java file into the appropriate class directory
2396 // e.g. gen/com/foo/app/R.java
Adam Lesinski1e4663852014-08-15 14:47:28 -07002397 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002398 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002399 } else {
2400 const String8 customPkg(bundle->getCustomPackage());
Adam Lesinski1e4663852014-08-15 14:47:28 -07002401 err = writeResourceSymbols(bundle, assets, customPkg, true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002402 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002403 }
2404 if (err < 0) {
2405 goto bail;
2406 }
2407 // If we have library files, we're going to write our R.java file into
2408 // the appropriate class directory for those libraries as well.
2409 // e.g. gen/com/foo/app/lib/R.java
2410 if (bundle->getExtraPackages() != NULL) {
2411 // Split on colon
2412 String8 libs(bundle->getExtraPackages());
2413 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2414 while (packageString != NULL) {
2415 // Write the R.java file out with the correct package name
Marcin Kosiba0f3a5a62014-09-11 13:48:48 +01002416 err = writeResourceSymbols(bundle, assets, String8(packageString), true,
Tao Baia6d7e3f2015-09-01 18:49:54 -07002417 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002418 if (err < 0) {
2419 goto bail;
2420 }
2421 packageString = strtok(NULL, ":");
2422 }
2423 libs.unlockBuffer();
2424 }
2425 } else {
Adam Lesinski1e4663852014-08-15 14:47:28 -07002426 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002427 if (err < 0) {
2428 goto bail;
2429 }
Adam Lesinski1e4663852014-08-15 14:47:28 -07002430 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002431 if (err < 0) {
2432 goto bail;
2433 }
2434 }
2435
2436 // Write out the ProGuard file
2437 err = writeProguardFile(bundle, assets);
2438 if (err < 0) {
2439 goto bail;
2440 }
2441
2442 // Write the apk
2443 if (outputAPKFile) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002444 // Gather all resources and add them to the APK Builder. The builder will then
2445 // figure out which Split they belong in.
2446 err = addResourcesToBuilder(assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002447 if (err != NO_ERROR) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002448 goto bail;
2449 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002450
2451 const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2452 const size_t numSplits = splits.size();
2453 for (size_t i = 0; i < numSplits; i++) {
2454 const sp<ApkSplit>& split = splits[i];
2455 String8 outputPath = buildApkName(String8(outputAPKFile), split);
2456 err = writeAPK(bundle, outputPath, split);
2457 if (err != NO_ERROR) {
2458 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2459 goto bail;
2460 }
2461 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002462 }
2463
2464 // If we've been asked to generate a dependency file, we need to finish up here.
2465 // the writeResourceSymbols and writeAPK functions have already written the target
2466 // half of the dependency file, now we need to write the prerequisites. (files that
2467 // the R.java file or .ap_ file depend on)
2468 if (bundle->getGenDependencies()) {
2469 // Now that writeResourceSymbols or writeAPK has taken care of writing
2470 // the targets to our dependency file, we'll write the prereqs
2471 fp = fopen(dependencyFile, "a+");
2472 fprintf(fp, " : ");
2473 bool includeRaw = (outputAPKFile != NULL);
2474 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2475 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2476 // and therefore was not added to our pathstores during slurping
2477 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2478 fclose(fp);
2479 }
2480
2481 retVal = 0;
2482bail:
2483 if (SourcePos::hasErrors()) {
2484 SourcePos::printErrors(stderr);
2485 }
2486 return retVal;
2487}
2488
2489/*
2490 * Do PNG Crunching
2491 * PRECONDITIONS
2492 * -S flag points to a source directory containing drawable* folders
2493 * -C flag points to destination directory. The folder structure in the
2494 * source directory will be mirrored to the destination (cache) directory
2495 *
2496 * POSTCONDITIONS
2497 * Destination directory will be updated to match the PNG files in
Maurice Chu2675f762013-10-22 17:33:11 -07002498 * the source directory.
Adam Lesinski282e1812014-01-23 18:17:42 -08002499 */
2500int doCrunch(Bundle* bundle)
2501{
2502 fprintf(stdout, "Crunching PNG Files in ");
2503 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2504 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2505
2506 updatePreProcessedCache(bundle);
2507
2508 return NO_ERROR;
2509}
2510
2511/*
2512 * Do PNG Crunching on a single flag
2513 * -i points to a single png file
2514 * -o points to a single png output file
2515 */
2516int doSingleCrunch(Bundle* bundle)
2517{
2518 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2519 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2520
2521 String8 input(bundle->getSingleCrunchInputFile());
2522 String8 output(bundle->getSingleCrunchOutputFile());
2523
2524 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2525 // we can't return the status_t as it gets truncate to the lower 8 bits.
2526 return 42;
2527 }
2528
2529 return NO_ERROR;
2530}
2531
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002532int runInDaemonMode(Bundle* bundle) {
2533 std::cout << "Ready" << std::endl;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002534 for (std::string cmd; std::getline(std::cin, cmd);) {
2535 if (cmd == "quit") {
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002536 return NO_ERROR;
Chris Warringtonde3ab0a2015-02-09 21:04:46 -08002537 } else if (cmd == "s") {
2538 // Two argument crunch
2539 std::string inputFile, outputFile;
2540 std::getline(std::cin, inputFile);
2541 std::getline(std::cin, outputFile);
2542 bundle->setSingleCrunchInputFile(inputFile.c_str());
2543 bundle->setSingleCrunchOutputFile(outputFile.c_str());
2544 std::cout << "Crunching " << inputFile << std::endl;
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002545 if (doSingleCrunch(bundle) != NO_ERROR) {
2546 std::cout << "Error" << std::endl;
2547 }
2548 std::cout << "Done" << std::endl;
2549 } else {
2550 // in case of invalid command, just bail out.
2551 std::cerr << "Unknown command" << std::endl;
2552 return -1;
2553 }
2554 }
2555 return -1;
2556}
2557
Adam Lesinski282e1812014-01-23 18:17:42 -08002558char CONSOLE_DATA[2925] = {
2559 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2560 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2561 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2562 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2563 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2564 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2565 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2566 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2567 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2568 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2569 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 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, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2572 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2573 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2574 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2575 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2576 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2577 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2578 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2579 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2580 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2581 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2582 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2583 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2584 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2585 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2586 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2587 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2588 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2589 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2590 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2591 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2592 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2593 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2594 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2595 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2596 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2597 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2598 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2599 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2600 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2601 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2602 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2603 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2604 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2605 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2606 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2607 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2608 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2609 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2610 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2611 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2612 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2613 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2614 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2615 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2616 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2617 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2618 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2619 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2620 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2621 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2622 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2623 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2624 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2625 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2626 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2627 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2628 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2629 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2630 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2631 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2632 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2633 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2634 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2635 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2636 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2637 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2638 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2639 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2640 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2641 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2642 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2643 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2644 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2645 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2646 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2647 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2648 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2649 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2650 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2651 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2652 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2653 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2654 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2655 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2656 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2657 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2658 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2659 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2660 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2661 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2662 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2663 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2664 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2665 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2666 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2667 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2668 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2669 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2670 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2671 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2672 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2673 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2674 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2675 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2676 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2677 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2678 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2679 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2680 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2681 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2682 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2683 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2684 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2685 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2686 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2687 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2688 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2689 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2690 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2691 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2692 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2693 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2694 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2695 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2696 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2697 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2698 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2699 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2700 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2701 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2702 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2703 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2704 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2705 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2706 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2707 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2708 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2709 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2710 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2711 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2712 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2713 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2714 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2715 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2716 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2717 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2718 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2719 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2720 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2721 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2722 };