blob: 198fce40ec8c4204c2d9be04edb74785a7f75791 [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
Dianne Hackborn6c997a92012-01-31 11:27:43 -0800482
483 } else if (strcmp("strings", option) == 0) {
484 const ResStringPool* pool = res.getTableStringBlock(0);
485 printStringPool(pool);
486
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800487 } else if (strcmp("xmltree", option) == 0) {
488 if (bundle->getFileSpecCount() < 3) {
489 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
490 goto bail;
491 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700492
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800493 for (int i=2; i<bundle->getFileSpecCount(); i++) {
494 const char* resname = bundle->getFileSpecEntry(i);
495 ResXMLTree tree;
496 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
497 if (asset == NULL) {
Kenny Root44b283d2009-09-01 19:03:11 -0500498 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800499 goto bail;
500 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700501
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800502 if (tree.setTo(asset->getBuffer(true),
503 asset->getLength()) != NO_ERROR) {
504 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
505 goto bail;
506 }
507 tree.restart();
508 printXMLBlock(&tree);
Kenny Root19138462009-12-04 09:38:48 -0800509 tree.uninit();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800510 delete asset;
511 asset = NULL;
512 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700513
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800514 } else if (strcmp("xmlstrings", option) == 0) {
515 if (bundle->getFileSpecCount() < 3) {
516 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
517 goto bail;
518 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700519
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800520 for (int i=2; i<bundle->getFileSpecCount(); i++) {
521 const char* resname = bundle->getFileSpecEntry(i);
522 ResXMLTree tree;
523 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
524 if (asset == NULL) {
Kenny Root44b283d2009-09-01 19:03:11 -0500525 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800526 goto bail;
527 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700528
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800529 if (tree.setTo(asset->getBuffer(true),
530 asset->getLength()) != NO_ERROR) {
531 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
532 goto bail;
533 }
534 printStringPool(&tree.getStrings());
535 delete asset;
536 asset = NULL;
537 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700538
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800539 } else {
540 ResXMLTree tree;
541 asset = assets.openNonAsset("AndroidManifest.xml",
542 Asset::ACCESS_BUFFER);
543 if (asset == NULL) {
544 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
545 goto bail;
546 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700547
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800548 if (tree.setTo(asset->getBuffer(true),
549 asset->getLength()) != NO_ERROR) {
550 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
551 goto bail;
552 }
553 tree.restart();
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700554
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800555 if (strcmp("permissions", option) == 0) {
556 size_t len;
557 ResXMLTree::event_code_t code;
558 int depth = 0;
559 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
560 if (code == ResXMLTree::END_TAG) {
561 depth--;
562 continue;
563 }
564 if (code != ResXMLTree::START_TAG) {
565 continue;
566 }
567 depth++;
568 String8 tag(tree.getElementName(&len));
569 //printf("Depth %d tag %s\n", depth, tag.string());
570 if (depth == 1) {
571 if (tag != "manifest") {
572 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
573 goto bail;
574 }
575 String8 pkg = getAttribute(tree, NULL, "package", NULL);
576 printf("package: %s\n", pkg.string());
577 } else if (depth == 2 && tag == "permission") {
578 String8 error;
579 String8 name = getAttribute(tree, NAME_ATTR, &error);
580 if (error != "") {
581 fprintf(stderr, "ERROR: %s\n", error.string());
582 goto bail;
583 }
584 printf("permission: %s\n", name.string());
585 } else if (depth == 2 && tag == "uses-permission") {
586 String8 error;
587 String8 name = getAttribute(tree, NAME_ATTR, &error);
588 if (error != "") {
589 fprintf(stderr, "ERROR: %s\n", error.string());
590 goto bail;
591 }
592 printf("uses-permission: %s\n", name.string());
593 }
594 }
595 } else if (strcmp("badging", option) == 0) {
Dianne Hackborne289bff2011-06-13 19:33:22 -0700596 Vector<String8> locales;
597 res.getLocales(&locales);
598
599 Vector<ResTable_config> configs;
600 res.getConfigurations(&configs);
601 SortedVector<int> densities;
602 const size_t NC = configs.size();
603 for (size_t i=0; i<NC; i++) {
604 int dens = configs[i].density;
605 if (dens == 0) dens = 160;
606 densities.add(dens);
607 }
608
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800609 size_t len;
610 ResXMLTree::event_code_t code;
611 int depth = 0;
612 String8 error;
613 bool withinActivity = false;
614 bool isMainActivity = false;
615 bool isLauncherActivity = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700616 bool isSearchable = false;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700617 bool withinApplication = false;
618 bool withinReceiver = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700619 bool withinService = false;
620 bool withinIntentFilter = false;
621 bool hasMainActivity = false;
622 bool hasOtherActivities = false;
623 bool hasOtherReceivers = false;
624 bool hasOtherServices = false;
625 bool hasWallpaperService = false;
626 bool hasImeService = false;
627 bool hasWidgetReceivers = false;
628 bool hasIntentFilter = false;
629 bool actMainActivity = false;
630 bool actWidgetReceivers = false;
631 bool actImeService = false;
632 bool actWallpaperService = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700633
Kenny Root063a44e2011-12-08 08:46:03 -0800634 // These two implement the implicit permissions that are granted
635 // to pre-1.6 applications.
636 bool hasWriteExternalStoragePermission = false;
637 bool hasReadPhoneStatePermission = false;
638
Dianne Hackborn79245122012-03-12 10:51:26 -0700639 // If an app requests write storage, they will also get read storage.
640 bool hasReadExternalStoragePermission = false;
641
Dan Morrill89d97c12010-05-03 16:13:14 -0700642 // This next group of variables is used to implement a group of
643 // backward-compatibility heuristics necessitated by the addition of
644 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
645 // heuristic is "if an app requests a permission but doesn't explicitly
646 // request the corresponding <uses-feature>, presume it's there anyway".
647 bool specCameraFeature = false; // camera-related
648 bool specCameraAutofocusFeature = false;
649 bool reqCameraAutofocusFeature = false;
650 bool reqCameraFlashFeature = false;
Dianne Hackborne5276a72009-08-27 16:28:44 -0700651 bool hasCameraPermission = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700652 bool specLocationFeature = false; // location-related
653 bool specNetworkLocFeature = false;
654 bool reqNetworkLocFeature = false;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800655 bool specGpsFeature = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700656 bool reqGpsFeature = false;
657 bool hasMockLocPermission = false;
658 bool hasCoarseLocPermission = false;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800659 bool hasGpsPermission = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700660 bool hasGeneralLocPermission = false;
661 bool specBluetoothFeature = false; // Bluetooth API-related
662 bool hasBluetoothPermission = false;
663 bool specMicrophoneFeature = false; // microphone-related
664 bool hasRecordAudioPermission = false;
665 bool specWiFiFeature = false;
666 bool hasWiFiPermission = false;
667 bool specTelephonyFeature = false; // telephony-related
668 bool reqTelephonySubFeature = false;
669 bool hasTelephonyPermission = false;
670 bool specTouchscreenFeature = false; // touchscreen-related
671 bool specMultitouchFeature = false;
672 bool reqDistinctMultitouchFeature = false;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700673 bool specScreenPortraitFeature = false;
674 bool specScreenLandscapeFeature = false;
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -0700675 bool reqScreenPortraitFeature = false;
676 bool reqScreenLandscapeFeature = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700677 // 2.2 also added some other features that apps can request, but that
678 // have no corresponding permission, so we cannot implement any
679 // back-compatibility heuristic for them. The below are thus unnecessary
680 // (but are retained here for documentary purposes.)
681 //bool specCompassFeature = false;
682 //bool specAccelerometerFeature = false;
683 //bool specProximityFeature = false;
684 //bool specAmbientLightFeature = false;
685 //bool specLiveWallpaperFeature = false;
686
Dianne Hackborn723738c2009-06-25 19:48:04 -0700687 int targetSdk = 0;
688 int smallScreen = 1;
689 int normalScreen = 1;
690 int largeScreen = 1;
Dianne Hackbornf43489d2010-08-20 12:44:33 -0700691 int xlargeScreen = 1;
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700692 int anyDensity = 1;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700693 int requiresSmallestWidthDp = 0;
694 int compatibleWidthLimitDp = 0;
695 int largestWidthLimitDp = 0;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700696 String8 pkg;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800697 String8 activityName;
698 String8 activityLabel;
699 String8 activityIcon;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700700 String8 receiverName;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700701 String8 serviceName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800702 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
703 if (code == ResXMLTree::END_TAG) {
704 depth--;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700705 if (depth < 2) {
706 withinApplication = false;
707 } else if (depth < 3) {
708 if (withinActivity && isMainActivity && isLauncherActivity) {
709 const char *aName = getComponentName(pkg, activityName);
Dianne Hackborne289bff2011-06-13 19:33:22 -0700710 printf("launchable-activity:");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700711 if (aName != NULL) {
Dianne Hackborne289bff2011-06-13 19:33:22 -0700712 printf(" name='%s' ", aName);
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700713 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700714 printf(" label='%s' icon='%s'\n",
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700715 activityLabel.string(),
716 activityIcon.string());
717 }
718 if (!hasIntentFilter) {
719 hasOtherActivities |= withinActivity;
720 hasOtherReceivers |= withinReceiver;
721 hasOtherServices |= withinService;
722 }
723 withinActivity = false;
724 withinService = false;
725 withinReceiver = false;
726 hasIntentFilter = false;
727 isMainActivity = isLauncherActivity = false;
728 } else if (depth < 4) {
729 if (withinIntentFilter) {
730 if (withinActivity) {
731 hasMainActivity |= actMainActivity;
732 hasOtherActivities |= !actMainActivity;
733 } else if (withinReceiver) {
734 hasWidgetReceivers |= actWidgetReceivers;
735 hasOtherReceivers |= !actWidgetReceivers;
736 } else if (withinService) {
737 hasImeService |= actImeService;
738 hasWallpaperService |= actWallpaperService;
739 hasOtherServices |= (!actImeService && !actWallpaperService);
740 }
741 }
742 withinIntentFilter = false;
743 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800744 continue;
745 }
746 if (code != ResXMLTree::START_TAG) {
747 continue;
748 }
749 depth++;
750 String8 tag(tree.getElementName(&len));
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700751 //printf("Depth %d, %s\n", depth, tag.string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800752 if (depth == 1) {
753 if (tag != "manifest") {
754 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
755 goto bail;
756 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700757 pkg = getAttribute(tree, NULL, "package", NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800758 printf("package: name='%s' ", pkg.string());
759 int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
760 if (error != "") {
761 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
762 goto bail;
763 }
764 if (versionCode > 0) {
765 printf("versionCode='%d' ", versionCode);
766 } else {
767 printf("versionCode='' ");
768 }
Dianne Hackborncf244ad2010-03-09 15:00:30 -0800769 String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800770 if (error != "") {
771 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
772 goto bail;
773 }
774 printf("versionName='%s'\n", versionName.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700775 } else if (depth == 2) {
776 withinApplication = false;
777 if (tag == "application") {
778 withinApplication = true;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700779
780 String8 label;
781 const size_t NL = locales.size();
782 for (size_t i=0; i<NL; i++) {
783 const char* localeStr = locales[i].string();
784 assets.setLocale(localeStr != NULL ? localeStr : "");
785 String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
786 if (llabel != "") {
787 if (localeStr == NULL || strlen(localeStr) == 0) {
788 label = llabel;
789 printf("application-label:'%s'\n", llabel.string());
790 } else {
791 if (label == "") {
792 label = llabel;
793 }
794 printf("application-label-%s:'%s'\n", localeStr,
795 llabel.string());
796 }
797 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700798 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700799
800 ResTable_config tmpConfig = config;
801 const size_t ND = densities.size();
802 for (size_t i=0; i<ND; i++) {
803 tmpConfig.density = densities[i];
804 assets.setConfiguration(tmpConfig);
805 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
806 if (icon != "") {
807 printf("application-icon-%d:'%s'\n", densities[i], icon.string());
808 }
809 }
810 assets.setConfiguration(config);
811
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700812 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
813 if (error != "") {
814 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
815 goto bail;
816 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700817 int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700818 if (error != "") {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700819 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700820 goto bail;
821 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700822 printf("application: label='%s' ", label.string());
823 printf("icon='%s'\n", icon.string());
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700824 if (testOnly != 0) {
825 printf("testOnly='%d'\n", testOnly);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700826 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700827 } else if (tag == "uses-sdk") {
828 int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
829 if (error != "") {
830 error = "";
831 String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
832 if (error != "") {
833 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
834 error.string());
835 goto bail;
836 }
Dianne Hackborn723738c2009-06-25 19:48:04 -0700837 if (name == "Donut") targetSdk = 4;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700838 printf("sdkVersion:'%s'\n", name.string());
839 } else if (code != -1) {
Dianne Hackborn723738c2009-06-25 19:48:04 -0700840 targetSdk = code;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700841 printf("sdkVersion:'%d'\n", code);
842 }
Suchi Amalapurapu75c49842009-08-14 15:13:09 -0700843 code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
844 if (code != -1) {
845 printf("maxSdkVersion:'%d'\n", code);
846 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700847 code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
848 if (error != "") {
849 error = "";
850 String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
851 if (error != "") {
852 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
853 error.string());
854 goto bail;
855 }
Dianne Hackborn723738c2009-06-25 19:48:04 -0700856 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700857 printf("targetSdkVersion:'%s'\n", name.string());
858 } else if (code != -1) {
Dianne Hackborn723738c2009-06-25 19:48:04 -0700859 if (targetSdk < code) {
860 targetSdk = code;
861 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700862 printf("targetSdkVersion:'%d'\n", code);
863 }
864 } else if (tag == "uses-configuration") {
865 int32_t reqTouchScreen = getIntegerAttribute(tree,
866 REQ_TOUCH_SCREEN_ATTR, NULL, 0);
867 int32_t reqKeyboardType = getIntegerAttribute(tree,
868 REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
869 int32_t reqHardKeyboard = getIntegerAttribute(tree,
870 REQ_HARD_KEYBOARD_ATTR, NULL, 0);
871 int32_t reqNavigation = getIntegerAttribute(tree,
872 REQ_NAVIGATION_ATTR, NULL, 0);
873 int32_t reqFiveWayNav = getIntegerAttribute(tree,
874 REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
Dianne Hackborncb2d50d2010-01-06 11:29:54 -0800875 printf("uses-configuration:");
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700876 if (reqTouchScreen != 0) {
877 printf(" reqTouchScreen='%d'", reqTouchScreen);
878 }
879 if (reqKeyboardType != 0) {
880 printf(" reqKeyboardType='%d'", reqKeyboardType);
881 }
882 if (reqHardKeyboard != 0) {
883 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
884 }
885 if (reqNavigation != 0) {
886 printf(" reqNavigation='%d'", reqNavigation);
887 }
888 if (reqFiveWayNav != 0) {
889 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
890 }
891 printf("\n");
Dianne Hackborn723738c2009-06-25 19:48:04 -0700892 } else if (tag == "supports-screens") {
893 smallScreen = getIntegerAttribute(tree,
894 SMALL_SCREEN_ATTR, NULL, 1);
895 normalScreen = getIntegerAttribute(tree,
896 NORMAL_SCREEN_ATTR, NULL, 1);
897 largeScreen = getIntegerAttribute(tree,
898 LARGE_SCREEN_ATTR, NULL, 1);
Dianne Hackbornf43489d2010-08-20 12:44:33 -0700899 xlargeScreen = getIntegerAttribute(tree,
900 XLARGE_SCREEN_ATTR, NULL, 1);
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700901 anyDensity = getIntegerAttribute(tree,
902 ANY_DENSITY_ATTR, NULL, 1);
Dianne Hackborne289bff2011-06-13 19:33:22 -0700903 requiresSmallestWidthDp = getIntegerAttribute(tree,
904 REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
905 compatibleWidthLimitDp = getIntegerAttribute(tree,
906 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
907 largestWidthLimitDp = getIntegerAttribute(tree,
908 LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
Dianne Hackborne5276a72009-08-27 16:28:44 -0700909 } else if (tag == "uses-feature") {
910 String8 name = getAttribute(tree, NAME_ATTR, &error);
Suchi Amalapurapu40b94722009-09-20 13:39:37 -0700911
912 if (name != "" && error == "") {
Dianne Hackborne5276a72009-08-27 16:28:44 -0700913 int req = getIntegerAttribute(tree,
914 REQUIRED_ATTR, NULL, 1);
Dan Morrill89d97c12010-05-03 16:13:14 -0700915
Dianne Hackborne5276a72009-08-27 16:28:44 -0700916 if (name == "android.hardware.camera") {
917 specCameraFeature = true;
Dan Morrill89d97c12010-05-03 16:13:14 -0700918 } else if (name == "android.hardware.camera.autofocus") {
919 // these have no corresponding permission to check for,
920 // but should imply the foundational camera permission
921 reqCameraAutofocusFeature = reqCameraAutofocusFeature || req;
922 specCameraAutofocusFeature = true;
923 } else if (req && (name == "android.hardware.camera.flash")) {
924 // these have no corresponding permission to check for,
925 // but should imply the foundational camera permission
926 reqCameraFlashFeature = true;
927 } else if (name == "android.hardware.location") {
928 specLocationFeature = true;
929 } else if (name == "android.hardware.location.network") {
930 specNetworkLocFeature = true;
931 reqNetworkLocFeature = reqNetworkLocFeature || req;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800932 } else if (name == "android.hardware.location.gps") {
933 specGpsFeature = true;
Dan Morrill89d97c12010-05-03 16:13:14 -0700934 reqGpsFeature = reqGpsFeature || req;
935 } else if (name == "android.hardware.bluetooth") {
936 specBluetoothFeature = true;
937 } else if (name == "android.hardware.touchscreen") {
938 specTouchscreenFeature = true;
939 } else if (name == "android.hardware.touchscreen.multitouch") {
940 specMultitouchFeature = true;
941 } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
942 reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req;
943 } else if (name == "android.hardware.microphone") {
944 specMicrophoneFeature = true;
945 } else if (name == "android.hardware.wifi") {
946 specWiFiFeature = true;
947 } else if (name == "android.hardware.telephony") {
948 specTelephonyFeature = true;
949 } else if (req && (name == "android.hardware.telephony.gsm" ||
950 name == "android.hardware.telephony.cdma")) {
951 // these have no corresponding permission to check for,
952 // but should imply the foundational telephony permission
953 reqTelephonySubFeature = true;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700954 } else if (name == "android.hardware.screen.portrait") {
955 specScreenPortraitFeature = true;
956 } else if (name == "android.hardware.screen.landscape") {
957 specScreenLandscapeFeature = true;
Dianne Hackborne5276a72009-08-27 16:28:44 -0700958 }
959 printf("uses-feature%s:'%s'\n",
960 req ? "" : "-not-required", name.string());
961 } else {
962 int vers = getIntegerAttribute(tree,
963 GL_ES_VERSION_ATTR, &error);
964 if (error == "") {
965 printf("uses-gl-es:'0x%x'\n", vers);
966 }
967 }
968 } else if (tag == "uses-permission") {
969 String8 name = getAttribute(tree, NAME_ATTR, &error);
Suchi Amalapurapu40b94722009-09-20 13:39:37 -0700970 if (name != "" && error == "") {
Dianne Hackborne5276a72009-08-27 16:28:44 -0700971 if (name == "android.permission.CAMERA") {
972 hasCameraPermission = true;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800973 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
974 hasGpsPermission = true;
Dan Morrill89d97c12010-05-03 16:13:14 -0700975 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
976 hasMockLocPermission = true;
977 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
978 hasCoarseLocPermission = true;
979 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
980 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
981 hasGeneralLocPermission = true;
982 } else if (name == "android.permission.BLUETOOTH" ||
983 name == "android.permission.BLUETOOTH_ADMIN") {
984 hasBluetoothPermission = true;
985 } else if (name == "android.permission.RECORD_AUDIO") {
986 hasRecordAudioPermission = true;
987 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
988 name == "android.permission.CHANGE_WIFI_STATE" ||
989 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
990 hasWiFiPermission = true;
991 } else if (name == "android.permission.CALL_PHONE" ||
992 name == "android.permission.CALL_PRIVILEGED" ||
993 name == "android.permission.MODIFY_PHONE_STATE" ||
994 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
995 name == "android.permission.READ_SMS" ||
996 name == "android.permission.RECEIVE_SMS" ||
997 name == "android.permission.RECEIVE_MMS" ||
998 name == "android.permission.RECEIVE_WAP_PUSH" ||
999 name == "android.permission.SEND_SMS" ||
1000 name == "android.permission.WRITE_APN_SETTINGS" ||
1001 name == "android.permission.WRITE_SMS") {
1002 hasTelephonyPermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001003 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1004 hasWriteExternalStoragePermission = true;
Dianne Hackborn79245122012-03-12 10:51:26 -07001005 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1006 hasReadExternalStoragePermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001007 } else if (name == "android.permission.READ_PHONE_STATE") {
1008 hasReadPhoneStatePermission = true;
Dianne Hackborne5276a72009-08-27 16:28:44 -07001009 }
1010 printf("uses-permission:'%s'\n", name.string());
1011 } else {
1012 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1013 error.string());
1014 goto bail;
1015 }
Dianne Hackborn43b68032010-09-02 17:14:41 -07001016 } else if (tag == "uses-package") {
1017 String8 name = getAttribute(tree, NAME_ATTR, &error);
1018 if (name != "" && error == "") {
1019 printf("uses-package:'%s'\n", name.string());
1020 } else {
1021 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1022 error.string());
1023 goto bail;
1024 }
Jeff Hamiltone2c17f92010-02-12 13:45:16 -06001025 } else if (tag == "original-package") {
1026 String8 name = getAttribute(tree, NAME_ATTR, &error);
1027 if (name != "" && error == "") {
1028 printf("original-package:'%s'\n", name.string());
1029 } else {
1030 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1031 error.string());
1032 goto bail;
1033 }
Dan Morrill096b67f2010-12-13 16:25:54 -08001034 } else if (tag == "supports-gl-texture") {
Dan Morrill6f51fc12010-10-13 14:33:43 -07001035 String8 name = getAttribute(tree, NAME_ATTR, &error);
1036 if (name != "" && error == "") {
Dan Morrill096b67f2010-12-13 16:25:54 -08001037 printf("supports-gl-texture:'%s'\n", name.string());
Dan Morrill6f51fc12010-10-13 14:33:43 -07001038 } else {
1039 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1040 error.string());
1041 goto bail;
1042 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001043 } else if (tag == "compatible-screens") {
1044 printCompatibleScreens(tree);
1045 depth--;
Kenny Root56088a52011-09-29 13:49:45 -07001046 } else if (tag == "package-verifier") {
1047 String8 name = getAttribute(tree, NAME_ATTR, &error);
1048 if (name != "" && error == "") {
1049 String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
1050 if (publicKey != "" && error == "") {
1051 printf("package-verifier: name='%s' publicKey='%s'\n",
1052 name.string(), publicKey.string());
1053 }
1054 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001055 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001056 } else if (depth == 3 && withinApplication) {
1057 withinActivity = false;
1058 withinReceiver = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001059 withinService = false;
1060 hasIntentFilter = false;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001061 if(tag == "activity") {
1062 withinActivity = true;
1063 activityName = getAttribute(tree, NAME_ATTR, &error);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001064 if (error != "") {
1065 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1066 goto bail;
1067 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001068
1069 activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001070 if (error != "") {
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001071 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001072 goto bail;
1073 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001074
1075 activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1076 if (error != "") {
1077 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
1078 goto bail;
1079 }
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001080
1081 int32_t orien = getResolvedIntegerAttribute(&res, tree,
1082 SCREEN_ORIENTATION_ATTR, &error);
1083 if (error == "") {
1084 if (orien == 0 || orien == 6 || orien == 8) {
1085 // Requests landscape, sensorLandscape, or reverseLandscape.
1086 reqScreenLandscapeFeature = true;
1087 } else if (orien == 1 || orien == 7 || orien == 9) {
1088 // Requests portrait, sensorPortrait, or reversePortrait.
1089 reqScreenPortraitFeature = true;
1090 }
1091 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001092 } else if (tag == "uses-library") {
1093 String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1094 if (error != "") {
1095 fprintf(stderr, "ERROR getting 'android:name' attribute for uses-library: %s\n", error.string());
1096 goto bail;
1097 }
Dianne Hackborn49237342009-08-27 20:08:01 -07001098 int req = getIntegerAttribute(tree,
1099 REQUIRED_ATTR, NULL, 1);
1100 printf("uses-library%s:'%s'\n",
1101 req ? "" : "-not-required", libraryName.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001102 } else if (tag == "receiver") {
1103 withinReceiver = true;
1104 receiverName = getAttribute(tree, NAME_ATTR, &error);
1105
1106 if (error != "") {
1107 fprintf(stderr, "ERROR getting 'android:name' attribute for receiver: %s\n", error.string());
1108 goto bail;
1109 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001110 } else if (tag == "service") {
1111 withinService = true;
1112 serviceName = getAttribute(tree, NAME_ATTR, &error);
1113
1114 if (error != "") {
1115 fprintf(stderr, "ERROR getting 'android:name' attribute for service: %s\n", error.string());
1116 goto bail;
1117 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001118 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001119 } else if ((depth == 4) && (tag == "intent-filter")) {
1120 hasIntentFilter = true;
1121 withinIntentFilter = true;
1122 actMainActivity = actWidgetReceivers = actImeService = actWallpaperService = false;
1123 } else if ((depth == 5) && withinIntentFilter){
1124 String8 action;
1125 if (tag == "action") {
1126 action = getAttribute(tree, NAME_ATTR, &error);
1127 if (error != "") {
1128 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1129 goto bail;
1130 }
1131 if (withinActivity) {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001132 if (action == "android.intent.action.MAIN") {
1133 isMainActivity = true;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001134 actMainActivity = true;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001135 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001136 } else if (withinReceiver) {
1137 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1138 actWidgetReceivers = true;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001139 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001140 } else if (withinService) {
1141 if (action == "android.view.InputMethod") {
1142 actImeService = true;
1143 } else if (action == "android.service.wallpaper.WallpaperService") {
1144 actWallpaperService = true;
1145 }
1146 }
1147 if (action == "android.intent.action.SEARCH") {
1148 isSearchable = true;
1149 }
1150 }
1151
1152 if (tag == "category") {
1153 String8 category = getAttribute(tree, NAME_ATTR, &error);
1154 if (error != "") {
1155 fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string());
1156 goto bail;
1157 }
1158 if (withinActivity) {
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001159 if (category == "android.intent.category.LAUNCHER") {
1160 isLauncherActivity = true;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001161 }
1162 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001163 }
1164 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001165 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001166
Kenny Root063a44e2011-12-08 08:46:03 -08001167 // Pre-1.6 implicitly granted permission compatibility logic
1168 if (targetSdk < 4) {
1169 if (!hasWriteExternalStoragePermission) {
1170 printf("uses-permission:'android.permission.WRITE_EXTERNAL_STORAGE'\n");
Dianne Hackborn79245122012-03-12 10:51:26 -07001171 hasWriteExternalStoragePermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001172 }
1173 if (!hasReadPhoneStatePermission) {
1174 printf("uses-permission:'android.permission.READ_PHONE_STATE'\n");
1175 }
1176 }
1177
Dianne Hackborn79245122012-03-12 10:51:26 -07001178 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1179 // force them to always take READ_EXTERNAL_STORAGE as well.
1180 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
1181 printf("uses-permission:'android.permission.READ_EXTERNAL_STORAGE'\n");
1182 }
1183
Dan Morrill89d97c12010-05-03 16:13:14 -07001184 /* The following blocks handle printing "inferred" uses-features, based
1185 * on whether related features or permissions are used by the app.
1186 * Note that the various spec*Feature variables denote whether the
1187 * relevant tag was *present* in the AndroidManfest, not that it was
1188 * present and set to true.
1189 */
1190 // Camera-related back-compatibility logic
1191 if (!specCameraFeature) {
1192 if (reqCameraFlashFeature || reqCameraAutofocusFeature) {
1193 // if app requested a sub-feature (autofocus or flash) and didn't
1194 // request the base camera feature, we infer that it meant to
1195 printf("uses-feature:'android.hardware.camera'\n");
1196 } else if (hasCameraPermission) {
1197 // if app wants to use camera but didn't request the feature, we infer
1198 // that it meant to, and further that it wants autofocus
1199 // (which was the 1.0 - 1.5 behavior)
1200 printf("uses-feature:'android.hardware.camera'\n");
1201 if (!specCameraAutofocusFeature) {
1202 printf("uses-feature:'android.hardware.camera.autofocus'\n");
1203 }
1204 }
Dianne Hackborne5276a72009-08-27 16:28:44 -07001205 }
Doug Zongkerdbe7a682009-10-09 11:24:51 -07001206
Dan Morrill89d97c12010-05-03 16:13:14 -07001207 // Location-related back-compatibility logic
1208 if (!specLocationFeature &&
1209 (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission ||
1210 hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) {
1211 // if app either takes a location-related permission or requests one of the
1212 // sub-features, we infer that it also meant to request the base location feature
1213 printf("uses-feature:'android.hardware.location'\n");
1214 }
Dianne Hackbornef05e072010-03-01 17:43:39 -08001215 if (!specGpsFeature && hasGpsPermission) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001216 // if app takes GPS (FINE location) perm but does not request the GPS
1217 // feature, we infer that it meant to
Dianne Hackbornef05e072010-03-01 17:43:39 -08001218 printf("uses-feature:'android.hardware.location.gps'\n");
1219 }
Dan Morrill89d97c12010-05-03 16:13:14 -07001220 if (!specNetworkLocFeature && hasCoarseLocPermission) {
1221 // if app takes Network location (COARSE location) perm but does not request the
1222 // network location feature, we infer that it meant to
1223 printf("uses-feature:'android.hardware.location.network'\n");
1224 }
1225
1226 // Bluetooth-related compatibility logic
Dan Morrill6b22d812010-06-15 21:41:42 -07001227 if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001228 // if app takes a Bluetooth permission but does not request the Bluetooth
1229 // feature, we infer that it meant to
1230 printf("uses-feature:'android.hardware.bluetooth'\n");
1231 }
1232
1233 // Microphone-related compatibility logic
1234 if (!specMicrophoneFeature && hasRecordAudioPermission) {
1235 // if app takes the record-audio permission but does not request the microphone
1236 // feature, we infer that it meant to
1237 printf("uses-feature:'android.hardware.microphone'\n");
1238 }
1239
1240 // WiFi-related compatibility logic
1241 if (!specWiFiFeature && hasWiFiPermission) {
1242 // if app takes one of the WiFi permissions but does not request the WiFi
1243 // feature, we infer that it meant to
1244 printf("uses-feature:'android.hardware.wifi'\n");
1245 }
1246
1247 // Telephony-related compatibility logic
1248 if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) {
1249 // if app takes one of the telephony permissions or requests a sub-feature but
1250 // does not request the base telephony feature, we infer that it meant to
1251 printf("uses-feature:'android.hardware.telephony'\n");
1252 }
1253
1254 // Touchscreen-related back-compatibility logic
1255 if (!specTouchscreenFeature) { // not a typo!
1256 // all apps are presumed to require a touchscreen, unless they explicitly say
1257 // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
1258 // Note that specTouchscreenFeature is true if the tag is present, regardless
1259 // of whether its value is true or false, so this is safe
1260 printf("uses-feature:'android.hardware.touchscreen'\n");
1261 }
1262 if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
1263 // if app takes one of the telephony permissions or requests a sub-feature but
1264 // does not request the base telephony feature, we infer that it meant to
1265 printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
1266 }
Dianne Hackbornef05e072010-03-01 17:43:39 -08001267
Dianne Hackborne289bff2011-06-13 19:33:22 -07001268 // Landscape/portrait-related compatibility logic
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001269 if (!specScreenLandscapeFeature && !specScreenPortraitFeature) {
1270 // If the app has specified any activities in its manifest
1271 // that request a specific orientation, then assume that
1272 // orientation is required.
1273 if (reqScreenLandscapeFeature) {
1274 printf("uses-feature:'android.hardware.screen.landscape'\n");
1275 }
1276 if (reqScreenPortraitFeature) {
1277 printf("uses-feature:'android.hardware.screen.portrait'\n");
1278 }
Dianne Hackborne289bff2011-06-13 19:33:22 -07001279 }
1280
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001281 if (hasMainActivity) {
1282 printf("main\n");
1283 }
1284 if (hasWidgetReceivers) {
1285 printf("app-widget\n");
1286 }
1287 if (hasImeService) {
1288 printf("ime\n");
1289 }
1290 if (hasWallpaperService) {
1291 printf("wallpaper\n");
1292 }
1293 if (hasOtherActivities) {
1294 printf("other-activities\n");
1295 }
1296 if (isSearchable) {
1297 printf("search\n");
1298 }
1299 if (hasOtherReceivers) {
1300 printf("other-receivers\n");
1301 }
1302 if (hasOtherServices) {
1303 printf("other-services\n");
1304 }
1305
Dianne Hackborne289bff2011-06-13 19:33:22 -07001306 // For modern apps, if screen size buckets haven't been specified
1307 // but the new width ranges have, then infer the buckets from them.
1308 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1309 && requiresSmallestWidthDp > 0) {
1310 int compatWidth = compatibleWidthLimitDp;
1311 if (compatWidth <= 0) compatWidth = requiresSmallestWidthDp;
1312 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1313 smallScreen = -1;
1314 } else {
1315 smallScreen = 0;
1316 }
1317 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1318 normalScreen = -1;
1319 } else {
1320 normalScreen = 0;
1321 }
1322 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1323 largeScreen = -1;
1324 } else {
1325 largeScreen = 0;
1326 }
1327 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1328 xlargeScreen = -1;
1329 } else {
1330 xlargeScreen = 0;
1331 }
1332 }
1333
Dianne Hackborn723738c2009-06-25 19:48:04 -07001334 // Determine default values for any unspecified screen sizes,
1335 // based on the target SDK of the package. As of 4 (donut)
1336 // the screen size support was introduced, so all default to
1337 // enabled.
1338 if (smallScreen > 0) {
1339 smallScreen = targetSdk >= 4 ? -1 : 0;
1340 }
1341 if (normalScreen > 0) {
1342 normalScreen = -1;
1343 }
1344 if (largeScreen > 0) {
1345 largeScreen = targetSdk >= 4 ? -1 : 0;
1346 }
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001347 if (xlargeScreen > 0) {
Scott Maind58fb972010-11-04 18:32:00 -07001348 // Introduced in Gingerbread.
1349 xlargeScreen = targetSdk >= 9 ? -1 : 0;
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001350 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001351 if (anyDensity > 0) {
Dianne Hackborne289bff2011-06-13 19:33:22 -07001352 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1353 || compatibleWidthLimitDp > 0) ? -1 : 0;
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001354 }
Dianne Hackborn723738c2009-06-25 19:48:04 -07001355 printf("supports-screens:");
1356 if (smallScreen != 0) printf(" 'small'");
1357 if (normalScreen != 0) printf(" 'normal'");
1358 if (largeScreen != 0) printf(" 'large'");
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001359 if (xlargeScreen != 0) printf(" 'xlarge'");
Dianne Hackborn723738c2009-06-25 19:48:04 -07001360 printf("\n");
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001361 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
Dianne Hackborne289bff2011-06-13 19:33:22 -07001362 if (requiresSmallestWidthDp > 0) {
1363 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1364 }
1365 if (compatibleWidthLimitDp > 0) {
1366 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1367 }
1368 if (largestWidthLimitDp > 0) {
1369 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1370 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001371
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001372 printf("locales:");
Dianne Hackborne17086b2009-06-19 15:13:28 -07001373 const size_t NL = locales.size();
1374 for (size_t i=0; i<NL; i++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001375 const char* localeStr = locales[i].string();
1376 if (localeStr == NULL || strlen(localeStr) == 0) {
1377 localeStr = "--_--";
1378 }
1379 printf(" '%s'", localeStr);
1380 }
1381 printf("\n");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001382
Dianne Hackborne17086b2009-06-19 15:13:28 -07001383 printf("densities:");
1384 const size_t ND = densities.size();
1385 for (size_t i=0; i<ND; i++) {
1386 printf(" '%d'", densities[i]);
1387 }
1388 printf("\n");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001389
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001390 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1391 if (dir != NULL) {
1392 if (dir->getFileCount() > 0) {
1393 printf("native-code:");
1394 for (size_t i=0; i<dir->getFileCount(); i++) {
1395 printf(" '%s'", dir->getFileName(i).string());
1396 }
1397 printf("\n");
1398 }
1399 delete dir;
1400 }
Dan Morrille74763e2012-01-06 10:47:10 -08001401 } else if (strcmp("badger", option) == 0) {
Dianne Hackborn6c997a92012-01-31 11:27:43 -08001402 printf("%s", CONSOLE_DATA);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001403 } else if (strcmp("configurations", option) == 0) {
1404 Vector<ResTable_config> configs;
1405 res.getConfigurations(&configs);
1406 const size_t N = configs.size();
1407 for (size_t i=0; i<N; i++) {
1408 printf("%s\n", configs[i].toString().string());
1409 }
1410 } else {
1411 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
1412 goto bail;
1413 }
1414 }
1415
1416 result = NO_ERROR;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001417
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001418bail:
1419 if (asset) {
1420 delete asset;
1421 }
1422 return (result != NO_ERROR);
1423}
1424
1425
1426/*
1427 * Handle the "add" command, which wants to add files to a new or
1428 * pre-existing archive.
1429 */
1430int doAdd(Bundle* bundle)
1431{
1432 ZipFile* zip = NULL;
1433 status_t result = UNKNOWN_ERROR;
1434 const char* zipFileName;
1435
1436 if (bundle->getUpdate()) {
1437 /* avoid confusion */
1438 fprintf(stderr, "ERROR: can't use '-u' with add\n");
1439 goto bail;
1440 }
1441
1442 if (bundle->getFileSpecCount() < 1) {
1443 fprintf(stderr, "ERROR: must specify zip file name\n");
1444 goto bail;
1445 }
1446 zipFileName = bundle->getFileSpecEntry(0);
1447
1448 if (bundle->getFileSpecCount() < 2) {
1449 fprintf(stderr, "NOTE: nothing to do\n");
1450 goto bail;
1451 }
1452
1453 zip = openReadWrite(zipFileName, true);
1454 if (zip == NULL) {
1455 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
1456 goto bail;
1457 }
1458
1459 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1460 const char* fileName = bundle->getFileSpecEntry(i);
1461
1462 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
1463 printf(" '%s'... (from gzip)\n", fileName);
1464 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
1465 } else {
Doug Zongkerdbe7a682009-10-09 11:24:51 -07001466 if (bundle->getJunkPath()) {
1467 String8 storageName = String8(fileName).getPathLeaf();
1468 printf(" '%s' as '%s'...\n", fileName, storageName.string());
1469 result = zip->add(fileName, storageName.string(),
1470 bundle->getCompressionMethod(), NULL);
1471 } else {
1472 printf(" '%s'...\n", fileName);
1473 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
1474 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001475 }
1476 if (result != NO_ERROR) {
1477 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
1478 if (result == NAME_NOT_FOUND)
1479 fprintf(stderr, ": file not found\n");
1480 else if (result == ALREADY_EXISTS)
1481 fprintf(stderr, ": already exists in archive\n");
1482 else
1483 fprintf(stderr, "\n");
1484 goto bail;
1485 }
1486 }
1487
1488 result = NO_ERROR;
1489
1490bail:
1491 delete zip;
1492 return (result != NO_ERROR);
1493}
1494
1495
1496/*
1497 * Delete files from an existing archive.
1498 */
1499int doRemove(Bundle* bundle)
1500{
1501 ZipFile* zip = NULL;
1502 status_t result = UNKNOWN_ERROR;
1503 const char* zipFileName;
1504
1505 if (bundle->getFileSpecCount() < 1) {
1506 fprintf(stderr, "ERROR: must specify zip file name\n");
1507 goto bail;
1508 }
1509 zipFileName = bundle->getFileSpecEntry(0);
1510
1511 if (bundle->getFileSpecCount() < 2) {
1512 fprintf(stderr, "NOTE: nothing to do\n");
1513 goto bail;
1514 }
1515
1516 zip = openReadWrite(zipFileName, false);
1517 if (zip == NULL) {
1518 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
1519 zipFileName);
1520 goto bail;
1521 }
1522
1523 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1524 const char* fileName = bundle->getFileSpecEntry(i);
1525 ZipEntry* entry;
1526
1527 entry = zip->getEntryByName(fileName);
1528 if (entry == NULL) {
1529 printf(" '%s' NOT FOUND\n", fileName);
1530 continue;
1531 }
1532
1533 result = zip->remove(entry);
1534
1535 if (result != NO_ERROR) {
1536 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
1537 bundle->getFileSpecEntry(i), zipFileName);
1538 goto bail;
1539 }
1540 }
1541
1542 /* update the archive */
1543 zip->flush();
1544
1545bail:
1546 delete zip;
1547 return (result != NO_ERROR);
1548}
1549
1550
1551/*
1552 * Package up an asset directory and associated application files.
1553 */
1554int doPackage(Bundle* bundle)
1555{
1556 const char* outputAPKFile;
1557 int retVal = 1;
1558 status_t err;
1559 sp<AaptAssets> assets;
1560 int N;
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001561 FILE* fp;
1562 String8 dependencyFile;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001563
1564 // -c zz_ZZ means do pseudolocalization
1565 ResourceFilter filter;
1566 err = filter.parse(bundle->getConfigurations());
1567 if (err != NO_ERROR) {
1568 goto bail;
1569 }
1570 if (filter.containsPseudo()) {
1571 bundle->setPseudolocalize(true);
1572 }
1573
1574 N = bundle->getFileSpecCount();
1575 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
1576 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDir() == NULL) {
1577 fprintf(stderr, "ERROR: no input files\n");
1578 goto bail;
1579 }
1580
1581 outputAPKFile = bundle->getOutputAPKFile();
1582
1583 // Make sure the filenames provided exist and are of the appropriate type.
1584 if (outputAPKFile) {
1585 FileType type;
1586 type = getFileType(outputAPKFile);
1587 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
1588 fprintf(stderr,
1589 "ERROR: output file '%s' exists but is not regular file\n",
1590 outputAPKFile);
1591 goto bail;
1592 }
1593 }
1594
1595 // Load the assets.
1596 assets = new AaptAssets();
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001597
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001598 // Set up the resource gathering in assets if we're going to generate
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001599 // dependency files. Every time we encounter a resource while slurping
1600 // the tree, we'll add it to these stores so we have full resource paths
1601 // to write to a dependency file.
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001602 if (bundle->getGenDependencies()) {
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001603 sp<FilePathStore> resPathStore = new FilePathStore;
1604 assets->setFullResPaths(resPathStore);
1605 sp<FilePathStore> assetPathStore = new FilePathStore;
1606 assets->setFullAssetPaths(assetPathStore);
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001607 }
1608
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001609 err = assets->slurpFromArgs(bundle);
1610 if (err < 0) {
1611 goto bail;
1612 }
1613
1614 if (bundle->getVerbose()) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001615 assets->print(String8());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001616 }
1617
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001618 // If they asked for any fileAs that need to be compiled, do so.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001619 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
1620 err = buildResources(bundle, assets);
1621 if (err != 0) {
1622 goto bail;
1623 }
1624 }
1625
1626 // At this point we've read everything and processed everything. From here
1627 // on out it's just writing output files.
1628 if (SourcePos::hasErrors()) {
1629 goto bail;
1630 }
1631
Dianne Hackborn1644c6d72012-02-06 15:33:21 -08001632 // Update symbols with information about which ones are needed as Java symbols.
1633 assets->applyJavaSymbols();
1634 if (SourcePos::hasErrors()) {
1635 goto bail;
1636 }
1637
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001638 // If we've been asked to generate a dependency file, do that here
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001639 if (bundle->getGenDependencies()) {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001640 // If this is the packaging step, generate the dependency file next to
1641 // the output apk (e.g. bin/resources.ap_.d)
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001642 if (outputAPKFile) {
1643 dependencyFile = String8(outputAPKFile);
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001644 // Add the .d extension to the dependency file.
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001645 dependencyFile.append(".d");
1646 } else {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001647 // Else if this is the R.java dependency generation step,
1648 // generate the dependency file in the R.java package subdirectory
1649 // e.g. gen/com/foo/app/R.java.d
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001650 dependencyFile = String8(bundle->getRClassDir());
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001651 dependencyFile.appendPath("R.java.d");
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001652 }
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001653 // Make sure we have a clean dependency file to start with
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001654 fp = fopen(dependencyFile, "w");
1655 fclose(fp);
1656 }
1657
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001658 // Write out R.java constants
Dianne Hackborn1644c6d72012-02-06 15:33:21 -08001659 if (!assets->havePrivateSymbols()) {
Xavier Ducrohet63459ad2009-11-30 18:05:10 -08001660 if (bundle->getCustomPackage() == NULL) {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001661 // Write the R.java file into the appropriate class directory
1662 // e.g. gen/com/foo/app/R.java
Xavier Ducrohet63459ad2009-11-30 18:05:10 -08001663 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001664 // If we have library files, we're going to write our R.java file into
1665 // the appropriate class directory for those libraries as well.
1666 // e.g. gen/com/foo/app/lib/R.java
Josiah Gaskince89f152011-06-08 19:31:40 -07001667 if (bundle->getExtraPackages() != NULL) {
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001668 // Split on colon
Josiah Gaskince89f152011-06-08 19:31:40 -07001669 String8 libs(bundle->getExtraPackages());
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001670 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
Josiah Gaskince89f152011-06-08 19:31:40 -07001671 while (packageString != NULL) {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001672 // Write the R.java file out with the correct package name
Josiah Gaskince89f152011-06-08 19:31:40 -07001673 err = writeResourceSymbols(bundle, assets, String8(packageString), true);
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001674 packageString = strtok(NULL, ":");
Josiah Gaskince89f152011-06-08 19:31:40 -07001675 }
1676 libs.unlockBuffer();
1677 }
Xavier Ducrohet63459ad2009-11-30 18:05:10 -08001678 } else {
1679 const String8 customPkg(bundle->getCustomPackage());
1680 err = writeResourceSymbols(bundle, assets, customPkg, true);
1681 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001682 if (err < 0) {
1683 goto bail;
1684 }
1685 } else {
1686 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
1687 if (err < 0) {
1688 goto bail;
1689 }
1690 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
1691 if (err < 0) {
1692 goto bail;
1693 }
1694 }
1695
Joe Onorato1553c822009-08-30 13:36:22 -07001696 // Write out the ProGuard file
1697 err = writeProguardFile(bundle, assets);
1698 if (err < 0) {
1699 goto bail;
1700 }
1701
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001702 // Write the apk
1703 if (outputAPKFile) {
1704 err = writeAPK(bundle, assets, String8(outputAPKFile));
1705 if (err != NO_ERROR) {
1706 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
1707 goto bail;
1708 }
1709 }
1710
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001711 // If we've been asked to generate a dependency file, we need to finish up here.
1712 // the writeResourceSymbols and writeAPK functions have already written the target
1713 // half of the dependency file, now we need to write the prerequisites. (files that
1714 // the R.java file or .ap_ file depend on)
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001715 if (bundle->getGenDependencies()) {
1716 // Now that writeResourceSymbols or writeAPK has taken care of writing
1717 // the targets to our dependency file, we'll write the prereqs
1718 fp = fopen(dependencyFile, "a+");
1719 fprintf(fp, " : ");
1720 bool includeRaw = (outputAPKFile != NULL);
1721 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001722 // Also manually add the AndroidManifeset since it's not under res/ or assets/
1723 // and therefore was not added to our pathstores during slurping
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001724 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
1725 fclose(fp);
1726 }
1727
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001728 retVal = 0;
1729bail:
1730 if (SourcePos::hasErrors()) {
1731 SourcePos::printErrors(stderr);
1732 }
1733 return retVal;
1734}
Josiah Gaskin8a39da82011-06-06 17:00:35 -07001735
1736/*
1737 * Do PNG Crunching
1738 * PRECONDITIONS
1739 * -S flag points to a source directory containing drawable* folders
1740 * -C flag points to destination directory. The folder structure in the
1741 * source directory will be mirrored to the destination (cache) directory
1742 *
1743 * POSTCONDITIONS
1744 * Destination directory will be updated to match the PNG files in
1745 * the source directory.
1746 */
1747int doCrunch(Bundle* bundle)
1748{
1749 fprintf(stdout, "Crunching PNG Files in ");
1750 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
1751 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
1752
1753 updatePreProcessedCache(bundle);
1754
1755 return NO_ERROR;
1756}
Dan Morrille74763e2012-01-06 10:47:10 -08001757
1758char CONSOLE_DATA[2925] = {
1759 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1760 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1761 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1762 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1763 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
1764 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
1765 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1766 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1767 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
1768 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
1769 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1770 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1771 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
1772 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1773 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1774 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
1775 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
1776 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1777 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1778 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
1779 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
1780 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
1781 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
1782 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
1783 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1784 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1785 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
1786 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
1787 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
1788 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1789 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
1790 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1791 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1792 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
1793 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
1794 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1795 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1796 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
1797 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
1798 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
1799 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1800 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
1801 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1802 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1803 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
1804 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
1805 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
1806 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
1807 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
1808 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1809 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
1810 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
1811 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
1812 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1813 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
1814 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
1815 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
1816 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
1817 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
1818 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
1819 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
1820 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
1821 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
1822 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
1823 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
1824 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
1825 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
1826 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
1827 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
1828 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
1829 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
1830 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
1831 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1832 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
1833 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
1834 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
1835 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1836 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
1837 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
1838 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1839 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
1840 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
1841 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
1842 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1843 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
1844 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
1845 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1846 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
1847 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
1848 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1849 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1850 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
1851 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
1852 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1853 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1854 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
1855 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1856 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
1857 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
1858 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
1859 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1860 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1861 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
1862 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
1863 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
1864 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1865 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
1866 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1867 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1868 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
1869 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
1870 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1871 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1872 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
1873 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1874 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
1875 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
1876 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
1877 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1878 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1879 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
1880 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
1881 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
1882 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
1883 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
1884 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
1885 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
1886 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
1887 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
1888 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1889 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1890 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
1891 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
1892 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
1893 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
1894 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
1895 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1896 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1897 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
1898 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
1899 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
1900 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1901 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
1902 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1903 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1904 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
1905 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
1906 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1907 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1908 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
1909 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
1910 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1911 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
1912 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
1913 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1914 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1915 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
1916 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
1917 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1918 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1919 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1920 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1921 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
1922 };