blob: 89942de57eaa5876886306550028aa97b87da10c [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001//
2// Copyright 2006 The Android Open Source Project
3//
4// Android Asset Packaging Tool main entry point.
5//
6#include "Main.h"
7#include "Bundle.h"
Dianne Hackborne6b68032011-10-13 16:26:02 -07008#include "ResourceFilter.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08009#include "ResourceTable.h"
10#include "XMLNode.h"
11
Mathias Agopian3b4062e2009-05-31 19:13:00 -070012#include <utils/Log.h>
13#include <utils/threads.h>
14#include <utils/List.h>
15#include <utils/Errors.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080016
17#include <fcntl.h>
18#include <errno.h>
19
20using namespace android;
21
22/*
23 * Show version info. All the cool kids do it.
24 */
25int doVersion(Bundle* bundle)
26{
27 if (bundle->getFileSpecCount() != 0)
28 printf("(ignoring extra arguments)\n");
29 printf("Android Asset Packaging Tool, v0.2\n");
30
31 return 0;
32}
33
34
35/*
36 * Open the file read only. The call fails if the file doesn't exist.
37 *
38 * Returns NULL on failure.
39 */
40ZipFile* openReadOnly(const char* fileName)
41{
42 ZipFile* zip;
43 status_t result;
44
45 zip = new ZipFile;
46 result = zip->open(fileName, ZipFile::kOpenReadOnly);
47 if (result != NO_ERROR) {
48 if (result == NAME_NOT_FOUND)
49 fprintf(stderr, "ERROR: '%s' not found\n", fileName);
50 else if (result == PERMISSION_DENIED)
51 fprintf(stderr, "ERROR: '%s' access denied\n", fileName);
52 else
53 fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n",
54 fileName);
55 delete zip;
56 return NULL;
57 }
58
59 return zip;
60}
61
62/*
63 * Open the file read-write. The file will be created if it doesn't
64 * already exist and "okayToCreate" is set.
65 *
66 * Returns NULL on failure.
67 */
68ZipFile* openReadWrite(const char* fileName, bool okayToCreate)
69{
70 ZipFile* zip = NULL;
71 status_t result;
72 int flags;
73
74 flags = ZipFile::kOpenReadWrite;
75 if (okayToCreate)
76 flags |= ZipFile::kOpenCreate;
77
78 zip = new ZipFile;
79 result = zip->open(fileName, flags);
80 if (result != NO_ERROR) {
81 delete zip;
82 zip = NULL;
83 goto bail;
84 }
85
86bail:
87 return zip;
88}
89
90
91/*
92 * Return a short string describing the compression method.
93 */
94const char* compressionName(int method)
95{
96 if (method == ZipEntry::kCompressStored)
97 return "Stored";
98 else if (method == ZipEntry::kCompressDeflated)
99 return "Deflated";
100 else
101 return "Unknown";
102}
103
104/*
105 * Return the percent reduction in size (0% == no compression).
106 */
107int calcPercent(long uncompressedLen, long compressedLen)
108{
109 if (!uncompressedLen)
110 return 0;
111 else
112 return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5);
113}
114
115/*
116 * Handle the "list" command, which can be a simple file dump or
117 * a verbose listing.
118 *
119 * The verbose listing closely matches the output of the Info-ZIP "unzip"
120 * command.
121 */
122int doList(Bundle* bundle)
123{
124 int result = 1;
125 ZipFile* zip = NULL;
126 const ZipEntry* entry;
127 long totalUncLen, totalCompLen;
128 const char* zipFileName;
129
130 if (bundle->getFileSpecCount() != 1) {
131 fprintf(stderr, "ERROR: specify zip file name (only)\n");
132 goto bail;
133 }
134 zipFileName = bundle->getFileSpecEntry(0);
135
136 zip = openReadOnly(zipFileName);
137 if (zip == NULL)
138 goto bail;
139
140 int count, i;
141
142 if (bundle->getVerbose()) {
143 printf("Archive: %s\n", zipFileName);
144 printf(
Kenny Rootfb2a9462010-08-25 07:36:31 -0700145 " Length Method Size Ratio Offset Date Time CRC-32 Name\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800146 printf(
Kenny Rootfb2a9462010-08-25 07:36:31 -0700147 "-------- ------ ------- ----- ------- ---- ---- ------ ----\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800148 }
149
150 totalUncLen = totalCompLen = 0;
151
152 count = zip->getNumEntries();
153 for (i = 0; i < count; i++) {
154 entry = zip->getEntryByIndex(i);
155 if (bundle->getVerbose()) {
156 char dateBuf[32];
157 time_t when;
158
159 when = entry->getModWhen();
160 strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M",
161 localtime(&when));
162
Kenny Rootfb2a9462010-08-25 07:36:31 -0700163 printf("%8ld %-7.7s %7ld %3d%% %8zd %s %08lx %s\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164 (long) entry->getUncompressedLen(),
165 compressionName(entry->getCompressionMethod()),
166 (long) entry->getCompressedLen(),
167 calcPercent(entry->getUncompressedLen(),
168 entry->getCompressedLen()),
Kenny Rootfb2a9462010-08-25 07:36:31 -0700169 (size_t) entry->getLFHOffset(),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800170 dateBuf,
171 entry->getCRC32(),
172 entry->getFileName());
173 } else {
174 printf("%s\n", entry->getFileName());
175 }
176
177 totalUncLen += entry->getUncompressedLen();
178 totalCompLen += entry->getCompressedLen();
179 }
180
181 if (bundle->getVerbose()) {
182 printf(
183 "-------- ------- --- -------\n");
184 printf("%8ld %7ld %2d%% %d files\n",
185 totalUncLen,
186 totalCompLen,
187 calcPercent(totalUncLen, totalCompLen),
188 zip->getNumEntries());
189 }
190
191 if (bundle->getAndroidList()) {
192 AssetManager assets;
193 if (!assets.addAssetPath(String8(zipFileName), NULL)) {
194 fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n");
195 goto bail;
196 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700197
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800198 const ResTable& res = assets.getResources(false);
199 if (&res == NULL) {
200 printf("\nNo resource table found.\n");
201 } else {
Steve Blockf1ff21a2010-06-14 17:34:04 +0100202#ifndef HAVE_ANDROID_OS
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800203 printf("\nResource table:\n");
Dianne Hackborne17086b2009-06-19 15:13:28 -0700204 res.print(false);
Steve Blockf1ff21a2010-06-14 17:34:04 +0100205#endif
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800206 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700207
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800208 Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
209 Asset::ACCESS_BUFFER);
210 if (manifestAsset == NULL) {
211 printf("\nNo AndroidManifest.xml found.\n");
212 } else {
213 printf("\nAndroid manifest:\n");
214 ResXMLTree tree;
215 tree.setTo(manifestAsset->getBuffer(true),
216 manifestAsset->getLength());
217 printXMLBlock(&tree);
218 }
219 delete manifestAsset;
220 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700221
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800222 result = 0;
223
224bail:
225 delete zip;
226 return result;
227}
228
229static ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes)
230{
231 size_t N = tree.getAttributeCount();
232 for (size_t i=0; i<N; i++) {
233 if (tree.getAttributeNameResID(i) == attrRes) {
234 return (ssize_t)i;
235 }
236 }
237 return -1;
238}
239
Joe Onorato1553c822009-08-30 13:36:22 -0700240String8 getAttribute(const ResXMLTree& tree, const char* ns,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800241 const char* attr, String8* outError)
242{
243 ssize_t idx = tree.indexOfAttribute(ns, attr);
244 if (idx < 0) {
245 return String8();
246 }
247 Res_value value;
248 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
249 if (value.dataType != Res_value::TYPE_STRING) {
250 if (outError != NULL) *outError = "attribute is not a string value";
251 return String8();
252 }
253 }
254 size_t len;
255 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
256 return str ? String8(str, len) : String8();
257}
258
259static String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError)
260{
261 ssize_t idx = indexOfAttribute(tree, attrRes);
262 if (idx < 0) {
263 return String8();
264 }
265 Res_value value;
266 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
267 if (value.dataType != Res_value::TYPE_STRING) {
268 if (outError != NULL) *outError = "attribute is not a string value";
269 return String8();
270 }
271 }
272 size_t len;
273 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
274 return str ? String8(str, len) : String8();
275}
276
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700277static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes,
278 String8* outError, int32_t defValue = -1)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800279{
280 ssize_t idx = indexOfAttribute(tree, attrRes);
281 if (idx < 0) {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700282 return defValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800283 }
284 Res_value value;
285 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700286 if (value.dataType < Res_value::TYPE_FIRST_INT
287 || value.dataType > Res_value::TYPE_LAST_INT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800288 if (outError != NULL) *outError = "attribute is not an integer value";
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700289 return defValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800290 }
291 }
292 return value.data;
293}
294
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -0700295static int32_t getResolvedIntegerAttribute(const ResTable* resTable, const ResXMLTree& tree,
296 uint32_t attrRes, String8* outError, int32_t defValue = -1)
297{
298 ssize_t idx = indexOfAttribute(tree, attrRes);
299 if (idx < 0) {
300 return defValue;
301 }
302 Res_value value;
303 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
304 if (value.dataType == Res_value::TYPE_REFERENCE) {
305 resTable->resolveReference(&value, 0);
306 }
307 if (value.dataType < Res_value::TYPE_FIRST_INT
308 || value.dataType > Res_value::TYPE_LAST_INT) {
309 if (outError != NULL) *outError = "attribute is not an integer value";
310 return defValue;
311 }
312 }
313 return value.data;
314}
315
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800316static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree,
317 uint32_t attrRes, String8* outError)
318{
319 ssize_t idx = indexOfAttribute(tree, attrRes);
320 if (idx < 0) {
321 return String8();
322 }
323 Res_value value;
324 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
325 if (value.dataType == Res_value::TYPE_STRING) {
326 size_t len;
327 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
328 return str ? String8(str, len) : String8();
329 }
330 resTable->resolveReference(&value, 0);
331 if (value.dataType != Res_value::TYPE_STRING) {
332 if (outError != NULL) *outError = "attribute is not a string value";
333 return String8();
334 }
335 }
336 size_t len;
337 const Res_value* value2 = &value;
338 const char16_t* str = const_cast<ResTable*>(resTable)->valueToString(value2, 0, NULL, &len);
339 return str ? String8(str, len) : String8();
340}
341
342// These are attribute resource constants for the platform, as found
343// in android.R.attr
344enum {
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -0700345 LABEL_ATTR = 0x01010001,
346 ICON_ATTR = 0x01010002,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800347 NAME_ATTR = 0x01010003,
348 VERSION_CODE_ATTR = 0x0101021b,
349 VERSION_NAME_ATTR = 0x0101021c,
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -0700350 SCREEN_ORIENTATION_ATTR = 0x0101001e,
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700351 MIN_SDK_VERSION_ATTR = 0x0101020c,
Suchi Amalapurapu75c49842009-08-14 15:13:09 -0700352 MAX_SDK_VERSION_ATTR = 0x01010271,
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700353 REQ_TOUCH_SCREEN_ATTR = 0x01010227,
354 REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
355 REQ_HARD_KEYBOARD_ATTR = 0x01010229,
356 REQ_NAVIGATION_ATTR = 0x0101022a,
357 REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
358 TARGET_SDK_VERSION_ATTR = 0x01010270,
359 TEST_ONLY_ATTR = 0x01010272,
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700360 ANY_DENSITY_ATTR = 0x0101026c,
Dianne Hackborne5276a72009-08-27 16:28:44 -0700361 GL_ES_VERSION_ATTR = 0x01010281,
Dianne Hackborn723738c2009-06-25 19:48:04 -0700362 SMALL_SCREEN_ATTR = 0x01010284,
363 NORMAL_SCREEN_ATTR = 0x01010285,
364 LARGE_SCREEN_ATTR = 0x01010286,
Dianne Hackbornf43489d2010-08-20 12:44:33 -0700365 XLARGE_SCREEN_ATTR = 0x010102bf,
Dianne Hackborne5276a72009-08-27 16:28:44 -0700366 REQUIRED_ATTR = 0x0101028e,
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700367 SCREEN_SIZE_ATTR = 0x010102ca,
368 SCREEN_DENSITY_ATTR = 0x010102cb,
Dianne Hackborne289bff2011-06-13 19:33:22 -0700369 REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
370 COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
371 LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
Kenny Root56088a52011-09-29 13:49:45 -0700372 PUBLIC_KEY_ATTR = 0x010103a6,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800373};
374
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700375const char *getComponentName(String8 &pkgName, String8 &componentName) {
376 ssize_t idx = componentName.find(".");
377 String8 retStr(pkgName);
378 if (idx == 0) {
379 retStr += componentName;
380 } else if (idx < 0) {
381 retStr += ".";
382 retStr += componentName;
383 } else {
384 return componentName.string();
385 }
386 return retStr.string();
387}
388
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700389static void printCompatibleScreens(ResXMLTree& tree) {
390 size_t len;
391 ResXMLTree::event_code_t code;
392 int depth = 0;
393 bool first = true;
394 printf("compatible-screens:");
395 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
396 if (code == ResXMLTree::END_TAG) {
397 depth--;
398 if (depth < 0) {
399 break;
400 }
401 continue;
402 }
403 if (code != ResXMLTree::START_TAG) {
404 continue;
405 }
406 depth++;
407 String8 tag(tree.getElementName(&len));
408 if (tag == "screen") {
409 int32_t screenSize = getIntegerAttribute(tree,
410 SCREEN_SIZE_ATTR, NULL, -1);
411 int32_t screenDensity = getIntegerAttribute(tree,
412 SCREEN_DENSITY_ATTR, NULL, -1);
413 if (screenSize > 0 && screenDensity > 0) {
414 if (!first) {
415 printf(",");
416 }
417 first = false;
418 printf("'%d/%d'", screenSize, screenDensity);
419 }
420 }
421 }
422 printf("\n");
423}
424
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800425/*
426 * Handle the "dump" command, to extract select data from an archive.
427 */
Dan Morrille74763e2012-01-06 10:47:10 -0800428extern char CONSOLE_DATA[2925]; // see EOF
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800429int doDump(Bundle* bundle)
430{
431 status_t result = UNKNOWN_ERROR;
432 Asset* asset = NULL;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700433
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800434 if (bundle->getFileSpecCount() < 1) {
435 fprintf(stderr, "ERROR: no dump option specified\n");
436 return 1;
437 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700438
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800439 if (bundle->getFileSpecCount() < 2) {
440 fprintf(stderr, "ERROR: no dump file specified\n");
441 return 1;
442 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700443
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800444 const char* option = bundle->getFileSpecEntry(0);
445 const char* filename = bundle->getFileSpecEntry(1);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700446
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800447 AssetManager assets;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700448 void* assetsCookie;
449 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800450 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
451 return 1;
452 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700453
Dianne Hackborne289bff2011-06-13 19:33:22 -0700454 // Make a dummy config for retrieving resources... we need to supply
455 // non-default values for some configs so that we can retrieve resources
456 // in the app that don't have a default. The most important of these is
457 // the API version because key resources like icons will have an implicit
458 // version if they are using newer config types like density.
459 ResTable_config config;
460 config.language[0] = 'e';
461 config.language[1] = 'n';
462 config.country[0] = 'U';
463 config.country[1] = 'S';
464 config.orientation = ResTable_config::ORIENTATION_PORT;
465 config.density = ResTable_config::DENSITY_MEDIUM;
466 config.sdkVersion = 10000; // Very high.
467 config.screenWidthDp = 320;
468 config.screenHeightDp = 480;
469 config.smallestScreenWidthDp = 320;
470 assets.setConfiguration(config);
471
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800472 const ResTable& res = assets.getResources(false);
473 if (&res == NULL) {
474 fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
475 goto bail;
476 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700477
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800478 if (strcmp("resources", option) == 0) {
Steve Blockf1ff21a2010-06-14 17:34:04 +0100479#ifndef HAVE_ANDROID_OS
Dianne Hackborne17086b2009-06-19 15:13:28 -0700480 res.print(bundle->getValues());
Steve Blockf1ff21a2010-06-14 17:34:04 +0100481#endif
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800482 } else if (strcmp("xmltree", option) == 0) {
483 if (bundle->getFileSpecCount() < 3) {
484 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
485 goto bail;
486 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700487
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800488 for (int i=2; i<bundle->getFileSpecCount(); i++) {
489 const char* resname = bundle->getFileSpecEntry(i);
490 ResXMLTree tree;
491 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
492 if (asset == NULL) {
Kenny Root44b283d2009-09-01 19:03:11 -0500493 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800494 goto bail;
495 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700496
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800497 if (tree.setTo(asset->getBuffer(true),
498 asset->getLength()) != NO_ERROR) {
499 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
500 goto bail;
501 }
502 tree.restart();
503 printXMLBlock(&tree);
Kenny Root19138462009-12-04 09:38:48 -0800504 tree.uninit();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800505 delete asset;
506 asset = NULL;
507 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700508
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800509 } else if (strcmp("xmlstrings", option) == 0) {
510 if (bundle->getFileSpecCount() < 3) {
511 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
512 goto bail;
513 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700514
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800515 for (int i=2; i<bundle->getFileSpecCount(); i++) {
516 const char* resname = bundle->getFileSpecEntry(i);
517 ResXMLTree tree;
518 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
519 if (asset == NULL) {
Kenny Root44b283d2009-09-01 19:03:11 -0500520 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800521 goto bail;
522 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700523
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800524 if (tree.setTo(asset->getBuffer(true),
525 asset->getLength()) != NO_ERROR) {
526 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
527 goto bail;
528 }
529 printStringPool(&tree.getStrings());
530 delete asset;
531 asset = NULL;
532 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700533
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800534 } else {
535 ResXMLTree tree;
536 asset = assets.openNonAsset("AndroidManifest.xml",
537 Asset::ACCESS_BUFFER);
538 if (asset == NULL) {
539 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
540 goto bail;
541 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700542
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800543 if (tree.setTo(asset->getBuffer(true),
544 asset->getLength()) != NO_ERROR) {
545 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
546 goto bail;
547 }
548 tree.restart();
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700549
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800550 if (strcmp("permissions", option) == 0) {
551 size_t len;
552 ResXMLTree::event_code_t code;
553 int depth = 0;
554 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
555 if (code == ResXMLTree::END_TAG) {
556 depth--;
557 continue;
558 }
559 if (code != ResXMLTree::START_TAG) {
560 continue;
561 }
562 depth++;
563 String8 tag(tree.getElementName(&len));
564 //printf("Depth %d tag %s\n", depth, tag.string());
565 if (depth == 1) {
566 if (tag != "manifest") {
567 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
568 goto bail;
569 }
570 String8 pkg = getAttribute(tree, NULL, "package", NULL);
571 printf("package: %s\n", pkg.string());
572 } else if (depth == 2 && tag == "permission") {
573 String8 error;
574 String8 name = getAttribute(tree, NAME_ATTR, &error);
575 if (error != "") {
576 fprintf(stderr, "ERROR: %s\n", error.string());
577 goto bail;
578 }
579 printf("permission: %s\n", name.string());
580 } else if (depth == 2 && tag == "uses-permission") {
581 String8 error;
582 String8 name = getAttribute(tree, NAME_ATTR, &error);
583 if (error != "") {
584 fprintf(stderr, "ERROR: %s\n", error.string());
585 goto bail;
586 }
587 printf("uses-permission: %s\n", name.string());
588 }
589 }
590 } else if (strcmp("badging", option) == 0) {
Dianne Hackborne289bff2011-06-13 19:33:22 -0700591 Vector<String8> locales;
592 res.getLocales(&locales);
593
594 Vector<ResTable_config> configs;
595 res.getConfigurations(&configs);
596 SortedVector<int> densities;
597 const size_t NC = configs.size();
598 for (size_t i=0; i<NC; i++) {
599 int dens = configs[i].density;
600 if (dens == 0) dens = 160;
601 densities.add(dens);
602 }
603
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800604 size_t len;
605 ResXMLTree::event_code_t code;
606 int depth = 0;
607 String8 error;
608 bool withinActivity = false;
609 bool isMainActivity = false;
610 bool isLauncherActivity = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700611 bool isSearchable = false;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700612 bool withinApplication = false;
613 bool withinReceiver = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700614 bool withinService = false;
615 bool withinIntentFilter = false;
616 bool hasMainActivity = false;
617 bool hasOtherActivities = false;
618 bool hasOtherReceivers = false;
619 bool hasOtherServices = false;
620 bool hasWallpaperService = false;
621 bool hasImeService = false;
622 bool hasWidgetReceivers = false;
623 bool hasIntentFilter = false;
624 bool actMainActivity = false;
625 bool actWidgetReceivers = false;
626 bool actImeService = false;
627 bool actWallpaperService = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700628
Kenny Root063a44e2011-12-08 08:46:03 -0800629 // These two implement the implicit permissions that are granted
630 // to pre-1.6 applications.
631 bool hasWriteExternalStoragePermission = false;
632 bool hasReadPhoneStatePermission = false;
633
Dan Morrill89d97c12010-05-03 16:13:14 -0700634 // This next group of variables is used to implement a group of
635 // backward-compatibility heuristics necessitated by the addition of
636 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
637 // heuristic is "if an app requests a permission but doesn't explicitly
638 // request the corresponding <uses-feature>, presume it's there anyway".
639 bool specCameraFeature = false; // camera-related
640 bool specCameraAutofocusFeature = false;
641 bool reqCameraAutofocusFeature = false;
642 bool reqCameraFlashFeature = false;
Dianne Hackborne5276a72009-08-27 16:28:44 -0700643 bool hasCameraPermission = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700644 bool specLocationFeature = false; // location-related
645 bool specNetworkLocFeature = false;
646 bool reqNetworkLocFeature = false;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800647 bool specGpsFeature = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700648 bool reqGpsFeature = false;
649 bool hasMockLocPermission = false;
650 bool hasCoarseLocPermission = false;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800651 bool hasGpsPermission = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700652 bool hasGeneralLocPermission = false;
653 bool specBluetoothFeature = false; // Bluetooth API-related
654 bool hasBluetoothPermission = false;
655 bool specMicrophoneFeature = false; // microphone-related
656 bool hasRecordAudioPermission = false;
657 bool specWiFiFeature = false;
658 bool hasWiFiPermission = false;
659 bool specTelephonyFeature = false; // telephony-related
660 bool reqTelephonySubFeature = false;
661 bool hasTelephonyPermission = false;
662 bool specTouchscreenFeature = false; // touchscreen-related
663 bool specMultitouchFeature = false;
664 bool reqDistinctMultitouchFeature = false;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700665 bool specScreenPortraitFeature = false;
666 bool specScreenLandscapeFeature = false;
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -0700667 bool reqScreenPortraitFeature = false;
668 bool reqScreenLandscapeFeature = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700669 // 2.2 also added some other features that apps can request, but that
670 // have no corresponding permission, so we cannot implement any
671 // back-compatibility heuristic for them. The below are thus unnecessary
672 // (but are retained here for documentary purposes.)
673 //bool specCompassFeature = false;
674 //bool specAccelerometerFeature = false;
675 //bool specProximityFeature = false;
676 //bool specAmbientLightFeature = false;
677 //bool specLiveWallpaperFeature = false;
678
Dianne Hackborn723738c2009-06-25 19:48:04 -0700679 int targetSdk = 0;
680 int smallScreen = 1;
681 int normalScreen = 1;
682 int largeScreen = 1;
Dianne Hackbornf43489d2010-08-20 12:44:33 -0700683 int xlargeScreen = 1;
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700684 int anyDensity = 1;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700685 int requiresSmallestWidthDp = 0;
686 int compatibleWidthLimitDp = 0;
687 int largestWidthLimitDp = 0;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700688 String8 pkg;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800689 String8 activityName;
690 String8 activityLabel;
691 String8 activityIcon;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700692 String8 receiverName;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700693 String8 serviceName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800694 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
695 if (code == ResXMLTree::END_TAG) {
696 depth--;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700697 if (depth < 2) {
698 withinApplication = false;
699 } else if (depth < 3) {
700 if (withinActivity && isMainActivity && isLauncherActivity) {
701 const char *aName = getComponentName(pkg, activityName);
Dianne Hackborne289bff2011-06-13 19:33:22 -0700702 printf("launchable-activity:");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700703 if (aName != NULL) {
Dianne Hackborne289bff2011-06-13 19:33:22 -0700704 printf(" name='%s' ", aName);
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700705 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700706 printf(" label='%s' icon='%s'\n",
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700707 activityLabel.string(),
708 activityIcon.string());
709 }
710 if (!hasIntentFilter) {
711 hasOtherActivities |= withinActivity;
712 hasOtherReceivers |= withinReceiver;
713 hasOtherServices |= withinService;
714 }
715 withinActivity = false;
716 withinService = false;
717 withinReceiver = false;
718 hasIntentFilter = false;
719 isMainActivity = isLauncherActivity = false;
720 } else if (depth < 4) {
721 if (withinIntentFilter) {
722 if (withinActivity) {
723 hasMainActivity |= actMainActivity;
724 hasOtherActivities |= !actMainActivity;
725 } else if (withinReceiver) {
726 hasWidgetReceivers |= actWidgetReceivers;
727 hasOtherReceivers |= !actWidgetReceivers;
728 } else if (withinService) {
729 hasImeService |= actImeService;
730 hasWallpaperService |= actWallpaperService;
731 hasOtherServices |= (!actImeService && !actWallpaperService);
732 }
733 }
734 withinIntentFilter = false;
735 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800736 continue;
737 }
738 if (code != ResXMLTree::START_TAG) {
739 continue;
740 }
741 depth++;
742 String8 tag(tree.getElementName(&len));
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700743 //printf("Depth %d, %s\n", depth, tag.string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800744 if (depth == 1) {
745 if (tag != "manifest") {
746 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
747 goto bail;
748 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700749 pkg = getAttribute(tree, NULL, "package", NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800750 printf("package: name='%s' ", pkg.string());
751 int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
752 if (error != "") {
753 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
754 goto bail;
755 }
756 if (versionCode > 0) {
757 printf("versionCode='%d' ", versionCode);
758 } else {
759 printf("versionCode='' ");
760 }
Dianne Hackborncf244ad2010-03-09 15:00:30 -0800761 String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800762 if (error != "") {
763 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
764 goto bail;
765 }
766 printf("versionName='%s'\n", versionName.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700767 } else if (depth == 2) {
768 withinApplication = false;
769 if (tag == "application") {
770 withinApplication = true;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700771
772 String8 label;
773 const size_t NL = locales.size();
774 for (size_t i=0; i<NL; i++) {
775 const char* localeStr = locales[i].string();
776 assets.setLocale(localeStr != NULL ? localeStr : "");
777 String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
778 if (llabel != "") {
779 if (localeStr == NULL || strlen(localeStr) == 0) {
780 label = llabel;
781 printf("application-label:'%s'\n", llabel.string());
782 } else {
783 if (label == "") {
784 label = llabel;
785 }
786 printf("application-label-%s:'%s'\n", localeStr,
787 llabel.string());
788 }
789 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700790 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700791
792 ResTable_config tmpConfig = config;
793 const size_t ND = densities.size();
794 for (size_t i=0; i<ND; i++) {
795 tmpConfig.density = densities[i];
796 assets.setConfiguration(tmpConfig);
797 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
798 if (icon != "") {
799 printf("application-icon-%d:'%s'\n", densities[i], icon.string());
800 }
801 }
802 assets.setConfiguration(config);
803
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700804 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
805 if (error != "") {
806 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
807 goto bail;
808 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700809 int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700810 if (error != "") {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700811 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700812 goto bail;
813 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700814 printf("application: label='%s' ", label.string());
815 printf("icon='%s'\n", icon.string());
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700816 if (testOnly != 0) {
817 printf("testOnly='%d'\n", testOnly);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700818 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700819 } else if (tag == "uses-sdk") {
820 int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
821 if (error != "") {
822 error = "";
823 String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
824 if (error != "") {
825 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
826 error.string());
827 goto bail;
828 }
Dianne Hackborn723738c2009-06-25 19:48:04 -0700829 if (name == "Donut") targetSdk = 4;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700830 printf("sdkVersion:'%s'\n", name.string());
831 } else if (code != -1) {
Dianne Hackborn723738c2009-06-25 19:48:04 -0700832 targetSdk = code;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700833 printf("sdkVersion:'%d'\n", code);
834 }
Suchi Amalapurapu75c49842009-08-14 15:13:09 -0700835 code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
836 if (code != -1) {
837 printf("maxSdkVersion:'%d'\n", code);
838 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700839 code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
840 if (error != "") {
841 error = "";
842 String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
843 if (error != "") {
844 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
845 error.string());
846 goto bail;
847 }
Dianne Hackborn723738c2009-06-25 19:48:04 -0700848 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700849 printf("targetSdkVersion:'%s'\n", name.string());
850 } else if (code != -1) {
Dianne Hackborn723738c2009-06-25 19:48:04 -0700851 if (targetSdk < code) {
852 targetSdk = code;
853 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700854 printf("targetSdkVersion:'%d'\n", code);
855 }
856 } else if (tag == "uses-configuration") {
857 int32_t reqTouchScreen = getIntegerAttribute(tree,
858 REQ_TOUCH_SCREEN_ATTR, NULL, 0);
859 int32_t reqKeyboardType = getIntegerAttribute(tree,
860 REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
861 int32_t reqHardKeyboard = getIntegerAttribute(tree,
862 REQ_HARD_KEYBOARD_ATTR, NULL, 0);
863 int32_t reqNavigation = getIntegerAttribute(tree,
864 REQ_NAVIGATION_ATTR, NULL, 0);
865 int32_t reqFiveWayNav = getIntegerAttribute(tree,
866 REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
Dianne Hackborncb2d50d2010-01-06 11:29:54 -0800867 printf("uses-configuration:");
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700868 if (reqTouchScreen != 0) {
869 printf(" reqTouchScreen='%d'", reqTouchScreen);
870 }
871 if (reqKeyboardType != 0) {
872 printf(" reqKeyboardType='%d'", reqKeyboardType);
873 }
874 if (reqHardKeyboard != 0) {
875 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
876 }
877 if (reqNavigation != 0) {
878 printf(" reqNavigation='%d'", reqNavigation);
879 }
880 if (reqFiveWayNav != 0) {
881 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
882 }
883 printf("\n");
Dianne Hackborn723738c2009-06-25 19:48:04 -0700884 } else if (tag == "supports-screens") {
885 smallScreen = getIntegerAttribute(tree,
886 SMALL_SCREEN_ATTR, NULL, 1);
887 normalScreen = getIntegerAttribute(tree,
888 NORMAL_SCREEN_ATTR, NULL, 1);
889 largeScreen = getIntegerAttribute(tree,
890 LARGE_SCREEN_ATTR, NULL, 1);
Dianne Hackbornf43489d2010-08-20 12:44:33 -0700891 xlargeScreen = getIntegerAttribute(tree,
892 XLARGE_SCREEN_ATTR, NULL, 1);
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700893 anyDensity = getIntegerAttribute(tree,
894 ANY_DENSITY_ATTR, NULL, 1);
Dianne Hackborne289bff2011-06-13 19:33:22 -0700895 requiresSmallestWidthDp = getIntegerAttribute(tree,
896 REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
897 compatibleWidthLimitDp = getIntegerAttribute(tree,
898 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
899 largestWidthLimitDp = getIntegerAttribute(tree,
900 LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
Dianne Hackborne5276a72009-08-27 16:28:44 -0700901 } else if (tag == "uses-feature") {
902 String8 name = getAttribute(tree, NAME_ATTR, &error);
Suchi Amalapurapu40b94722009-09-20 13:39:37 -0700903
904 if (name != "" && error == "") {
Dianne Hackborne5276a72009-08-27 16:28:44 -0700905 int req = getIntegerAttribute(tree,
906 REQUIRED_ATTR, NULL, 1);
Dan Morrill89d97c12010-05-03 16:13:14 -0700907
Dianne Hackborne5276a72009-08-27 16:28:44 -0700908 if (name == "android.hardware.camera") {
909 specCameraFeature = true;
Dan Morrill89d97c12010-05-03 16:13:14 -0700910 } else if (name == "android.hardware.camera.autofocus") {
911 // these have no corresponding permission to check for,
912 // but should imply the foundational camera permission
913 reqCameraAutofocusFeature = reqCameraAutofocusFeature || req;
914 specCameraAutofocusFeature = true;
915 } else if (req && (name == "android.hardware.camera.flash")) {
916 // these have no corresponding permission to check for,
917 // but should imply the foundational camera permission
918 reqCameraFlashFeature = true;
919 } else if (name == "android.hardware.location") {
920 specLocationFeature = true;
921 } else if (name == "android.hardware.location.network") {
922 specNetworkLocFeature = true;
923 reqNetworkLocFeature = reqNetworkLocFeature || req;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800924 } else if (name == "android.hardware.location.gps") {
925 specGpsFeature = true;
Dan Morrill89d97c12010-05-03 16:13:14 -0700926 reqGpsFeature = reqGpsFeature || req;
927 } else if (name == "android.hardware.bluetooth") {
928 specBluetoothFeature = true;
929 } else if (name == "android.hardware.touchscreen") {
930 specTouchscreenFeature = true;
931 } else if (name == "android.hardware.touchscreen.multitouch") {
932 specMultitouchFeature = true;
933 } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
934 reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req;
935 } else if (name == "android.hardware.microphone") {
936 specMicrophoneFeature = true;
937 } else if (name == "android.hardware.wifi") {
938 specWiFiFeature = true;
939 } else if (name == "android.hardware.telephony") {
940 specTelephonyFeature = true;
941 } else if (req && (name == "android.hardware.telephony.gsm" ||
942 name == "android.hardware.telephony.cdma")) {
943 // these have no corresponding permission to check for,
944 // but should imply the foundational telephony permission
945 reqTelephonySubFeature = true;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700946 } else if (name == "android.hardware.screen.portrait") {
947 specScreenPortraitFeature = true;
948 } else if (name == "android.hardware.screen.landscape") {
949 specScreenLandscapeFeature = true;
Dianne Hackborne5276a72009-08-27 16:28:44 -0700950 }
951 printf("uses-feature%s:'%s'\n",
952 req ? "" : "-not-required", name.string());
953 } else {
954 int vers = getIntegerAttribute(tree,
955 GL_ES_VERSION_ATTR, &error);
956 if (error == "") {
957 printf("uses-gl-es:'0x%x'\n", vers);
958 }
959 }
960 } else if (tag == "uses-permission") {
961 String8 name = getAttribute(tree, NAME_ATTR, &error);
Suchi Amalapurapu40b94722009-09-20 13:39:37 -0700962 if (name != "" && error == "") {
Dianne Hackborne5276a72009-08-27 16:28:44 -0700963 if (name == "android.permission.CAMERA") {
964 hasCameraPermission = true;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800965 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
966 hasGpsPermission = true;
Dan Morrill89d97c12010-05-03 16:13:14 -0700967 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
968 hasMockLocPermission = true;
969 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
970 hasCoarseLocPermission = true;
971 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
972 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
973 hasGeneralLocPermission = true;
974 } else if (name == "android.permission.BLUETOOTH" ||
975 name == "android.permission.BLUETOOTH_ADMIN") {
976 hasBluetoothPermission = true;
977 } else if (name == "android.permission.RECORD_AUDIO") {
978 hasRecordAudioPermission = true;
979 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
980 name == "android.permission.CHANGE_WIFI_STATE" ||
981 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
982 hasWiFiPermission = true;
983 } else if (name == "android.permission.CALL_PHONE" ||
984 name == "android.permission.CALL_PRIVILEGED" ||
985 name == "android.permission.MODIFY_PHONE_STATE" ||
986 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
987 name == "android.permission.READ_SMS" ||
988 name == "android.permission.RECEIVE_SMS" ||
989 name == "android.permission.RECEIVE_MMS" ||
990 name == "android.permission.RECEIVE_WAP_PUSH" ||
991 name == "android.permission.SEND_SMS" ||
992 name == "android.permission.WRITE_APN_SETTINGS" ||
993 name == "android.permission.WRITE_SMS") {
994 hasTelephonyPermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -0800995 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
996 hasWriteExternalStoragePermission = true;
997 } else if (name == "android.permission.READ_PHONE_STATE") {
998 hasReadPhoneStatePermission = true;
Dianne Hackborne5276a72009-08-27 16:28:44 -0700999 }
1000 printf("uses-permission:'%s'\n", name.string());
1001 } else {
1002 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1003 error.string());
1004 goto bail;
1005 }
Dianne Hackborn43b68032010-09-02 17:14:41 -07001006 } else if (tag == "uses-package") {
1007 String8 name = getAttribute(tree, NAME_ATTR, &error);
1008 if (name != "" && error == "") {
1009 printf("uses-package:'%s'\n", name.string());
1010 } else {
1011 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1012 error.string());
1013 goto bail;
1014 }
Jeff Hamiltone2c17f92010-02-12 13:45:16 -06001015 } else if (tag == "original-package") {
1016 String8 name = getAttribute(tree, NAME_ATTR, &error);
1017 if (name != "" && error == "") {
1018 printf("original-package:'%s'\n", name.string());
1019 } else {
1020 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1021 error.string());
1022 goto bail;
1023 }
Dan Morrill096b67f2010-12-13 16:25:54 -08001024 } else if (tag == "supports-gl-texture") {
Dan Morrill6f51fc12010-10-13 14:33:43 -07001025 String8 name = getAttribute(tree, NAME_ATTR, &error);
1026 if (name != "" && error == "") {
Dan Morrill096b67f2010-12-13 16:25:54 -08001027 printf("supports-gl-texture:'%s'\n", name.string());
Dan Morrill6f51fc12010-10-13 14:33:43 -07001028 } else {
1029 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1030 error.string());
1031 goto bail;
1032 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001033 } else if (tag == "compatible-screens") {
1034 printCompatibleScreens(tree);
1035 depth--;
Kenny Root56088a52011-09-29 13:49:45 -07001036 } else if (tag == "package-verifier") {
1037 String8 name = getAttribute(tree, NAME_ATTR, &error);
1038 if (name != "" && error == "") {
1039 String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
1040 if (publicKey != "" && error == "") {
1041 printf("package-verifier: name='%s' publicKey='%s'\n",
1042 name.string(), publicKey.string());
1043 }
1044 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001045 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001046 } else if (depth == 3 && withinApplication) {
1047 withinActivity = false;
1048 withinReceiver = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001049 withinService = false;
1050 hasIntentFilter = false;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001051 if(tag == "activity") {
1052 withinActivity = true;
1053 activityName = getAttribute(tree, NAME_ATTR, &error);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001054 if (error != "") {
1055 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1056 goto bail;
1057 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001058
1059 activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001060 if (error != "") {
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001061 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001062 goto bail;
1063 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001064
1065 activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1066 if (error != "") {
1067 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
1068 goto bail;
1069 }
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001070
1071 int32_t orien = getResolvedIntegerAttribute(&res, tree,
1072 SCREEN_ORIENTATION_ATTR, &error);
1073 if (error == "") {
1074 if (orien == 0 || orien == 6 || orien == 8) {
1075 // Requests landscape, sensorLandscape, or reverseLandscape.
1076 reqScreenLandscapeFeature = true;
1077 } else if (orien == 1 || orien == 7 || orien == 9) {
1078 // Requests portrait, sensorPortrait, or reversePortrait.
1079 reqScreenPortraitFeature = true;
1080 }
1081 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001082 } else if (tag == "uses-library") {
1083 String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1084 if (error != "") {
1085 fprintf(stderr, "ERROR getting 'android:name' attribute for uses-library: %s\n", error.string());
1086 goto bail;
1087 }
Dianne Hackborn49237342009-08-27 20:08:01 -07001088 int req = getIntegerAttribute(tree,
1089 REQUIRED_ATTR, NULL, 1);
1090 printf("uses-library%s:'%s'\n",
1091 req ? "" : "-not-required", libraryName.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001092 } else if (tag == "receiver") {
1093 withinReceiver = true;
1094 receiverName = getAttribute(tree, NAME_ATTR, &error);
1095
1096 if (error != "") {
1097 fprintf(stderr, "ERROR getting 'android:name' attribute for receiver: %s\n", error.string());
1098 goto bail;
1099 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001100 } else if (tag == "service") {
1101 withinService = true;
1102 serviceName = getAttribute(tree, NAME_ATTR, &error);
1103
1104 if (error != "") {
1105 fprintf(stderr, "ERROR getting 'android:name' attribute for service: %s\n", error.string());
1106 goto bail;
1107 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001108 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001109 } else if ((depth == 4) && (tag == "intent-filter")) {
1110 hasIntentFilter = true;
1111 withinIntentFilter = true;
1112 actMainActivity = actWidgetReceivers = actImeService = actWallpaperService = false;
1113 } else if ((depth == 5) && withinIntentFilter){
1114 String8 action;
1115 if (tag == "action") {
1116 action = getAttribute(tree, NAME_ATTR, &error);
1117 if (error != "") {
1118 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1119 goto bail;
1120 }
1121 if (withinActivity) {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001122 if (action == "android.intent.action.MAIN") {
1123 isMainActivity = true;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001124 actMainActivity = true;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001125 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001126 } else if (withinReceiver) {
1127 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1128 actWidgetReceivers = true;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001129 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001130 } else if (withinService) {
1131 if (action == "android.view.InputMethod") {
1132 actImeService = true;
1133 } else if (action == "android.service.wallpaper.WallpaperService") {
1134 actWallpaperService = true;
1135 }
1136 }
1137 if (action == "android.intent.action.SEARCH") {
1138 isSearchable = true;
1139 }
1140 }
1141
1142 if (tag == "category") {
1143 String8 category = getAttribute(tree, NAME_ATTR, &error);
1144 if (error != "") {
1145 fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string());
1146 goto bail;
1147 }
1148 if (withinActivity) {
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001149 if (category == "android.intent.category.LAUNCHER") {
1150 isLauncherActivity = true;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001151 }
1152 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001153 }
1154 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001155 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001156
Kenny Root063a44e2011-12-08 08:46:03 -08001157 // Pre-1.6 implicitly granted permission compatibility logic
1158 if (targetSdk < 4) {
1159 if (!hasWriteExternalStoragePermission) {
1160 printf("uses-permission:'android.permission.WRITE_EXTERNAL_STORAGE'\n");
1161 }
1162 if (!hasReadPhoneStatePermission) {
1163 printf("uses-permission:'android.permission.READ_PHONE_STATE'\n");
1164 }
1165 }
1166
Dan Morrill89d97c12010-05-03 16:13:14 -07001167 /* The following blocks handle printing "inferred" uses-features, based
1168 * on whether related features or permissions are used by the app.
1169 * Note that the various spec*Feature variables denote whether the
1170 * relevant tag was *present* in the AndroidManfest, not that it was
1171 * present and set to true.
1172 */
1173 // Camera-related back-compatibility logic
1174 if (!specCameraFeature) {
1175 if (reqCameraFlashFeature || reqCameraAutofocusFeature) {
1176 // if app requested a sub-feature (autofocus or flash) and didn't
1177 // request the base camera feature, we infer that it meant to
1178 printf("uses-feature:'android.hardware.camera'\n");
1179 } else if (hasCameraPermission) {
1180 // if app wants to use camera but didn't request the feature, we infer
1181 // that it meant to, and further that it wants autofocus
1182 // (which was the 1.0 - 1.5 behavior)
1183 printf("uses-feature:'android.hardware.camera'\n");
1184 if (!specCameraAutofocusFeature) {
1185 printf("uses-feature:'android.hardware.camera.autofocus'\n");
1186 }
1187 }
Dianne Hackborne5276a72009-08-27 16:28:44 -07001188 }
Doug Zongkerdbe7a682009-10-09 11:24:51 -07001189
Dan Morrill89d97c12010-05-03 16:13:14 -07001190 // Location-related back-compatibility logic
1191 if (!specLocationFeature &&
1192 (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission ||
1193 hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) {
1194 // if app either takes a location-related permission or requests one of the
1195 // sub-features, we infer that it also meant to request the base location feature
1196 printf("uses-feature:'android.hardware.location'\n");
1197 }
Dianne Hackbornef05e072010-03-01 17:43:39 -08001198 if (!specGpsFeature && hasGpsPermission) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001199 // if app takes GPS (FINE location) perm but does not request the GPS
1200 // feature, we infer that it meant to
Dianne Hackbornef05e072010-03-01 17:43:39 -08001201 printf("uses-feature:'android.hardware.location.gps'\n");
1202 }
Dan Morrill89d97c12010-05-03 16:13:14 -07001203 if (!specNetworkLocFeature && hasCoarseLocPermission) {
1204 // if app takes Network location (COARSE location) perm but does not request the
1205 // network location feature, we infer that it meant to
1206 printf("uses-feature:'android.hardware.location.network'\n");
1207 }
1208
1209 // Bluetooth-related compatibility logic
Dan Morrill6b22d812010-06-15 21:41:42 -07001210 if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001211 // if app takes a Bluetooth permission but does not request the Bluetooth
1212 // feature, we infer that it meant to
1213 printf("uses-feature:'android.hardware.bluetooth'\n");
1214 }
1215
1216 // Microphone-related compatibility logic
1217 if (!specMicrophoneFeature && hasRecordAudioPermission) {
1218 // if app takes the record-audio permission but does not request the microphone
1219 // feature, we infer that it meant to
1220 printf("uses-feature:'android.hardware.microphone'\n");
1221 }
1222
1223 // WiFi-related compatibility logic
1224 if (!specWiFiFeature && hasWiFiPermission) {
1225 // if app takes one of the WiFi permissions but does not request the WiFi
1226 // feature, we infer that it meant to
1227 printf("uses-feature:'android.hardware.wifi'\n");
1228 }
1229
1230 // Telephony-related compatibility logic
1231 if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) {
1232 // if app takes one of the telephony permissions or requests a sub-feature but
1233 // does not request the base telephony feature, we infer that it meant to
1234 printf("uses-feature:'android.hardware.telephony'\n");
1235 }
1236
1237 // Touchscreen-related back-compatibility logic
1238 if (!specTouchscreenFeature) { // not a typo!
1239 // all apps are presumed to require a touchscreen, unless they explicitly say
1240 // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
1241 // Note that specTouchscreenFeature is true if the tag is present, regardless
1242 // of whether its value is true or false, so this is safe
1243 printf("uses-feature:'android.hardware.touchscreen'\n");
1244 }
1245 if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
1246 // if app takes one of the telephony permissions or requests a sub-feature but
1247 // does not request the base telephony feature, we infer that it meant to
1248 printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
1249 }
Dianne Hackbornef05e072010-03-01 17:43:39 -08001250
Dianne Hackborne289bff2011-06-13 19:33:22 -07001251 // Landscape/portrait-related compatibility logic
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001252 if (!specScreenLandscapeFeature && !specScreenPortraitFeature) {
1253 // If the app has specified any activities in its manifest
1254 // that request a specific orientation, then assume that
1255 // orientation is required.
1256 if (reqScreenLandscapeFeature) {
1257 printf("uses-feature:'android.hardware.screen.landscape'\n");
1258 }
1259 if (reqScreenPortraitFeature) {
1260 printf("uses-feature:'android.hardware.screen.portrait'\n");
1261 }
Dianne Hackborne289bff2011-06-13 19:33:22 -07001262 }
1263
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001264 if (hasMainActivity) {
1265 printf("main\n");
1266 }
1267 if (hasWidgetReceivers) {
1268 printf("app-widget\n");
1269 }
1270 if (hasImeService) {
1271 printf("ime\n");
1272 }
1273 if (hasWallpaperService) {
1274 printf("wallpaper\n");
1275 }
1276 if (hasOtherActivities) {
1277 printf("other-activities\n");
1278 }
1279 if (isSearchable) {
1280 printf("search\n");
1281 }
1282 if (hasOtherReceivers) {
1283 printf("other-receivers\n");
1284 }
1285 if (hasOtherServices) {
1286 printf("other-services\n");
1287 }
1288
Dianne Hackborne289bff2011-06-13 19:33:22 -07001289 // For modern apps, if screen size buckets haven't been specified
1290 // but the new width ranges have, then infer the buckets from them.
1291 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1292 && requiresSmallestWidthDp > 0) {
1293 int compatWidth = compatibleWidthLimitDp;
1294 if (compatWidth <= 0) compatWidth = requiresSmallestWidthDp;
1295 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1296 smallScreen = -1;
1297 } else {
1298 smallScreen = 0;
1299 }
1300 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1301 normalScreen = -1;
1302 } else {
1303 normalScreen = 0;
1304 }
1305 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1306 largeScreen = -1;
1307 } else {
1308 largeScreen = 0;
1309 }
1310 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1311 xlargeScreen = -1;
1312 } else {
1313 xlargeScreen = 0;
1314 }
1315 }
1316
Dianne Hackborn723738c2009-06-25 19:48:04 -07001317 // Determine default values for any unspecified screen sizes,
1318 // based on the target SDK of the package. As of 4 (donut)
1319 // the screen size support was introduced, so all default to
1320 // enabled.
1321 if (smallScreen > 0) {
1322 smallScreen = targetSdk >= 4 ? -1 : 0;
1323 }
1324 if (normalScreen > 0) {
1325 normalScreen = -1;
1326 }
1327 if (largeScreen > 0) {
1328 largeScreen = targetSdk >= 4 ? -1 : 0;
1329 }
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001330 if (xlargeScreen > 0) {
Scott Maind58fb972010-11-04 18:32:00 -07001331 // Introduced in Gingerbread.
1332 xlargeScreen = targetSdk >= 9 ? -1 : 0;
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001333 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001334 if (anyDensity > 0) {
Dianne Hackborne289bff2011-06-13 19:33:22 -07001335 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1336 || compatibleWidthLimitDp > 0) ? -1 : 0;
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001337 }
Dianne Hackborn723738c2009-06-25 19:48:04 -07001338 printf("supports-screens:");
1339 if (smallScreen != 0) printf(" 'small'");
1340 if (normalScreen != 0) printf(" 'normal'");
1341 if (largeScreen != 0) printf(" 'large'");
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001342 if (xlargeScreen != 0) printf(" 'xlarge'");
Dianne Hackborn723738c2009-06-25 19:48:04 -07001343 printf("\n");
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001344 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
Dianne Hackborne289bff2011-06-13 19:33:22 -07001345 if (requiresSmallestWidthDp > 0) {
1346 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1347 }
1348 if (compatibleWidthLimitDp > 0) {
1349 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1350 }
1351 if (largestWidthLimitDp > 0) {
1352 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1353 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001354
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001355 printf("locales:");
Dianne Hackborne17086b2009-06-19 15:13:28 -07001356 const size_t NL = locales.size();
1357 for (size_t i=0; i<NL; i++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001358 const char* localeStr = locales[i].string();
1359 if (localeStr == NULL || strlen(localeStr) == 0) {
1360 localeStr = "--_--";
1361 }
1362 printf(" '%s'", localeStr);
1363 }
1364 printf("\n");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001365
Dianne Hackborne17086b2009-06-19 15:13:28 -07001366 printf("densities:");
1367 const size_t ND = densities.size();
1368 for (size_t i=0; i<ND; i++) {
1369 printf(" '%d'", densities[i]);
1370 }
1371 printf("\n");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001372
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001373 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1374 if (dir != NULL) {
1375 if (dir->getFileCount() > 0) {
1376 printf("native-code:");
1377 for (size_t i=0; i<dir->getFileCount(); i++) {
1378 printf(" '%s'", dir->getFileName(i).string());
1379 }
1380 printf("\n");
1381 }
1382 delete dir;
1383 }
Dan Morrille74763e2012-01-06 10:47:10 -08001384 } else if (strcmp("badger", option) == 0) {
1385 printf(CONSOLE_DATA);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001386 } else if (strcmp("configurations", option) == 0) {
1387 Vector<ResTable_config> configs;
1388 res.getConfigurations(&configs);
1389 const size_t N = configs.size();
1390 for (size_t i=0; i<N; i++) {
1391 printf("%s\n", configs[i].toString().string());
1392 }
1393 } else {
1394 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
1395 goto bail;
1396 }
1397 }
1398
1399 result = NO_ERROR;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001400
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001401bail:
1402 if (asset) {
1403 delete asset;
1404 }
1405 return (result != NO_ERROR);
1406}
1407
1408
1409/*
1410 * Handle the "add" command, which wants to add files to a new or
1411 * pre-existing archive.
1412 */
1413int doAdd(Bundle* bundle)
1414{
1415 ZipFile* zip = NULL;
1416 status_t result = UNKNOWN_ERROR;
1417 const char* zipFileName;
1418
1419 if (bundle->getUpdate()) {
1420 /* avoid confusion */
1421 fprintf(stderr, "ERROR: can't use '-u' with add\n");
1422 goto bail;
1423 }
1424
1425 if (bundle->getFileSpecCount() < 1) {
1426 fprintf(stderr, "ERROR: must specify zip file name\n");
1427 goto bail;
1428 }
1429 zipFileName = bundle->getFileSpecEntry(0);
1430
1431 if (bundle->getFileSpecCount() < 2) {
1432 fprintf(stderr, "NOTE: nothing to do\n");
1433 goto bail;
1434 }
1435
1436 zip = openReadWrite(zipFileName, true);
1437 if (zip == NULL) {
1438 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
1439 goto bail;
1440 }
1441
1442 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1443 const char* fileName = bundle->getFileSpecEntry(i);
1444
1445 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
1446 printf(" '%s'... (from gzip)\n", fileName);
1447 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
1448 } else {
Doug Zongkerdbe7a682009-10-09 11:24:51 -07001449 if (bundle->getJunkPath()) {
1450 String8 storageName = String8(fileName).getPathLeaf();
1451 printf(" '%s' as '%s'...\n", fileName, storageName.string());
1452 result = zip->add(fileName, storageName.string(),
1453 bundle->getCompressionMethod(), NULL);
1454 } else {
1455 printf(" '%s'...\n", fileName);
1456 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
1457 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001458 }
1459 if (result != NO_ERROR) {
1460 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
1461 if (result == NAME_NOT_FOUND)
1462 fprintf(stderr, ": file not found\n");
1463 else if (result == ALREADY_EXISTS)
1464 fprintf(stderr, ": already exists in archive\n");
1465 else
1466 fprintf(stderr, "\n");
1467 goto bail;
1468 }
1469 }
1470
1471 result = NO_ERROR;
1472
1473bail:
1474 delete zip;
1475 return (result != NO_ERROR);
1476}
1477
1478
1479/*
1480 * Delete files from an existing archive.
1481 */
1482int doRemove(Bundle* bundle)
1483{
1484 ZipFile* zip = NULL;
1485 status_t result = UNKNOWN_ERROR;
1486 const char* zipFileName;
1487
1488 if (bundle->getFileSpecCount() < 1) {
1489 fprintf(stderr, "ERROR: must specify zip file name\n");
1490 goto bail;
1491 }
1492 zipFileName = bundle->getFileSpecEntry(0);
1493
1494 if (bundle->getFileSpecCount() < 2) {
1495 fprintf(stderr, "NOTE: nothing to do\n");
1496 goto bail;
1497 }
1498
1499 zip = openReadWrite(zipFileName, false);
1500 if (zip == NULL) {
1501 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
1502 zipFileName);
1503 goto bail;
1504 }
1505
1506 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1507 const char* fileName = bundle->getFileSpecEntry(i);
1508 ZipEntry* entry;
1509
1510 entry = zip->getEntryByName(fileName);
1511 if (entry == NULL) {
1512 printf(" '%s' NOT FOUND\n", fileName);
1513 continue;
1514 }
1515
1516 result = zip->remove(entry);
1517
1518 if (result != NO_ERROR) {
1519 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
1520 bundle->getFileSpecEntry(i), zipFileName);
1521 goto bail;
1522 }
1523 }
1524
1525 /* update the archive */
1526 zip->flush();
1527
1528bail:
1529 delete zip;
1530 return (result != NO_ERROR);
1531}
1532
1533
1534/*
1535 * Package up an asset directory and associated application files.
1536 */
1537int doPackage(Bundle* bundle)
1538{
1539 const char* outputAPKFile;
1540 int retVal = 1;
1541 status_t err;
1542 sp<AaptAssets> assets;
1543 int N;
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001544 FILE* fp;
1545 String8 dependencyFile;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001546
1547 // -c zz_ZZ means do pseudolocalization
1548 ResourceFilter filter;
1549 err = filter.parse(bundle->getConfigurations());
1550 if (err != NO_ERROR) {
1551 goto bail;
1552 }
1553 if (filter.containsPseudo()) {
1554 bundle->setPseudolocalize(true);
1555 }
1556
1557 N = bundle->getFileSpecCount();
1558 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
1559 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDir() == NULL) {
1560 fprintf(stderr, "ERROR: no input files\n");
1561 goto bail;
1562 }
1563
1564 outputAPKFile = bundle->getOutputAPKFile();
1565
1566 // Make sure the filenames provided exist and are of the appropriate type.
1567 if (outputAPKFile) {
1568 FileType type;
1569 type = getFileType(outputAPKFile);
1570 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
1571 fprintf(stderr,
1572 "ERROR: output file '%s' exists but is not regular file\n",
1573 outputAPKFile);
1574 goto bail;
1575 }
1576 }
1577
1578 // Load the assets.
1579 assets = new AaptAssets();
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001580
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001581 // Set up the resource gathering in assets if we're going to generate
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001582 // dependency files. Every time we encounter a resource while slurping
1583 // the tree, we'll add it to these stores so we have full resource paths
1584 // to write to a dependency file.
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001585 if (bundle->getGenDependencies()) {
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001586 sp<FilePathStore> resPathStore = new FilePathStore;
1587 assets->setFullResPaths(resPathStore);
1588 sp<FilePathStore> assetPathStore = new FilePathStore;
1589 assets->setFullAssetPaths(assetPathStore);
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001590 }
1591
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001592 err = assets->slurpFromArgs(bundle);
1593 if (err < 0) {
1594 goto bail;
1595 }
1596
1597 if (bundle->getVerbose()) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001598 assets->print(String8());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001599 }
1600
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001601 // If they asked for any fileAs that need to be compiled, do so.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001602 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
1603 err = buildResources(bundle, assets);
1604 if (err != 0) {
1605 goto bail;
1606 }
1607 }
1608
1609 // At this point we've read everything and processed everything. From here
1610 // on out it's just writing output files.
1611 if (SourcePos::hasErrors()) {
1612 goto bail;
1613 }
1614
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001615 // If we've been asked to generate a dependency file, do that here
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001616 if (bundle->getGenDependencies()) {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001617 // If this is the packaging step, generate the dependency file next to
1618 // the output apk (e.g. bin/resources.ap_.d)
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001619 if (outputAPKFile) {
1620 dependencyFile = String8(outputAPKFile);
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001621 // Add the .d extension to the dependency file.
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001622 dependencyFile.append(".d");
1623 } else {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001624 // Else if this is the R.java dependency generation step,
1625 // generate the dependency file in the R.java package subdirectory
1626 // e.g. gen/com/foo/app/R.java.d
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001627 dependencyFile = String8(bundle->getRClassDir());
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001628 dependencyFile.appendPath("R.java.d");
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001629 }
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001630 // Make sure we have a clean dependency file to start with
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001631 fp = fopen(dependencyFile, "w");
1632 fclose(fp);
1633 }
1634
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001635 // Write out R.java constants
1636 if (assets->getPackage() == assets->getSymbolsPrivatePackage()) {
Xavier Ducrohet63459ad2009-11-30 18:05:10 -08001637 if (bundle->getCustomPackage() == NULL) {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001638 // Write the R.java file into the appropriate class directory
1639 // e.g. gen/com/foo/app/R.java
Xavier Ducrohet63459ad2009-11-30 18:05:10 -08001640 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001641 // If we have library files, we're going to write our R.java file into
1642 // the appropriate class directory for those libraries as well.
1643 // e.g. gen/com/foo/app/lib/R.java
Josiah Gaskince89f152011-06-08 19:31:40 -07001644 if (bundle->getExtraPackages() != NULL) {
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001645 // Split on colon
Josiah Gaskince89f152011-06-08 19:31:40 -07001646 String8 libs(bundle->getExtraPackages());
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001647 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
Josiah Gaskince89f152011-06-08 19:31:40 -07001648 while (packageString != NULL) {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001649 // Write the R.java file out with the correct package name
Josiah Gaskince89f152011-06-08 19:31:40 -07001650 err = writeResourceSymbols(bundle, assets, String8(packageString), true);
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001651 packageString = strtok(NULL, ":");
Josiah Gaskince89f152011-06-08 19:31:40 -07001652 }
1653 libs.unlockBuffer();
1654 }
Xavier Ducrohet63459ad2009-11-30 18:05:10 -08001655 } else {
1656 const String8 customPkg(bundle->getCustomPackage());
1657 err = writeResourceSymbols(bundle, assets, customPkg, true);
1658 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001659 if (err < 0) {
1660 goto bail;
1661 }
1662 } else {
1663 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
1664 if (err < 0) {
1665 goto bail;
1666 }
1667 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
1668 if (err < 0) {
1669 goto bail;
1670 }
1671 }
1672
Joe Onorato1553c822009-08-30 13:36:22 -07001673 // Write out the ProGuard file
1674 err = writeProguardFile(bundle, assets);
1675 if (err < 0) {
1676 goto bail;
1677 }
1678
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001679 // Write the apk
1680 if (outputAPKFile) {
1681 err = writeAPK(bundle, assets, String8(outputAPKFile));
1682 if (err != NO_ERROR) {
1683 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
1684 goto bail;
1685 }
1686 }
1687
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001688 // If we've been asked to generate a dependency file, we need to finish up here.
1689 // the writeResourceSymbols and writeAPK functions have already written the target
1690 // half of the dependency file, now we need to write the prerequisites. (files that
1691 // the R.java file or .ap_ file depend on)
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001692 if (bundle->getGenDependencies()) {
1693 // Now that writeResourceSymbols or writeAPK has taken care of writing
1694 // the targets to our dependency file, we'll write the prereqs
1695 fp = fopen(dependencyFile, "a+");
1696 fprintf(fp, " : ");
1697 bool includeRaw = (outputAPKFile != NULL);
1698 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001699 // Also manually add the AndroidManifeset since it's not under res/ or assets/
1700 // and therefore was not added to our pathstores during slurping
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001701 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
1702 fclose(fp);
1703 }
1704
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001705 retVal = 0;
1706bail:
1707 if (SourcePos::hasErrors()) {
1708 SourcePos::printErrors(stderr);
1709 }
1710 return retVal;
1711}
Josiah Gaskin8a39da82011-06-06 17:00:35 -07001712
1713/*
1714 * Do PNG Crunching
1715 * PRECONDITIONS
1716 * -S flag points to a source directory containing drawable* folders
1717 * -C flag points to destination directory. The folder structure in the
1718 * source directory will be mirrored to the destination (cache) directory
1719 *
1720 * POSTCONDITIONS
1721 * Destination directory will be updated to match the PNG files in
1722 * the source directory.
1723 */
1724int doCrunch(Bundle* bundle)
1725{
1726 fprintf(stdout, "Crunching PNG Files in ");
1727 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
1728 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
1729
1730 updatePreProcessedCache(bundle);
1731
1732 return NO_ERROR;
1733}
Dan Morrille74763e2012-01-06 10:47:10 -08001734
1735char CONSOLE_DATA[2925] = {
1736 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1737 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1738 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1739 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1740 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
1741 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
1742 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1743 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1744 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
1745 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
1746 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1747 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1748 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
1749 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1750 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1751 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
1752 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
1753 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1754 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1755 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
1756 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
1757 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
1758 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
1759 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
1760 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1761 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1762 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
1763 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
1764 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
1765 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1766 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
1767 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1768 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1769 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
1770 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
1771 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1772 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1773 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
1774 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
1775 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
1776 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1777 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
1778 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1779 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1780 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
1781 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
1782 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
1783 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
1784 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
1785 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1786 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
1787 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
1788 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
1789 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1790 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
1791 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
1792 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
1793 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
1794 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
1795 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
1796 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
1797 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
1798 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
1799 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
1800 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
1801 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
1802 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
1803 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
1804 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
1805 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
1806 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
1807 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
1808 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1809 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
1810 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
1811 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
1812 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1813 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
1814 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
1815 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1816 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
1817 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
1818 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
1819 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1820 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
1821 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
1822 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1823 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
1824 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
1825 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1826 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1827 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
1828 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
1829 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1830 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1831 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
1832 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1833 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
1834 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
1835 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
1836 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1837 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1838 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
1839 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
1840 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
1841 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1842 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
1843 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1844 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1845 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
1846 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
1847 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1848 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1849 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
1850 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1851 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
1852 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
1853 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
1854 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1855 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1856 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
1857 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
1858 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
1859 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
1860 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
1861 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
1862 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
1863 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
1864 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
1865 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1866 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1867 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
1868 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
1869 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
1870 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
1871 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
1872 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1873 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1874 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
1875 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
1876 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
1877 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1878 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
1879 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1880 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1881 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
1882 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
1883 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1884 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1885 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
1886 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
1887 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1888 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
1889 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
1890 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1891 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1892 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
1893 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
1894 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1895 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1896 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1897 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1898 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
1899 };