blob: 70044f29da9555ead6aeb606f672203e17530a38 [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
Andreas Gampeb8dc7bc2014-10-01 19:07:51 -0700218#ifdef HAVE_ANDROID_OS
219 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,
Adam Lesinski282e1812014-01-23 18:17:42 -0800311};
312
Maurice Chu2675f762013-10-22 17:33:11 -0700313String8 getComponentName(String8 &pkgName, String8 &componentName) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800314 ssize_t idx = componentName.find(".");
315 String8 retStr(pkgName);
316 if (idx == 0) {
317 retStr += componentName;
318 } else if (idx < 0) {
319 retStr += ".";
320 retStr += componentName;
321 } else {
Maurice Chu2675f762013-10-22 17:33:11 -0700322 return componentName;
Adam Lesinski282e1812014-01-23 18:17:42 -0800323 }
Maurice Chu2675f762013-10-22 17:33:11 -0700324 return retStr;
Adam Lesinski282e1812014-01-23 18:17:42 -0800325}
326
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700327static void printCompatibleScreens(ResXMLTree& tree, String8* outError) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800328 size_t len;
329 ResXMLTree::event_code_t code;
330 int depth = 0;
331 bool first = true;
332 printf("compatible-screens:");
333 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
334 if (code == ResXMLTree::END_TAG) {
335 depth--;
336 if (depth < 0) {
337 break;
338 }
339 continue;
340 }
341 if (code != ResXMLTree::START_TAG) {
342 continue;
343 }
344 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700345 const char16_t* ctag16 = tree.getElementName(&len);
346 if (ctag16 == NULL) {
347 *outError = "failed to get XML element name (bad string pool)";
348 return;
349 }
350 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800351 if (tag == "screen") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700352 int32_t screenSize = AaptXml::getIntegerAttribute(tree,
353 SCREEN_SIZE_ATTR);
354 int32_t screenDensity = AaptXml::getIntegerAttribute(tree,
355 SCREEN_DENSITY_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -0800356 if (screenSize > 0 && screenDensity > 0) {
357 if (!first) {
358 printf(",");
359 }
360 first = false;
361 printf("'%d/%d'", screenSize, screenDensity);
362 }
363 }
364 }
365 printf("\n");
366}
367
Adam Lesinski58f1f362013-11-12 12:59:08 -0800368static void printUsesPermission(const String8& name, bool optional=false, int maxSdkVersion=-1) {
369 printf("uses-permission: name='%s'", ResTable::normalizeForOutput(name.string()).string());
370 if (maxSdkVersion != -1) {
371 printf(" maxSdkVersion='%d'", maxSdkVersion);
372 }
373 printf("\n");
374
375 if (optional) {
376 printf("optional-permission: name='%s'",
377 ResTable::normalizeForOutput(name.string()).string());
378 if (maxSdkVersion != -1) {
379 printf(" maxSdkVersion='%d'", maxSdkVersion);
380 }
381 printf("\n");
382 }
383}
384
385static void printUsesImpliedPermission(const String8& name, const String8& reason) {
386 printf("uses-implied-permission: name='%s' reason='%s'\n",
387 ResTable::normalizeForOutput(name.string()).string(),
388 ResTable::normalizeForOutput(reason.string()).string());
389}
390
Adam Lesinski94fc9122013-09-30 17:16:09 -0700391Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool offHost,
392 String8 *outError = NULL)
393{
394 Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER);
395 if (aidAsset == NULL) {
396 if (outError != NULL) *outError = "xml resource does not exist";
397 return Vector<String8>();
398 }
399
400 const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service");
401
402 bool withinApduService = false;
403 Vector<String8> categories;
404
405 String8 error;
406 ResXMLTree tree;
407 tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength());
408
409 size_t len;
410 int depth = 0;
411 ResXMLTree::event_code_t code;
412 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
413 if (code == ResXMLTree::END_TAG) {
414 depth--;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700415 const char16_t* ctag16 = tree.getElementName(&len);
416 if (ctag16 == NULL) {
417 *outError = "failed to get XML element name (bad string pool)";
418 return Vector<String8>();
419 }
420 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700421
422 if (depth == 0 && tag == serviceTagName) {
423 withinApduService = false;
424 }
425
426 } else if (code == ResXMLTree::START_TAG) {
427 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700428 const char16_t* ctag16 = tree.getElementName(&len);
429 if (ctag16 == NULL) {
430 *outError = "failed to get XML element name (bad string pool)";
431 return Vector<String8>();
432 }
433 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700434
435 if (depth == 1) {
436 if (tag == serviceTagName) {
437 withinApduService = true;
438 }
439 } else if (depth == 2 && withinApduService) {
440 if (tag == "aid-group") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700441 String8 category = AaptXml::getAttribute(tree, CATEGORY_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700442 if (error != "") {
443 if (outError != NULL) *outError = error;
444 return Vector<String8>();
445 }
446
447 categories.add(category);
448 }
449 }
450 }
451 }
452 aidAsset->close();
453 return categories;
454}
455
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700456static void printComponentPresence(const char* componentName) {
457 printf("provides-component:'%s'\n", componentName);
458}
459
Adam Lesinski2c72b682014-06-24 09:56:01 -0700460/**
461 * Represents a feature that has been automatically added due to
462 * a pre-requisite or some other reason.
463 */
464struct ImpliedFeature {
465 /**
466 * Name of the implied feature.
467 */
468 String8 name;
469
470 /**
471 * List of human-readable reasons for why this feature was implied.
472 */
473 SortedVector<String8> reasons;
474};
475
476/**
477 * Represents a <feature-group> tag in the AndroidManifest.xml
478 */
479struct FeatureGroup {
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700480 FeatureGroup() : openGLESVersion(-1) {}
481
Adam Lesinski2c72b682014-06-24 09:56:01 -0700482 /**
483 * Human readable label
484 */
485 String8 label;
486
487 /**
488 * Explicit features defined in the group
489 */
490 KeyedVector<String8, bool> features;
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700491
492 /**
493 * OpenGL ES version required
494 */
495 int openGLESVersion;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700496};
497
498static void addImpliedFeature(KeyedVector<String8, ImpliedFeature>* impliedFeatures,
499 const char* name, const char* reason) {
500 String8 name8(name);
501 ssize_t idx = impliedFeatures->indexOfKey(name8);
502 if (idx < 0) {
503 idx = impliedFeatures->add(name8, ImpliedFeature());
504 impliedFeatures->editValueAt(idx).name = name8;
505 }
506 impliedFeatures->editValueAt(idx).reasons.add(String8(reason));
507}
508
509static void printFeatureGroup(const FeatureGroup& grp,
510 const KeyedVector<String8, ImpliedFeature>* impliedFeatures = NULL) {
511 printf("feature-group: label='%s'\n", grp.label.string());
512
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700513 if (grp.openGLESVersion > 0) {
514 printf(" uses-gl-es: '0x%x'\n", grp.openGLESVersion);
515 }
516
Adam Lesinski2c72b682014-06-24 09:56:01 -0700517 const size_t numFeatures = grp.features.size();
518 for (size_t i = 0; i < numFeatures; i++) {
519 if (!grp.features[i]) {
520 continue;
521 }
522
523 const String8& featureName = grp.features.keyAt(i);
524 printf(" uses-feature: name='%s'\n",
525 ResTable::normalizeForOutput(featureName.string()).string());
526 }
527
528 const size_t numImpliedFeatures =
529 (impliedFeatures != NULL) ? impliedFeatures->size() : 0;
530 for (size_t i = 0; i < numImpliedFeatures; i++) {
531 const ImpliedFeature& impliedFeature = impliedFeatures->valueAt(i);
532 if (grp.features.indexOfKey(impliedFeature.name) >= 0) {
533 // The feature is explicitly set, no need to use implied
534 // definition.
535 continue;
536 }
537
538 String8 printableFeatureName(ResTable::normalizeForOutput(
539 impliedFeature.name.string()));
540 printf(" uses-feature: name='%s'\n", printableFeatureName.string());
541 printf(" uses-implied-feature: name='%s' reason='",
542 printableFeatureName.string());
543 const size_t numReasons = impliedFeature.reasons.size();
544 for (size_t j = 0; j < numReasons; j++) {
545 printf("%s", impliedFeature.reasons[j].string());
546 if (j + 2 < numReasons) {
547 printf(", ");
548 } else if (j + 1 < numReasons) {
549 printf(", and ");
550 }
551 }
552 printf("'\n");
553 }
554}
555
556static void addParentFeatures(FeatureGroup* grp, const String8& name) {
557 if (name == "android.hardware.camera.autofocus" ||
558 name == "android.hardware.camera.flash") {
559 grp->features.add(String8("android.hardware.camera"), true);
560 } else if (name == "android.hardware.location.gps" ||
561 name == "android.hardware.location.network") {
562 grp->features.add(String8("android.hardware.location"), true);
563 } else if (name == "android.hardware.touchscreen.multitouch") {
564 grp->features.add(String8("android.hardware.touchscreen"), true);
565 } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
566 grp->features.add(String8("android.hardware.touchscreen.multitouch"), true);
567 grp->features.add(String8("android.hardware.touchscreen"), true);
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700568 } else if (name == "android.hardware.opengles.aep") {
569 const int openGLESVersion31 = 0x00030001;
570 if (openGLESVersion31 > grp->openGLESVersion) {
571 grp->openGLESVersion = openGLESVersion31;
572 }
Adam Lesinski2c72b682014-06-24 09:56:01 -0700573 }
574}
575
Adam Lesinski282e1812014-01-23 18:17:42 -0800576/*
577 * Handle the "dump" command, to extract select data from an archive.
578 */
579extern char CONSOLE_DATA[2925]; // see EOF
580int doDump(Bundle* bundle)
581{
582 status_t result = UNKNOWN_ERROR;
Adam Lesinski282e1812014-01-23 18:17:42 -0800583
584 if (bundle->getFileSpecCount() < 1) {
585 fprintf(stderr, "ERROR: no dump option specified\n");
586 return 1;
587 }
588
589 if (bundle->getFileSpecCount() < 2) {
590 fprintf(stderr, "ERROR: no dump file specified\n");
591 return 1;
592 }
593
594 const char* option = bundle->getFileSpecEntry(0);
595 const char* filename = bundle->getFileSpecEntry(1);
596
597 AssetManager assets;
Narayan Kamathf85e41f2014-01-24 14:14:30 +0000598 int32_t assetsCookie;
Adam Lesinski282e1812014-01-23 18:17:42 -0800599 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
600 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
601 return 1;
602 }
603
604 // Make a dummy config for retrieving resources... we need to supply
605 // non-default values for some configs so that we can retrieve resources
606 // in the app that don't have a default. The most important of these is
607 // the API version because key resources like icons will have an implicit
608 // version if they are using newer config types like density.
609 ResTable_config config;
Narayan Kamath91447d82014-01-21 15:32:36 +0000610 memset(&config, 0, sizeof(ResTable_config));
Adam Lesinski282e1812014-01-23 18:17:42 -0800611 config.language[0] = 'e';
612 config.language[1] = 'n';
613 config.country[0] = 'U';
614 config.country[1] = 'S';
615 config.orientation = ResTable_config::ORIENTATION_PORT;
616 config.density = ResTable_config::DENSITY_MEDIUM;
617 config.sdkVersion = 10000; // Very high.
618 config.screenWidthDp = 320;
619 config.screenHeightDp = 480;
620 config.smallestScreenWidthDp = 320;
Adam Lesinskic2dea8d2014-08-04 16:40:41 -0700621 config.screenLayout |= ResTable_config::SCREENSIZE_NORMAL;
Adam Lesinski282e1812014-01-23 18:17:42 -0800622 assets.setConfiguration(config);
623
624 const ResTable& res = assets.getResources(false);
Dan Albert68001652014-09-09 09:51:01 -0700625 if (res.getError() != NO_ERROR) {
Adam Lesinski25e9d552014-05-19 15:01:43 -0700626 fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
Adam Lesinski63e646e2014-07-30 11:40:39 -0700627 return 1;
Adam Lesinski282e1812014-01-23 18:17:42 -0800628 }
629
Adam Lesinski2cb761e2014-08-15 13:59:02 -0700630 // The dynamicRefTable can be null if there are no resources for this asset cookie.
631 // This fine.
Adam Lesinski63e646e2014-07-30 11:40:39 -0700632 const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700633
634 Asset* asset = NULL;
635
Adam Lesinski282e1812014-01-23 18:17:42 -0800636 if (strcmp("resources", option) == 0) {
637#ifndef HAVE_ANDROID_OS
638 res.print(bundle->getValues());
639#endif
640
641 } else if (strcmp("strings", option) == 0) {
642 const ResStringPool* pool = res.getTableStringBlock(0);
643 printStringPool(pool);
644
645 } else if (strcmp("xmltree", option) == 0) {
646 if (bundle->getFileSpecCount() < 3) {
647 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
648 goto bail;
649 }
650
651 for (int i=2; i<bundle->getFileSpecCount(); i++) {
652 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700653 ResXMLTree tree(dynamicRefTable);
654 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800655 if (asset == NULL) {
656 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
657 goto bail;
658 }
659
660 if (tree.setTo(asset->getBuffer(true),
661 asset->getLength()) != NO_ERROR) {
662 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
663 goto bail;
664 }
665 tree.restart();
666 printXMLBlock(&tree);
667 tree.uninit();
668 delete asset;
669 asset = NULL;
670 }
671
672 } else if (strcmp("xmlstrings", option) == 0) {
673 if (bundle->getFileSpecCount() < 3) {
674 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
675 goto bail;
676 }
677
678 for (int i=2; i<bundle->getFileSpecCount(); i++) {
679 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700680 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800681 if (asset == NULL) {
682 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
683 goto bail;
684 }
685
Adam Lesinski63e646e2014-07-30 11:40:39 -0700686 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800687 if (tree.setTo(asset->getBuffer(true),
688 asset->getLength()) != NO_ERROR) {
689 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
690 goto bail;
691 }
692 printStringPool(&tree.getStrings());
693 delete asset;
694 asset = NULL;
695 }
696
697 } else {
Adam Lesinski63e646e2014-07-30 11:40:39 -0700698 asset = assets.openNonAsset(assetsCookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800699 if (asset == NULL) {
700 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
701 goto bail;
702 }
703
Adam Lesinski63e646e2014-07-30 11:40:39 -0700704 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800705 if (tree.setTo(asset->getBuffer(true),
706 asset->getLength()) != NO_ERROR) {
707 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
708 goto bail;
709 }
710 tree.restart();
711
712 if (strcmp("permissions", option) == 0) {
713 size_t len;
714 ResXMLTree::event_code_t code;
715 int depth = 0;
716 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
717 if (code == ResXMLTree::END_TAG) {
718 depth--;
719 continue;
720 }
721 if (code != ResXMLTree::START_TAG) {
722 continue;
723 }
724 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700725 const char16_t* ctag16 = tree.getElementName(&len);
726 if (ctag16 == NULL) {
727 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
728 goto bail;
729 }
730 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800731 //printf("Depth %d tag %s\n", depth, tag.string());
732 if (depth == 1) {
733 if (tag != "manifest") {
734 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
735 goto bail;
736 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700737 String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -0700738 printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800739 } else if (depth == 2 && tag == "permission") {
740 String8 error;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700741 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -0800742 if (error != "") {
743 fprintf(stderr, "ERROR: %s\n", error.string());
744 goto bail;
745 }
Maurice Chu2675f762013-10-22 17:33:11 -0700746 printf("permission: %s\n",
747 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800748 } else if (depth == 2 && tag == "uses-permission") {
749 String8 error;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700750 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -0800751 if (error != "") {
752 fprintf(stderr, "ERROR: %s\n", error.string());
753 goto bail;
754 }
Adam Lesinski58f1f362013-11-12 12:59:08 -0800755 printUsesPermission(name,
Adam Lesinskiad2d07d2014-08-27 16:21:08 -0700756 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
757 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
Adam Lesinski282e1812014-01-23 18:17:42 -0800758 }
759 }
760 } else if (strcmp("badging", option) == 0) {
761 Vector<String8> locales;
762 res.getLocales(&locales);
763
764 Vector<ResTable_config> configs;
765 res.getConfigurations(&configs);
766 SortedVector<int> densities;
767 const size_t NC = configs.size();
768 for (size_t i=0; i<NC; i++) {
769 int dens = configs[i].density;
770 if (dens == 0) {
771 dens = 160;
772 }
773 densities.add(dens);
774 }
775
776 size_t len;
777 ResXMLTree::event_code_t code;
778 int depth = 0;
779 String8 error;
780 bool withinActivity = false;
781 bool isMainActivity = false;
782 bool isLauncherActivity = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800783 bool isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800784 bool isSearchable = false;
785 bool withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700786 bool withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700787 bool withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800788 bool withinReceiver = false;
789 bool withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700790 bool withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800791 bool withinIntentFilter = false;
792 bool hasMainActivity = false;
793 bool hasOtherActivities = false;
794 bool hasOtherReceivers = false;
795 bool hasOtherServices = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700796 bool hasIntentFilter = false;
797
Adam Lesinski282e1812014-01-23 18:17:42 -0800798 bool hasWallpaperService = false;
799 bool hasImeService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700800 bool hasAccessibilityService = false;
801 bool hasPrintService = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800802 bool hasWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700803 bool hasDeviceAdminReceiver = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700804 bool hasPaymentService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700805 bool hasDocumentsProvider = false;
806 bool hasCameraActivity = false;
807 bool hasCameraSecureActivity = false;
808 bool hasLauncher = false;
809 bool hasNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400810 bool hasDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700811
Adam Lesinski282e1812014-01-23 18:17:42 -0800812 bool actMainActivity = false;
813 bool actWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700814 bool actDeviceAdminEnabled = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800815 bool actImeService = false;
816 bool actWallpaperService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700817 bool actAccessibilityService = false;
818 bool actPrintService = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700819 bool actHostApduService = false;
820 bool actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700821 bool actDocumentsProvider = false;
822 bool actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400823 bool actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700824 bool actCamera = false;
825 bool actCameraSecure = false;
826 bool catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700827 bool hasMetaHostPaymentCategory = false;
828 bool hasMetaOffHostPaymentCategory = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700829
830 // These permissions are required by services implementing services
831 // the system binds to (IME, Accessibility, PrintServices, etc.)
832 bool hasBindDeviceAdminPermission = false;
833 bool hasBindInputMethodPermission = false;
834 bool hasBindAccessibilityServicePermission = false;
835 bool hasBindPrintServicePermission = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700836 bool hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700837 bool hasRequiredSafAttributes = false;
838 bool hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400839 bool hasBindDreamServicePermission = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800840
841 // These two implement the implicit permissions that are granted
842 // to pre-1.6 applications.
843 bool hasWriteExternalStoragePermission = false;
844 bool hasReadPhoneStatePermission = false;
845
846 // If an app requests write storage, they will also get read storage.
847 bool hasReadExternalStoragePermission = false;
848
849 // Implement transition to read and write call log.
850 bool hasReadContactsPermission = false;
851 bool hasWriteContactsPermission = false;
852 bool hasReadCallLogPermission = false;
853 bool hasWriteCallLogPermission = false;
854
Adam Lesinskie47fd122014-08-15 22:25:36 -0700855 // If an app declares itself as multiArch, we report the
856 // native libraries differently.
857 bool hasMultiArch = false;
858
Adam Lesinski282e1812014-01-23 18:17:42 -0800859 // This next group of variables is used to implement a group of
860 // backward-compatibility heuristics necessitated by the addition of
861 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
862 // heuristic is "if an app requests a permission but doesn't explicitly
863 // request the corresponding <uses-feature>, presume it's there anyway".
Adam Lesinski2c72b682014-06-24 09:56:01 -0700864
Adam Lesinski282e1812014-01-23 18:17:42 -0800865 // 2.2 also added some other features that apps can request, but that
866 // have no corresponding permission, so we cannot implement any
867 // back-compatibility heuristic for them. The below are thus unnecessary
868 // (but are retained here for documentary purposes.)
869 //bool specCompassFeature = false;
870 //bool specAccelerometerFeature = false;
871 //bool specProximityFeature = false;
872 //bool specAmbientLightFeature = false;
873 //bool specLiveWallpaperFeature = false;
874
875 int targetSdk = 0;
876 int smallScreen = 1;
877 int normalScreen = 1;
878 int largeScreen = 1;
879 int xlargeScreen = 1;
880 int anyDensity = 1;
881 int requiresSmallestWidthDp = 0;
882 int compatibleWidthLimitDp = 0;
883 int largestWidthLimitDp = 0;
884 String8 pkg;
885 String8 activityName;
886 String8 activityLabel;
887 String8 activityIcon;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800888 String8 activityBanner;
Adam Lesinski282e1812014-01-23 18:17:42 -0800889 String8 receiverName;
890 String8 serviceName;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700891 Vector<String8> supportedInput;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700892
893 FeatureGroup commonFeatures;
894 Vector<FeatureGroup> featureGroups;
895 KeyedVector<String8, ImpliedFeature> impliedFeatures;
896
Adam Lesinski282e1812014-01-23 18:17:42 -0800897 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
898 if (code == ResXMLTree::END_TAG) {
899 depth--;
900 if (depth < 2) {
Michael Wrightec4fdec2013-09-06 16:50:52 -0700901 if (withinSupportsInput && !supportedInput.isEmpty()) {
902 printf("supports-input: '");
903 const size_t N = supportedInput.size();
904 for (size_t i=0; i<N; i++) {
Maurice Chu2675f762013-10-22 17:33:11 -0700905 printf("%s", ResTable::normalizeForOutput(
906 supportedInput[i].string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -0700907 if (i != N - 1) {
908 printf("' '");
909 } else {
910 printf("'\n");
911 }
912 }
913 supportedInput.clear();
914 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800915 withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700916 withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700917 withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800918 } else if (depth < 3) {
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800919 if (withinActivity && isMainActivity) {
Maurice Chu2675f762013-10-22 17:33:11 -0700920 String8 aName(getComponentName(pkg, activityName));
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800921 if (isLauncherActivity) {
922 printf("launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800923 if (aName.length() > 0) {
924 printf(" name='%s' ",
925 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800926 }
927 printf(" label='%s' icon='%s'\n",
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800928 ResTable::normalizeForOutput(activityLabel.string()).string(),
929 ResTable::normalizeForOutput(activityIcon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800930 }
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800931 if (isLeanbackLauncherActivity) {
932 printf("leanback-launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800933 if (aName.length() > 0) {
934 printf(" name='%s' ",
935 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800936 }
937 printf(" label='%s' icon='%s' banner='%s'\n",
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800938 ResTable::normalizeForOutput(activityLabel.string()).string(),
939 ResTable::normalizeForOutput(activityIcon.string()).string(),
940 ResTable::normalizeForOutput(activityBanner.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800941 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800942 }
943 if (!hasIntentFilter) {
944 hasOtherActivities |= withinActivity;
945 hasOtherReceivers |= withinReceiver;
946 hasOtherServices |= withinService;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700947 } else {
948 if (withinService) {
949 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
950 hasBindNfcServicePermission);
951 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
952 hasBindNfcServicePermission);
953 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800954 }
955 withinActivity = false;
956 withinService = false;
957 withinReceiver = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700958 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800959 hasIntentFilter = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800960 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800961 } else if (depth < 4) {
962 if (withinIntentFilter) {
963 if (withinActivity) {
964 hasMainActivity |= actMainActivity;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700965 hasLauncher |= catLauncher;
966 hasCameraActivity |= actCamera;
967 hasCameraSecureActivity |= actCameraSecure;
968 hasOtherActivities |= !actMainActivity && !actCamera && !actCameraSecure;
Adam Lesinski282e1812014-01-23 18:17:42 -0800969 } else if (withinReceiver) {
970 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700971 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
972 hasBindDeviceAdminPermission);
973 hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled);
Adam Lesinski282e1812014-01-23 18:17:42 -0800974 } else if (withinService) {
975 hasImeService |= actImeService;
976 hasWallpaperService |= actWallpaperService;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700977 hasAccessibilityService |= (actAccessibilityService &&
978 hasBindAccessibilityServicePermission);
979 hasPrintService |= (actPrintService && hasBindPrintServicePermission);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700980 hasNotificationListenerService |= actNotificationListenerService &&
981 hasBindNotificationListenerServicePermission;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400982 hasDreamService |= actDreamService && hasBindDreamServicePermission;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700983 hasOtherServices |= (!actImeService && !actWallpaperService &&
Adam Lesinski94fc9122013-09-30 17:16:09 -0700984 !actAccessibilityService && !actPrintService &&
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700985 !actHostApduService && !actOffHostApduService &&
986 !actNotificationListenerService);
987 } else if (withinProvider) {
988 hasDocumentsProvider |= actDocumentsProvider && hasRequiredSafAttributes;
Adam Lesinski282e1812014-01-23 18:17:42 -0800989 }
990 }
991 withinIntentFilter = false;
992 }
993 continue;
994 }
995 if (code != ResXMLTree::START_TAG) {
996 continue;
997 }
998 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700999
1000 const char16_t* ctag16 = tree.getElementName(&len);
1001 if (ctag16 == NULL) {
1002 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
1003 goto bail;
1004 }
1005 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -08001006 //printf("Depth %d, %s\n", depth, tag.string());
1007 if (depth == 1) {
1008 if (tag != "manifest") {
1009 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
1010 goto bail;
1011 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001012 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -07001013 printf("package: name='%s' ",
1014 ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001015 int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR,
1016 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001017 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001018 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n",
1019 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001020 goto bail;
1021 }
1022 if (versionCode > 0) {
1023 printf("versionCode='%d' ", versionCode);
1024 } else {
1025 printf("versionCode='' ");
1026 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001027 String8 versionName = AaptXml::getResolvedAttribute(res, tree,
1028 VERSION_NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001029 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001030 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n",
1031 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001032 goto bail;
1033 }
Adam Lesinski25d35a92014-08-11 09:41:56 -07001034 printf("versionName='%s'",
Maurice Chu2675f762013-10-22 17:33:11 -07001035 ResTable::normalizeForOutput(versionName.string()).string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001036
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001037 String8 splitName = AaptXml::getAttribute(tree, NULL, "split");
Adam Lesinski25d35a92014-08-11 09:41:56 -07001038 if (!splitName.isEmpty()) {
1039 printf(" split='%s'", ResTable::normalizeForOutput(
1040 splitName.string()).string());
1041 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001042
Adam Lesinski5283fab2014-08-29 11:23:55 -07001043 String8 platformVersionName = AaptXml::getAttribute(tree, NULL,
1044 "platformBuildVersionName");
1045 printf(" platformBuildVersionName='%s'", platformVersionName.string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001046 printf("\n");
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001047
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001048 int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
1049 INSTALL_LOCATION_ATTR, &error);
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001050 if (error != "") {
1051 fprintf(stderr, "ERROR getting 'android:installLocation' attribute: %s\n",
1052 error.string());
1053 goto bail;
1054 }
1055
1056 if (installLocation >= 0) {
1057 printf("install-location:'");
1058 switch (installLocation) {
1059 case 0:
1060 printf("auto");
1061 break;
1062 case 1:
1063 printf("internalOnly");
1064 break;
1065 case 2:
1066 printf("preferExternal");
1067 break;
1068 default:
1069 fprintf(stderr, "Invalid installLocation %d\n", installLocation);
1070 goto bail;
1071 }
1072 printf("'\n");
1073 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001074 } else if (depth == 2) {
1075 withinApplication = false;
1076 if (tag == "application") {
1077 withinApplication = true;
1078
1079 String8 label;
1080 const size_t NL = locales.size();
1081 for (size_t i=0; i<NL; i++) {
1082 const char* localeStr = locales[i].string();
1083 assets.setLocale(localeStr != NULL ? localeStr : "");
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001084 String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1085 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001086 if (llabel != "") {
1087 if (localeStr == NULL || strlen(localeStr) == 0) {
1088 label = llabel;
Maurice Chu2675f762013-10-22 17:33:11 -07001089 printf("application-label:'%s'\n",
1090 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001091 } else {
1092 if (label == "") {
1093 label = llabel;
1094 }
1095 printf("application-label-%s:'%s'\n", localeStr,
Maurice Chu2675f762013-10-22 17:33:11 -07001096 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001097 }
1098 }
1099 }
1100
1101 ResTable_config tmpConfig = config;
1102 const size_t ND = densities.size();
1103 for (size_t i=0; i<ND; i++) {
1104 tmpConfig.density = densities[i];
1105 assets.setConfiguration(tmpConfig);
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001106 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1107 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001108 if (icon != "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001109 printf("application-icon-%d:'%s'\n", densities[i],
1110 ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001111 }
1112 }
1113 assets.setConfiguration(config);
1114
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001115 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001116 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001117 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1118 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001119 goto bail;
1120 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001121 int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0,
1122 &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001123 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001124 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n",
1125 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001126 goto bail;
1127 }
Maurice Chu2675f762013-10-22 17:33:11 -07001128 printf("application: label='%s' ",
1129 ResTable::normalizeForOutput(label.string()).string());
1130 printf("icon='%s'\n", ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001131 if (testOnly != 0) {
1132 printf("testOnly='%d'\n", testOnly);
1133 }
1134
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001135 int32_t debuggable = AaptXml::getResolvedIntegerAttribute(res, tree,
1136 DEBUGGABLE_ATTR, 0, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001137 if (error != "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001138 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n",
1139 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001140 goto bail;
1141 }
1142 if (debuggable != 0) {
1143 printf("application-debuggable\n");
1144 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07001145
1146 // We must search by name because the multiArch flag hasn't been API
1147 // frozen yet.
1148 int32_t multiArchIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
1149 "multiArch");
1150 if (multiArchIndex >= 0) {
1151 Res_value value;
1152 if (tree.getAttributeValue(multiArchIndex, &value) != NO_ERROR) {
1153 if (value.dataType >= Res_value::TYPE_FIRST_INT &&
1154 value.dataType <= Res_value::TYPE_LAST_INT) {
1155 hasMultiArch = value.data;
1156 }
1157 }
1158 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001159 } else if (tag == "uses-sdk") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001160 int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001161 if (error != "") {
1162 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001163 String8 name = AaptXml::getResolvedAttribute(res, tree,
1164 MIN_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001165 if (error != "") {
1166 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
1167 error.string());
1168 goto bail;
1169 }
1170 if (name == "Donut") targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001171 printf("sdkVersion:'%s'\n",
1172 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001173 } else if (code != -1) {
1174 targetSdk = code;
1175 printf("sdkVersion:'%d'\n", code);
1176 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001177 code = AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR);
Adam Lesinski282e1812014-01-23 18:17:42 -08001178 if (code != -1) {
1179 printf("maxSdkVersion:'%d'\n", code);
1180 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001181 code = AaptXml::getIntegerAttribute(tree, TARGET_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 TARGET_SDK_VERSION_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001186 if (error != "") {
1187 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
1188 error.string());
1189 goto bail;
1190 }
1191 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001192 printf("targetSdkVersion:'%s'\n",
1193 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001194 } else if (code != -1) {
1195 if (targetSdk < code) {
1196 targetSdk = code;
1197 }
1198 printf("targetSdkVersion:'%d'\n", code);
1199 }
1200 } else if (tag == "uses-configuration") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001201 int32_t reqTouchScreen = AaptXml::getIntegerAttribute(tree,
1202 REQ_TOUCH_SCREEN_ATTR, 0);
1203 int32_t reqKeyboardType = AaptXml::getIntegerAttribute(tree,
1204 REQ_KEYBOARD_TYPE_ATTR, 0);
1205 int32_t reqHardKeyboard = AaptXml::getIntegerAttribute(tree,
1206 REQ_HARD_KEYBOARD_ATTR, 0);
1207 int32_t reqNavigation = AaptXml::getIntegerAttribute(tree,
1208 REQ_NAVIGATION_ATTR, 0);
1209 int32_t reqFiveWayNav = AaptXml::getIntegerAttribute(tree,
1210 REQ_FIVE_WAY_NAV_ATTR, 0);
Adam Lesinski282e1812014-01-23 18:17:42 -08001211 printf("uses-configuration:");
1212 if (reqTouchScreen != 0) {
1213 printf(" reqTouchScreen='%d'", reqTouchScreen);
1214 }
1215 if (reqKeyboardType != 0) {
1216 printf(" reqKeyboardType='%d'", reqKeyboardType);
1217 }
1218 if (reqHardKeyboard != 0) {
1219 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1220 }
1221 if (reqNavigation != 0) {
1222 printf(" reqNavigation='%d'", reqNavigation);
1223 }
1224 if (reqFiveWayNav != 0) {
1225 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1226 }
1227 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001228 } else if (tag == "supports-input") {
1229 withinSupportsInput = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001230 } else if (tag == "supports-screens") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001231 smallScreen = AaptXml::getIntegerAttribute(tree,
1232 SMALL_SCREEN_ATTR, 1);
1233 normalScreen = AaptXml::getIntegerAttribute(tree,
1234 NORMAL_SCREEN_ATTR, 1);
1235 largeScreen = AaptXml::getIntegerAttribute(tree,
1236 LARGE_SCREEN_ATTR, 1);
1237 xlargeScreen = AaptXml::getIntegerAttribute(tree,
1238 XLARGE_SCREEN_ATTR, 1);
1239 anyDensity = AaptXml::getIntegerAttribute(tree,
1240 ANY_DENSITY_ATTR, 1);
1241 requiresSmallestWidthDp = AaptXml::getIntegerAttribute(tree,
1242 REQUIRES_SMALLEST_WIDTH_DP_ATTR, 0);
1243 compatibleWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1244 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, 0);
1245 largestWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1246 LARGEST_WIDTH_LIMIT_DP_ATTR, 0);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001247 } else if (tag == "feature-group") {
1248 withinFeatureGroup = true;
1249 FeatureGroup group;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001250 group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001251 if (error != "") {
1252 fprintf(stderr, "ERROR getting 'android:label' attribute:"
1253 " %s\n", error.string());
1254 goto bail;
1255 }
1256 featureGroups.add(group);
1257
Adam Lesinski282e1812014-01-23 18:17:42 -08001258 } else if (tag == "uses-feature") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001259 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001260 if (name != "" && error == "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001261 int req = AaptXml::getIntegerAttribute(tree,
1262 REQUIRED_ATTR, 1);
Adam Lesinski282e1812014-01-23 18:17:42 -08001263
Adam Lesinski2c72b682014-06-24 09:56:01 -07001264 commonFeatures.features.add(name, req);
1265 if (req) {
1266 addParentFeatures(&commonFeatures, name);
Adam Lesinski282e1812014-01-23 18:17:42 -08001267 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001268 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001269 int vers = AaptXml::getIntegerAttribute(tree,
Adam Lesinski282e1812014-01-23 18:17:42 -08001270 GL_ES_VERSION_ATTR, &error);
1271 if (error == "") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001272 if (vers > commonFeatures.openGLESVersion) {
1273 commonFeatures.openGLESVersion = vers;
1274 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001275 }
1276 }
1277 } else if (tag == "uses-permission") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001278 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001279 if (name != "" && error == "") {
1280 if (name == "android.permission.CAMERA") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001281 addImpliedFeature(&impliedFeatures, "android.hardware.camera",
Adam Lesinski2c72b682014-06-24 09:56:01 -07001282 String8::format("requested %s permission", name.string())
1283 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001284 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001285 addImpliedFeature(&impliedFeatures, "android.hardware.location.gps",
1286 String8::format("requested %s permission", name.string())
1287 .string());
1288 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1289 String8::format("requested %s permission", name.string())
1290 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001291 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001292 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1293 String8::format("requested %s permission", name.string())
1294 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001295 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001296 addImpliedFeature(&impliedFeatures, "android.hardware.location.network",
1297 String8::format("requested %s permission", name.string())
1298 .string());
1299 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1300 String8::format("requested %s permission", name.string())
1301 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001302 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
1303 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001304 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1305 String8::format("requested %s permission", name.string())
1306 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001307 } else if (name == "android.permission.BLUETOOTH" ||
1308 name == "android.permission.BLUETOOTH_ADMIN") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001309 if (targetSdk > 4) {
1310 addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
1311 String8::format("requested %s permission", name.string())
1312 .string());
1313 addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
1314 "targetSdkVersion > 4");
1315 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001316 } else if (name == "android.permission.RECORD_AUDIO") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001317 addImpliedFeature(&impliedFeatures, "android.hardware.microphone",
1318 String8::format("requested %s permission", name.string())
1319 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001320 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1321 name == "android.permission.CHANGE_WIFI_STATE" ||
1322 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001323 addImpliedFeature(&impliedFeatures, "android.hardware.wifi",
1324 String8::format("requested %s permission", name.string())
1325 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001326 } else if (name == "android.permission.CALL_PHONE" ||
1327 name == "android.permission.CALL_PRIVILEGED" ||
1328 name == "android.permission.MODIFY_PHONE_STATE" ||
1329 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1330 name == "android.permission.READ_SMS" ||
1331 name == "android.permission.RECEIVE_SMS" ||
1332 name == "android.permission.RECEIVE_MMS" ||
1333 name == "android.permission.RECEIVE_WAP_PUSH" ||
1334 name == "android.permission.SEND_SMS" ||
1335 name == "android.permission.WRITE_APN_SETTINGS" ||
1336 name == "android.permission.WRITE_SMS") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001337 addImpliedFeature(&impliedFeatures, "android.hardware.telephony",
1338 String8("requested a telephony permission").string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001339 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1340 hasWriteExternalStoragePermission = true;
1341 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1342 hasReadExternalStoragePermission = true;
1343 } else if (name == "android.permission.READ_PHONE_STATE") {
1344 hasReadPhoneStatePermission = true;
1345 } else if (name == "android.permission.READ_CONTACTS") {
1346 hasReadContactsPermission = true;
1347 } else if (name == "android.permission.WRITE_CONTACTS") {
1348 hasWriteContactsPermission = true;
1349 } else if (name == "android.permission.READ_CALL_LOG") {
1350 hasReadCallLogPermission = true;
1351 } else if (name == "android.permission.WRITE_CALL_LOG") {
1352 hasWriteCallLogPermission = true;
1353 }
Adam Lesinski58f1f362013-11-12 12:59:08 -08001354
1355 printUsesPermission(name,
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001356 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
1357 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
Adam Lesinski58f1f362013-11-12 12:59:08 -08001358 } else {
Adam Lesinski282e1812014-01-23 18:17:42 -08001359 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1360 error.string());
1361 goto bail;
1362 }
1363 } else if (tag == "uses-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001364 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001365 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001366 printf("uses-package:'%s'\n",
1367 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001368 } else {
1369 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1370 error.string());
1371 goto bail;
1372 }
1373 } else if (tag == "original-package") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001374 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001375 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001376 printf("original-package:'%s'\n",
1377 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001378 } else {
1379 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1380 error.string());
1381 goto bail;
1382 }
1383 } else if (tag == "supports-gl-texture") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001384 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001385 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001386 printf("supports-gl-texture:'%s'\n",
1387 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001388 } else {
1389 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1390 error.string());
1391 goto bail;
1392 }
1393 } else if (tag == "compatible-screens") {
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001394 printCompatibleScreens(tree, &error);
1395 if (error != "") {
1396 fprintf(stderr, "ERROR getting compatible screens: %s\n",
1397 error.string());
1398 goto bail;
1399 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001400 depth--;
1401 } else if (tag == "package-verifier") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001402 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001403 if (name != "" && error == "") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001404 String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001405 if (publicKey != "" && error == "") {
1406 printf("package-verifier: name='%s' publicKey='%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001407 ResTable::normalizeForOutput(name.string()).string(),
1408 ResTable::normalizeForOutput(publicKey.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001409 }
1410 }
1411 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001412 } else if (depth == 3) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001413 withinActivity = false;
1414 withinReceiver = false;
1415 withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001416 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001417 hasIntentFilter = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001418 hasMetaHostPaymentCategory = false;
1419 hasMetaOffHostPaymentCategory = false;
1420 hasBindDeviceAdminPermission = false;
1421 hasBindInputMethodPermission = false;
1422 hasBindAccessibilityServicePermission = false;
1423 hasBindPrintServicePermission = false;
1424 hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001425 hasRequiredSafAttributes = false;
1426 hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001427 hasBindDreamServicePermission = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001428 if (withinApplication) {
1429 if(tag == "activity") {
1430 withinActivity = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001431 activityName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001432 if (error != "") {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001433 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1434 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001435 goto bail;
1436 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001437
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001438 activityLabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1439 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001440 if (error != "") {
1441 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
1442 error.string());
1443 goto bail;
1444 }
1445
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001446 activityIcon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1447 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001448 if (error != "") {
1449 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1450 error.string());
1451 goto bail;
1452 }
1453
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001454 activityBanner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1455 &error);
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001456 if (error != "") {
1457 fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
1458 error.string());
1459 goto bail;
1460 }
1461
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001462 int32_t orien = AaptXml::getResolvedIntegerAttribute(res, tree,
Michael Wrightec4fdec2013-09-06 16:50:52 -07001463 SCREEN_ORIENTATION_ATTR, &error);
1464 if (error == "") {
1465 if (orien == 0 || orien == 6 || orien == 8) {
1466 // Requests landscape, sensorLandscape, or reverseLandscape.
Adam Lesinski2c72b682014-06-24 09:56:01 -07001467 addImpliedFeature(&impliedFeatures, "android.hardware.screen.landscape",
1468 "one or more activities have specified a landscape orientation");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001469 } else if (orien == 1 || orien == 7 || orien == 9) {
1470 // Requests portrait, sensorPortrait, or reversePortrait.
Adam Lesinski2c72b682014-06-24 09:56:01 -07001471 addImpliedFeature(&impliedFeatures, "android.hardware.screen.portrait",
1472 "one or more activities have specified a portrait orientation");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001473 }
1474 }
1475 } else if (tag == "uses-library") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001476 String8 libraryName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001477 if (error != "") {
1478 fprintf(stderr,
1479 "ERROR getting 'android:name' attribute for uses-library"
1480 " %s\n", error.string());
1481 goto bail;
1482 }
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001483 int req = AaptXml::getIntegerAttribute(tree,
1484 REQUIRED_ATTR, 1);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001485 printf("uses-library%s:'%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001486 req ? "" : "-not-required", ResTable::normalizeForOutput(
1487 libraryName.string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001488 } else if (tag == "receiver") {
1489 withinReceiver = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001490 receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001491
1492 if (error != "") {
1493 fprintf(stderr,
1494 "ERROR getting 'android:name' attribute for receiver:"
1495 " %s\n", error.string());
1496 goto bail;
1497 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001498
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001499 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1500 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001501 if (error == "") {
1502 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1503 hasBindDeviceAdminPermission = true;
1504 }
1505 } else {
1506 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1507 " receiver '%s': %s\n", receiverName.string(), error.string());
1508 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001509 } else if (tag == "service") {
1510 withinService = true;
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001511 serviceName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001512
1513 if (error != "") {
1514 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1515 "service:%s\n", error.string());
1516 goto bail;
1517 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001518
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001519 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1520 &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001521 if (error == "") {
1522 if (permission == "android.permission.BIND_INPUT_METHOD") {
1523 hasBindInputMethodPermission = true;
1524 } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") {
1525 hasBindAccessibilityServicePermission = true;
1526 } else if (permission == "android.permission.BIND_PRINT_SERVICE") {
1527 hasBindPrintServicePermission = true;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001528 } else if (permission == "android.permission.BIND_NFC_SERVICE") {
1529 hasBindNfcServicePermission = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001530 } else if (permission == "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
1531 hasBindNotificationListenerServicePermission = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001532 } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
1533 hasBindDreamServicePermission = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001534 }
1535 } else {
1536 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1537 " service '%s': %s\n", serviceName.string(), error.string());
1538 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001539 } else if (tag == "provider") {
1540 withinProvider = true;
1541
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001542 bool exported = AaptXml::getResolvedIntegerAttribute(res, tree,
1543 EXPORTED_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001544 if (error != "") {
1545 fprintf(stderr, "ERROR getting 'android:exported' attribute for provider:"
1546 " %s\n", error.string());
1547 goto bail;
1548 }
1549
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001550 bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute(
1551 res, tree, GRANT_URI_PERMISSIONS_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001552 if (error != "") {
1553 fprintf(stderr, "ERROR getting 'android:grantUriPermissions' attribute for provider:"
1554 " %s\n", error.string());
1555 goto bail;
1556 }
1557
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001558 String8 permission = AaptXml::getResolvedAttribute(res, tree,
1559 PERMISSION_ATTR, &error);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001560 if (error != "") {
1561 fprintf(stderr, "ERROR getting 'android:permission' attribute for provider:"
1562 " %s\n", error.string());
1563 goto bail;
1564 }
1565
1566 hasRequiredSafAttributes |= exported && grantUriPermissions &&
1567 permission == "android.permission.MANAGE_DOCUMENTS";
1568
Michael Wrightec4fdec2013-09-06 16:50:52 -07001569 } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001570 String8 metaDataName = AaptXml::getResolvedAttribute(res, tree,
1571 NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001572 if (error != "") {
1573 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1574 "meta-data:%s\n", error.string());
1575 goto bail;
1576 }
Maurice Chu2675f762013-10-22 17:33:11 -07001577 printf("meta-data: name='%s' ",
1578 ResTable::normalizeForOutput(metaDataName.string()).string());
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001579 printResolvedResourceAttribute(res, tree, VALUE_ATTR, String8("value"),
Maurice Chu76327312013-10-16 18:28:46 -07001580 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001581 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001582 // Try looking for a RESOURCE_ATTR
1583 error = "";
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001584 printResolvedResourceAttribute(res, tree, RESOURCE_ATTR,
Maurice Chu76327312013-10-16 18:28:46 -07001585 String8("resource"), &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001586 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001587 fprintf(stderr, "ERROR getting 'android:value' or "
1588 "'android:resource' attribute for "
1589 "meta-data:%s\n", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001590 goto bail;
1591 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001592 }
Maurice Chu76327312013-10-16 18:28:46 -07001593 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001594 } else if (withinSupportsInput && tag == "input-type") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001595 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001596 if (name != "" && error == "") {
1597 supportedInput.add(name);
1598 } else {
1599 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1600 error.string());
1601 goto bail;
1602 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001603 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001604 } else if (withinFeatureGroup && tag == "uses-feature") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001605 FeatureGroup& top = featureGroups.editTop();
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001606
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001607 String8 name = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001608 if (name != "" && error == "") {
Adam Lesinskid3edfde2014-08-08 17:32:44 -07001609 top.features.add(name, true);
1610 addParentFeatures(&top, name);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001611 } else {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001612 int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR,
1613 &error);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001614 if (error == "") {
1615 if (vers > top.openGLESVersion) {
1616 top.openGLESVersion = vers;
1617 }
1618 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001619 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001620 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001621 } else if (depth == 4) {
1622 if (tag == "intent-filter") {
1623 hasIntentFilter = true;
1624 withinIntentFilter = true;
1625 actMainActivity = false;
1626 actWidgetReceivers = false;
1627 actImeService = false;
1628 actWallpaperService = false;
1629 actAccessibilityService = false;
1630 actPrintService = false;
1631 actDeviceAdminEnabled = false;
1632 actHostApduService = false;
1633 actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001634 actDocumentsProvider = false;
1635 actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001636 actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001637 actCamera = false;
1638 actCameraSecure = false;
1639 catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001640 } else if (withinService && tag == "meta-data") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001641 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001642 if (error != "") {
1643 fprintf(stderr, "ERROR getting 'android:name' attribute for"
1644 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1645 goto bail;
1646 }
1647
1648 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1649 name == "android.nfc.cardemulation.off_host_apdu_service") {
1650 bool offHost = true;
1651 if (name == "android.nfc.cardemulation.host_apdu_service") {
1652 offHost = false;
1653 }
1654
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001655 String8 xmlPath = AaptXml::getResolvedAttribute(res, tree,
1656 RESOURCE_ATTR, &error);
Adam Lesinski94fc9122013-09-30 17:16:09 -07001657 if (error != "") {
1658 fprintf(stderr, "ERROR getting 'android:resource' attribute for"
1659 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1660 goto bail;
1661 }
1662
1663 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1664 offHost, &error);
1665 if (error != "") {
1666 fprintf(stderr, "ERROR getting AID category for service '%s'\n",
1667 serviceName.string());
1668 goto bail;
1669 }
1670
1671 const size_t catLen = categories.size();
1672 for (size_t i = 0; i < catLen; i++) {
1673 bool paymentCategory = (categories[i] == "payment");
1674 if (offHost) {
1675 hasMetaOffHostPaymentCategory |= paymentCategory;
1676 } else {
1677 hasMetaHostPaymentCategory |= paymentCategory;
1678 }
1679 }
1680 }
1681 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001682 } else if ((depth == 5) && withinIntentFilter) {
1683 String8 action;
1684 if (tag == "action") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001685 action = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001686 if (error != "") {
1687 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1688 error.string());
1689 goto bail;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001690 }
1691
Adam Lesinskia5018c92013-09-30 16:23:15 -07001692 if (withinActivity) {
1693 if (action == "android.intent.action.MAIN") {
1694 isMainActivity = true;
1695 actMainActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001696 } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
1697 action == "android.media.action.VIDEO_CAMERA") {
1698 actCamera = true;
1699 } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
1700 actCameraSecure = true;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001701 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001702 } else if (withinReceiver) {
1703 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1704 actWidgetReceivers = true;
1705 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1706 actDeviceAdminEnabled = true;
1707 }
1708 } else if (withinService) {
1709 if (action == "android.view.InputMethod") {
1710 actImeService = true;
1711 } else if (action == "android.service.wallpaper.WallpaperService") {
1712 actWallpaperService = true;
1713 } else if (action == "android.accessibilityservice.AccessibilityService") {
1714 actAccessibilityService = true;
1715 } else if (action == "android.printservice.PrintService") {
1716 actPrintService = true;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001717 } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
1718 actHostApduService = true;
1719 } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
1720 actOffHostApduService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001721 } else if (action == "android.service.notification.NotificationListenerService") {
1722 actNotificationListenerService = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001723 } else if (action == "android.service.dreams.DreamService") {
1724 actDreamService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001725 }
1726 } else if (withinProvider) {
1727 if (action == "android.content.action.DOCUMENTS_PROVIDER") {
1728 actDocumentsProvider = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001729 }
1730 }
1731 if (action == "android.intent.action.SEARCH") {
1732 isSearchable = true;
1733 }
1734 }
1735
1736 if (tag == "category") {
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001737 String8 category = AaptXml::getAttribute(tree, NAME_ATTR, &error);
Adam Lesinskia5018c92013-09-30 16:23:15 -07001738 if (error != "") {
1739 fprintf(stderr, "ERROR getting 'name' attribute: %s\n",
1740 error.string());
1741 goto bail;
1742 }
1743 if (withinActivity) {
1744 if (category == "android.intent.category.LAUNCHER") {
1745 isLauncherActivity = true;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001746 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
1747 isLeanbackLauncherActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001748 } else if (category == "android.intent.category.HOME") {
1749 catLauncher = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001750 }
1751 }
1752 }
1753 }
1754 }
1755
1756 // Pre-1.6 implicitly granted permission compatibility logic
1757 if (targetSdk < 4) {
1758 if (!hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001759 printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
1760 printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
1761 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001762 hasWriteExternalStoragePermission = true;
1763 }
1764 if (!hasReadPhoneStatePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001765 printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
1766 printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
1767 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001768 }
1769 }
1770
1771 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1772 // force them to always take READ_EXTERNAL_STORAGE as well. We always
1773 // do this (regardless of target API version) because we can't have
1774 // an app with write permission but not read permission.
1775 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001776 printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"));
1777 printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
1778 String8("requested WRITE_EXTERNAL_STORAGE"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001779 }
1780
1781 // Pre-JellyBean call log permission compatibility.
1782 if (targetSdk < 16) {
1783 if (!hasReadCallLogPermission && hasReadContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001784 printUsesPermission(String8("android.permission.READ_CALL_LOG"));
1785 printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
1786 String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001787 }
1788 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001789 printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
1790 printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
1791 String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001792 }
1793 }
1794
Adam Lesinski2c72b682014-06-24 09:56:01 -07001795 addImpliedFeature(&impliedFeatures, "android.hardware.touchscreen",
1796 "default feature for all apps");
1797
1798 const size_t numFeatureGroups = featureGroups.size();
1799 if (numFeatureGroups == 0) {
1800 // If no <feature-group> tags were defined, apply auto-implied features.
1801 printFeatureGroup(commonFeatures, &impliedFeatures);
1802
1803 } else {
1804 // <feature-group> tags are defined, so we ignore implied features and
1805 for (size_t i = 0; i < numFeatureGroups; i++) {
1806 FeatureGroup& grp = featureGroups.editItemAt(i);
1807
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001808 if (commonFeatures.openGLESVersion > grp.openGLESVersion) {
1809 grp.openGLESVersion = commonFeatures.openGLESVersion;
1810 }
1811
Adam Lesinski2c72b682014-06-24 09:56:01 -07001812 // Merge the features defined in the top level (not inside a <feature-group>)
1813 // with this feature group.
1814 const size_t numCommonFeatures = commonFeatures.features.size();
1815 for (size_t j = 0; j < numCommonFeatures; j++) {
1816 if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001817 grp.features.add(commonFeatures.features.keyAt(j),
1818 commonFeatures.features[j]);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001819 }
1820 }
1821
1822 if (!grp.features.isEmpty()) {
1823 printFeatureGroup(grp);
Adam Lesinski282e1812014-01-23 18:17:42 -08001824 }
1825 }
1826 }
1827
Adam Lesinski282e1812014-01-23 18:17:42 -08001828
Adam Lesinski282e1812014-01-23 18:17:42 -08001829 if (hasWidgetReceivers) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001830 printComponentPresence("app-widget");
Adam Lesinski282e1812014-01-23 18:17:42 -08001831 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001832 if (hasDeviceAdminReceiver) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001833 printComponentPresence("device-admin");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001834 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001835 if (hasImeService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001836 printComponentPresence("ime");
Adam Lesinski282e1812014-01-23 18:17:42 -08001837 }
1838 if (hasWallpaperService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001839 printComponentPresence("wallpaper");
Adam Lesinski282e1812014-01-23 18:17:42 -08001840 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001841 if (hasAccessibilityService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001842 printComponentPresence("accessibility");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001843 }
1844 if (hasPrintService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001845 printComponentPresence("print-service");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001846 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001847 if (hasPaymentService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001848 printComponentPresence("payment");
1849 }
1850 if (isSearchable) {
1851 printComponentPresence("search");
1852 }
1853 if (hasDocumentsProvider) {
1854 printComponentPresence("document-provider");
1855 }
1856 if (hasLauncher) {
1857 printComponentPresence("launcher");
1858 }
1859 if (hasNotificationListenerService) {
1860 printComponentPresence("notification-listener");
1861 }
John Spurlockeb8d1be2014-06-25 17:46:15 -04001862 if (hasDreamService) {
1863 printComponentPresence("dream");
1864 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001865 if (hasCameraActivity) {
1866 printComponentPresence("camera");
1867 }
1868 if (hasCameraSecureActivity) {
1869 printComponentPresence("camera-secure");
1870 }
1871
1872 if (hasMainActivity) {
1873 printf("main\n");
Adam Lesinski94fc9122013-09-30 17:16:09 -07001874 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001875 if (hasOtherActivities) {
1876 printf("other-activities\n");
1877 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001878 if (hasOtherReceivers) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001879 printf("other-receivers\n");
1880 }
1881 if (hasOtherServices) {
1882 printf("other-services\n");
1883 }
1884
1885 // For modern apps, if screen size buckets haven't been specified
1886 // but the new width ranges have, then infer the buckets from them.
1887 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1888 && requiresSmallestWidthDp > 0) {
1889 int compatWidth = compatibleWidthLimitDp;
1890 if (compatWidth <= 0) {
1891 compatWidth = requiresSmallestWidthDp;
1892 }
1893 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1894 smallScreen = -1;
1895 } else {
1896 smallScreen = 0;
1897 }
1898 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1899 normalScreen = -1;
1900 } else {
1901 normalScreen = 0;
1902 }
1903 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1904 largeScreen = -1;
1905 } else {
1906 largeScreen = 0;
1907 }
1908 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1909 xlargeScreen = -1;
1910 } else {
1911 xlargeScreen = 0;
1912 }
1913 }
1914
1915 // Determine default values for any unspecified screen sizes,
1916 // based on the target SDK of the package. As of 4 (donut)
1917 // the screen size support was introduced, so all default to
1918 // enabled.
1919 if (smallScreen > 0) {
1920 smallScreen = targetSdk >= 4 ? -1 : 0;
1921 }
1922 if (normalScreen > 0) {
1923 normalScreen = -1;
1924 }
1925 if (largeScreen > 0) {
1926 largeScreen = targetSdk >= 4 ? -1 : 0;
1927 }
1928 if (xlargeScreen > 0) {
1929 // Introduced in Gingerbread.
1930 xlargeScreen = targetSdk >= 9 ? -1 : 0;
1931 }
1932 if (anyDensity > 0) {
1933 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1934 || compatibleWidthLimitDp > 0) ? -1 : 0;
1935 }
1936 printf("supports-screens:");
1937 if (smallScreen != 0) {
1938 printf(" 'small'");
1939 }
1940 if (normalScreen != 0) {
1941 printf(" 'normal'");
1942 }
1943 if (largeScreen != 0) {
1944 printf(" 'large'");
1945 }
1946 if (xlargeScreen != 0) {
1947 printf(" 'xlarge'");
1948 }
1949 printf("\n");
1950 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
1951 if (requiresSmallestWidthDp > 0) {
1952 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1953 }
1954 if (compatibleWidthLimitDp > 0) {
1955 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1956 }
1957 if (largestWidthLimitDp > 0) {
1958 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1959 }
1960
1961 printf("locales:");
1962 const size_t NL = locales.size();
1963 for (size_t i=0; i<NL; i++) {
1964 const char* localeStr = locales[i].string();
1965 if (localeStr == NULL || strlen(localeStr) == 0) {
1966 localeStr = "--_--";
1967 }
1968 printf(" '%s'", localeStr);
1969 }
1970 printf("\n");
1971
1972 printf("densities:");
1973 const size_t ND = densities.size();
1974 for (size_t i=0; i<ND; i++) {
1975 printf(" '%d'", densities[i]);
1976 }
1977 printf("\n");
1978
1979 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1980 if (dir != NULL) {
1981 if (dir->getFileCount() > 0) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07001982 SortedVector<String8> architectures;
Adam Lesinski282e1812014-01-23 18:17:42 -08001983 for (size_t i=0; i<dir->getFileCount(); i++) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07001984 architectures.add(ResTable::normalizeForOutput(
1985 dir->getFileName(i).string()));
Adam Lesinski282e1812014-01-23 18:17:42 -08001986 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07001987
1988 bool outputAltNativeCode = false;
1989 // A multiArch package is one that contains 64-bit and
1990 // 32-bit versions of native code and expects 3rd-party
1991 // apps to load these native code libraries. Since most
1992 // 64-bit systems also support 32-bit apps, the apps
1993 // loading this multiArch package's code may be either
1994 // 32-bit or 64-bit.
1995 if (hasMultiArch) {
1996 // If this is a multiArch package, report the 64-bit
1997 // version only. Then as a separate entry, report the
1998 // rest.
1999 //
2000 // If we report the 32-bit architecture, this APK will
2001 // be installed on a 32-bit device, causing a large waste
2002 // of bandwidth and disk space. This assumes that
2003 // the developer of the multiArch package has also
2004 // made a version that is 32-bit only.
2005 String8 intel64("x86_64");
2006 String8 arm64("arm64-v8a");
2007 ssize_t index = architectures.indexOf(intel64);
2008 if (index < 0) {
2009 index = architectures.indexOf(arm64);
2010 }
2011
2012 if (index >= 0) {
2013 printf("native-code: '%s'\n", architectures[index].string());
2014 architectures.removeAt(index);
2015 outputAltNativeCode = true;
2016 }
2017 }
2018
2019 const size_t archCount = architectures.size();
2020 if (archCount > 0) {
2021 if (outputAltNativeCode) {
2022 printf("alt-");
2023 }
2024 printf("native-code:");
2025 for (size_t i = 0; i < archCount; i++) {
2026 printf(" '%s'", architectures[i].string());
2027 }
2028 printf("\n");
2029 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002030 }
2031 delete dir;
2032 }
2033 } else if (strcmp("badger", option) == 0) {
2034 printf("%s", CONSOLE_DATA);
2035 } else if (strcmp("configurations", option) == 0) {
2036 Vector<ResTable_config> configs;
2037 res.getConfigurations(&configs);
2038 const size_t N = configs.size();
2039 for (size_t i=0; i<N; i++) {
2040 printf("%s\n", configs[i].toString().string());
2041 }
2042 } else {
2043 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2044 goto bail;
2045 }
2046 }
2047
2048 result = NO_ERROR;
2049
2050bail:
2051 if (asset) {
2052 delete asset;
2053 }
2054 return (result != NO_ERROR);
2055}
2056
2057
2058/*
2059 * Handle the "add" command, which wants to add files to a new or
2060 * pre-existing archive.
2061 */
2062int doAdd(Bundle* bundle)
2063{
2064 ZipFile* zip = NULL;
2065 status_t result = UNKNOWN_ERROR;
2066 const char* zipFileName;
2067
2068 if (bundle->getUpdate()) {
2069 /* avoid confusion */
2070 fprintf(stderr, "ERROR: can't use '-u' with add\n");
2071 goto bail;
2072 }
2073
2074 if (bundle->getFileSpecCount() < 1) {
2075 fprintf(stderr, "ERROR: must specify zip file name\n");
2076 goto bail;
2077 }
2078 zipFileName = bundle->getFileSpecEntry(0);
2079
2080 if (bundle->getFileSpecCount() < 2) {
2081 fprintf(stderr, "NOTE: nothing to do\n");
2082 goto bail;
2083 }
2084
2085 zip = openReadWrite(zipFileName, true);
2086 if (zip == NULL) {
2087 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2088 goto bail;
2089 }
2090
2091 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2092 const char* fileName = bundle->getFileSpecEntry(i);
2093
2094 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
2095 printf(" '%s'... (from gzip)\n", fileName);
2096 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
2097 } else {
2098 if (bundle->getJunkPath()) {
2099 String8 storageName = String8(fileName).getPathLeaf();
Maurice Chu2675f762013-10-22 17:33:11 -07002100 printf(" '%s' as '%s'...\n", fileName,
2101 ResTable::normalizeForOutput(storageName.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08002102 result = zip->add(fileName, storageName.string(),
2103 bundle->getCompressionMethod(), NULL);
2104 } else {
2105 printf(" '%s'...\n", fileName);
2106 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2107 }
2108 }
2109 if (result != NO_ERROR) {
2110 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2111 if (result == NAME_NOT_FOUND) {
2112 fprintf(stderr, ": file not found\n");
2113 } else if (result == ALREADY_EXISTS) {
2114 fprintf(stderr, ": already exists in archive\n");
2115 } else {
2116 fprintf(stderr, "\n");
2117 }
2118 goto bail;
2119 }
2120 }
2121
2122 result = NO_ERROR;
2123
2124bail:
2125 delete zip;
2126 return (result != NO_ERROR);
2127}
2128
2129
2130/*
2131 * Delete files from an existing archive.
2132 */
2133int doRemove(Bundle* bundle)
2134{
2135 ZipFile* zip = NULL;
2136 status_t result = UNKNOWN_ERROR;
2137 const char* zipFileName;
2138
2139 if (bundle->getFileSpecCount() < 1) {
2140 fprintf(stderr, "ERROR: must specify zip file name\n");
2141 goto bail;
2142 }
2143 zipFileName = bundle->getFileSpecEntry(0);
2144
2145 if (bundle->getFileSpecCount() < 2) {
2146 fprintf(stderr, "NOTE: nothing to do\n");
2147 goto bail;
2148 }
2149
2150 zip = openReadWrite(zipFileName, false);
2151 if (zip == NULL) {
2152 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2153 zipFileName);
2154 goto bail;
2155 }
2156
2157 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2158 const char* fileName = bundle->getFileSpecEntry(i);
2159 ZipEntry* entry;
2160
2161 entry = zip->getEntryByName(fileName);
2162 if (entry == NULL) {
2163 printf(" '%s' NOT FOUND\n", fileName);
2164 continue;
2165 }
2166
2167 result = zip->remove(entry);
2168
2169 if (result != NO_ERROR) {
2170 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2171 bundle->getFileSpecEntry(i), zipFileName);
2172 goto bail;
2173 }
2174 }
2175
2176 /* update the archive */
2177 zip->flush();
2178
2179bail:
2180 delete zip;
2181 return (result != NO_ERROR);
2182}
2183
Adam Lesinski3921e872014-05-13 10:56:25 -07002184static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002185 const size_t numDirs = dir->getDirs().size();
2186 for (size_t i = 0; i < numDirs; i++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002187 bool ignore = ignoreConfig;
2188 const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2189 const char* dirStr = subDir->getLeaf().string();
2190 if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2191 ignore = true;
2192 }
2193 status_t err = addResourcesToBuilder(subDir, builder, ignore);
Adam Lesinskifab50872014-04-16 14:40:42 -07002194 if (err != NO_ERROR) {
2195 return err;
2196 }
2197 }
2198
2199 const size_t numFiles = dir->getFiles().size();
2200 for (size_t i = 0; i < numFiles; i++) {
2201 sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2202 const size_t numConfigs = gp->getFiles().size();
2203 for (size_t j = 0; j < numConfigs; j++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002204 status_t err = NO_ERROR;
2205 if (ignoreConfig) {
2206 err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2207 } else {
2208 err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2209 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002210 if (err != NO_ERROR) {
2211 fprintf(stderr, "Failed to add %s (%s) to builder.\n",
2212 gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
2213 return err;
2214 }
2215 }
2216 }
2217 return NO_ERROR;
2218}
2219
2220static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2221 if (split->isBase()) {
2222 return original;
2223 }
2224
2225 String8 ext(original.getPathExtension());
2226 if (ext == String8(".apk")) {
2227 return String8::format("%s_%s%s",
2228 original.getBasePath().string(),
2229 split->getDirectorySafeName().string(),
2230 ext.string());
2231 }
2232
2233 return String8::format("%s_%s", original.string(),
2234 split->getDirectorySafeName().string());
2235}
Adam Lesinski282e1812014-01-23 18:17:42 -08002236
2237/*
2238 * Package up an asset directory and associated application files.
2239 */
2240int doPackage(Bundle* bundle)
2241{
2242 const char* outputAPKFile;
2243 int retVal = 1;
2244 status_t err;
2245 sp<AaptAssets> assets;
2246 int N;
2247 FILE* fp;
2248 String8 dependencyFile;
Adam Lesinskifab50872014-04-16 14:40:42 -07002249 sp<ApkBuilder> builder;
Adam Lesinski282e1812014-01-23 18:17:42 -08002250
Anton Krumina2ef5c02014-03-12 14:46:44 -07002251 // -c en_XA or/and ar_XB means do pseudolocalization
Adam Lesinskifab50872014-04-16 14:40:42 -07002252 sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2253 err = configFilter->parse(bundle->getConfigurations());
Adam Lesinski282e1812014-01-23 18:17:42 -08002254 if (err != NO_ERROR) {
2255 goto bail;
2256 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002257 if (configFilter->containsPseudo()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002258 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2259 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002260 if (configFilter->containsPseudoBidi()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002261 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
Adam Lesinski282e1812014-01-23 18:17:42 -08002262 }
2263
2264 N = bundle->getFileSpecCount();
2265 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
Adam Lesinski09384302014-01-22 16:07:42 -08002266 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002267 fprintf(stderr, "ERROR: no input files\n");
2268 goto bail;
2269 }
2270
2271 outputAPKFile = bundle->getOutputAPKFile();
2272
2273 // Make sure the filenames provided exist and are of the appropriate type.
2274 if (outputAPKFile) {
2275 FileType type;
2276 type = getFileType(outputAPKFile);
2277 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2278 fprintf(stderr,
2279 "ERROR: output file '%s' exists but is not regular file\n",
2280 outputAPKFile);
2281 goto bail;
2282 }
2283 }
2284
2285 // Load the assets.
2286 assets = new AaptAssets();
2287
2288 // Set up the resource gathering in assets if we're going to generate
2289 // dependency files. Every time we encounter a resource while slurping
2290 // the tree, we'll add it to these stores so we have full resource paths
2291 // to write to a dependency file.
2292 if (bundle->getGenDependencies()) {
2293 sp<FilePathStore> resPathStore = new FilePathStore;
2294 assets->setFullResPaths(resPathStore);
2295 sp<FilePathStore> assetPathStore = new FilePathStore;
2296 assets->setFullAssetPaths(assetPathStore);
2297 }
2298
2299 err = assets->slurpFromArgs(bundle);
2300 if (err < 0) {
2301 goto bail;
2302 }
2303
2304 if (bundle->getVerbose()) {
2305 assets->print(String8());
2306 }
2307
Adam Lesinskifab50872014-04-16 14:40:42 -07002308 // Create the ApkBuilder, which will collect the compiled files
2309 // to write to the final APK (or sets of APKs if we are building
2310 // a Split APK.
2311 builder = new ApkBuilder(configFilter);
2312
2313 // If we are generating a Split APK, find out which configurations to split on.
2314 if (bundle->getSplitConfigurations().size() > 0) {
2315 const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2316 const size_t numSplits = splitStrs.size();
2317 for (size_t i = 0; i < numSplits; i++) {
2318 std::set<ConfigDescription> configs;
2319 if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2320 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2321 goto bail;
2322 }
2323
2324 err = builder->createSplitForConfigs(configs);
2325 if (err != NO_ERROR) {
2326 goto bail;
2327 }
2328 }
2329 }
2330
Adam Lesinski282e1812014-01-23 18:17:42 -08002331 // If they asked for any fileAs that need to be compiled, do so.
2332 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002333 err = buildResources(bundle, assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002334 if (err != 0) {
2335 goto bail;
2336 }
2337 }
2338
2339 // At this point we've read everything and processed everything. From here
2340 // on out it's just writing output files.
2341 if (SourcePos::hasErrors()) {
2342 goto bail;
2343 }
2344
2345 // Update symbols with information about which ones are needed as Java symbols.
2346 assets->applyJavaSymbols();
2347 if (SourcePos::hasErrors()) {
2348 goto bail;
2349 }
2350
2351 // If we've been asked to generate a dependency file, do that here
2352 if (bundle->getGenDependencies()) {
2353 // If this is the packaging step, generate the dependency file next to
2354 // the output apk (e.g. bin/resources.ap_.d)
2355 if (outputAPKFile) {
2356 dependencyFile = String8(outputAPKFile);
2357 // Add the .d extension to the dependency file.
2358 dependencyFile.append(".d");
2359 } else {
2360 // Else if this is the R.java dependency generation step,
2361 // generate the dependency file in the R.java package subdirectory
2362 // e.g. gen/com/foo/app/R.java.d
2363 dependencyFile = String8(bundle->getRClassDir());
2364 dependencyFile.appendPath("R.java.d");
2365 }
2366 // Make sure we have a clean dependency file to start with
2367 fp = fopen(dependencyFile, "w");
2368 fclose(fp);
2369 }
2370
2371 // Write out R.java constants
2372 if (!assets->havePrivateSymbols()) {
2373 if (bundle->getCustomPackage() == NULL) {
2374 // Write the R.java file into the appropriate class directory
2375 // e.g. gen/com/foo/app/R.java
Adam Lesinski1e4663852014-08-15 14:47:28 -07002376 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true,
2377 bundle->getBuildSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002378 } else {
2379 const String8 customPkg(bundle->getCustomPackage());
Adam Lesinski1e4663852014-08-15 14:47:28 -07002380 err = writeResourceSymbols(bundle, assets, customPkg, true,
2381 bundle->getBuildSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002382 }
2383 if (err < 0) {
2384 goto bail;
2385 }
2386 // If we have library files, we're going to write our R.java file into
2387 // the appropriate class directory for those libraries as well.
2388 // e.g. gen/com/foo/app/lib/R.java
2389 if (bundle->getExtraPackages() != NULL) {
2390 // Split on colon
2391 String8 libs(bundle->getExtraPackages());
2392 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2393 while (packageString != NULL) {
2394 // Write the R.java file out with the correct package name
Marcin Kosiba0f3a5a62014-09-11 13:48:48 +01002395 err = writeResourceSymbols(bundle, assets, String8(packageString), true,
2396 bundle->getBuildSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002397 if (err < 0) {
2398 goto bail;
2399 }
2400 packageString = strtok(NULL, ":");
2401 }
2402 libs.unlockBuffer();
2403 }
2404 } else {
Adam Lesinski1e4663852014-08-15 14:47:28 -07002405 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002406 if (err < 0) {
2407 goto bail;
2408 }
Adam Lesinski1e4663852014-08-15 14:47:28 -07002409 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002410 if (err < 0) {
2411 goto bail;
2412 }
2413 }
2414
2415 // Write out the ProGuard file
2416 err = writeProguardFile(bundle, assets);
2417 if (err < 0) {
2418 goto bail;
2419 }
2420
2421 // Write the apk
2422 if (outputAPKFile) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002423 // Gather all resources and add them to the APK Builder. The builder will then
2424 // figure out which Split they belong in.
2425 err = addResourcesToBuilder(assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002426 if (err != NO_ERROR) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002427 goto bail;
2428 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002429
2430 const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2431 const size_t numSplits = splits.size();
2432 for (size_t i = 0; i < numSplits; i++) {
2433 const sp<ApkSplit>& split = splits[i];
2434 String8 outputPath = buildApkName(String8(outputAPKFile), split);
2435 err = writeAPK(bundle, outputPath, split);
2436 if (err != NO_ERROR) {
2437 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2438 goto bail;
2439 }
2440 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002441 }
2442
2443 // If we've been asked to generate a dependency file, we need to finish up here.
2444 // the writeResourceSymbols and writeAPK functions have already written the target
2445 // half of the dependency file, now we need to write the prerequisites. (files that
2446 // the R.java file or .ap_ file depend on)
2447 if (bundle->getGenDependencies()) {
2448 // Now that writeResourceSymbols or writeAPK has taken care of writing
2449 // the targets to our dependency file, we'll write the prereqs
2450 fp = fopen(dependencyFile, "a+");
2451 fprintf(fp, " : ");
2452 bool includeRaw = (outputAPKFile != NULL);
2453 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2454 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2455 // and therefore was not added to our pathstores during slurping
2456 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2457 fclose(fp);
2458 }
2459
2460 retVal = 0;
2461bail:
2462 if (SourcePos::hasErrors()) {
2463 SourcePos::printErrors(stderr);
2464 }
2465 return retVal;
2466}
2467
2468/*
2469 * Do PNG Crunching
2470 * PRECONDITIONS
2471 * -S flag points to a source directory containing drawable* folders
2472 * -C flag points to destination directory. The folder structure in the
2473 * source directory will be mirrored to the destination (cache) directory
2474 *
2475 * POSTCONDITIONS
2476 * Destination directory will be updated to match the PNG files in
Maurice Chu2675f762013-10-22 17:33:11 -07002477 * the source directory.
Adam Lesinski282e1812014-01-23 18:17:42 -08002478 */
2479int doCrunch(Bundle* bundle)
2480{
2481 fprintf(stdout, "Crunching PNG Files in ");
2482 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2483 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2484
2485 updatePreProcessedCache(bundle);
2486
2487 return NO_ERROR;
2488}
2489
2490/*
2491 * Do PNG Crunching on a single flag
2492 * -i points to a single png file
2493 * -o points to a single png output file
2494 */
2495int doSingleCrunch(Bundle* bundle)
2496{
2497 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2498 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2499
2500 String8 input(bundle->getSingleCrunchInputFile());
2501 String8 output(bundle->getSingleCrunchOutputFile());
2502
2503 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2504 // we can't return the status_t as it gets truncate to the lower 8 bits.
2505 return 42;
2506 }
2507
2508 return NO_ERROR;
2509}
2510
Jerome Dochez6f1280c2014-09-26 10:21:21 -07002511int runInDaemonMode(Bundle* bundle) {
2512 std::cout << "Ready" << std::endl;
2513 for (std::string line; std::getline(std::cin, line);) {
2514 if (line == "quit") {
2515 return NO_ERROR;
2516 }
2517 std::stringstream ss;
2518 ss << line;
2519 std::string s;
2520
2521 std::string command, parameterOne, parameterTwo;
2522 std::getline(ss, command, ' ');
2523 std::getline(ss, parameterOne, ' ');
2524 std::getline(ss, parameterTwo, ' ');
2525 if (command[0] == 's') {
2526 bundle->setSingleCrunchInputFile(parameterOne.c_str());
2527 bundle->setSingleCrunchOutputFile(parameterTwo.c_str());
2528 std::cout << "Crunching " << parameterOne << std::endl;
2529 if (doSingleCrunch(bundle) != NO_ERROR) {
2530 std::cout << "Error" << std::endl;
2531 }
2532 std::cout << "Done" << std::endl;
2533 } else {
2534 // in case of invalid command, just bail out.
2535 std::cerr << "Unknown command" << std::endl;
2536 return -1;
2537 }
2538 }
2539 return -1;
2540}
2541
Adam Lesinski282e1812014-01-23 18:17:42 -08002542char CONSOLE_DATA[2925] = {
2543 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2544 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2545 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2546 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2547 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2548 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2549 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2550 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2551 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2552 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2553 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2554 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2555 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2556 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2557 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2558 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2559 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2560 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2561 10, 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, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2563 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2564 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2565 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2566 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2567 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2568 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2569 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2570 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2571 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2572 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2573 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2574 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2575 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2576 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2577 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2578 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2579 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2580 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2581 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2582 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2583 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2584 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2585 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2586 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2587 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2588 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2589 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2590 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2591 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2592 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2593 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2594 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2595 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2596 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2597 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2598 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2599 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2600 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2601 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2602 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2603 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2604 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2605 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2606 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2607 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2608 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2609 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2610 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2611 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2612 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2613 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2614 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2615 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2616 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2617 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2618 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2619 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2620 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2621 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2622 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2623 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2624 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2625 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2626 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2627 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2628 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2629 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2630 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2631 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2632 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2633 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2634 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2635 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2636 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2637 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2638 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2639 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2640 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2641 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2642 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2643 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2644 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2645 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2646 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2647 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2648 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2649 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2650 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2651 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2652 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2653 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2654 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2655 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2656 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2657 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2658 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2659 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2660 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2661 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2662 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2663 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2664 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2665 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2666 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2667 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2668 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2669 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2670 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2671 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2672 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2673 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2674 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2675 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2676 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2677 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2678 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2679 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2680 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2681 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2682 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2683 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2684 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2685 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2686 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2687 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2688 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2689 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2690 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2691 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2692 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2693 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2694 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2695 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2696 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2697 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2698 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2699 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2700 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2701 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2702 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2703 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2704 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2705 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2706 };