blob: 607056a6c1f27395eb131b5e129a938e9368d60c [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
Dan Morrill89d97c12010-05-03 16:13:14 -0700639 // This next group of variables is used to implement a group of
640 // backward-compatibility heuristics necessitated by the addition of
641 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
642 // heuristic is "if an app requests a permission but doesn't explicitly
643 // request the corresponding <uses-feature>, presume it's there anyway".
644 bool specCameraFeature = false; // camera-related
645 bool specCameraAutofocusFeature = false;
646 bool reqCameraAutofocusFeature = false;
647 bool reqCameraFlashFeature = false;
Dianne Hackborne5276a72009-08-27 16:28:44 -0700648 bool hasCameraPermission = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700649 bool specLocationFeature = false; // location-related
650 bool specNetworkLocFeature = false;
651 bool reqNetworkLocFeature = false;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800652 bool specGpsFeature = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700653 bool reqGpsFeature = false;
654 bool hasMockLocPermission = false;
655 bool hasCoarseLocPermission = false;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800656 bool hasGpsPermission = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700657 bool hasGeneralLocPermission = false;
658 bool specBluetoothFeature = false; // Bluetooth API-related
659 bool hasBluetoothPermission = false;
660 bool specMicrophoneFeature = false; // microphone-related
661 bool hasRecordAudioPermission = false;
662 bool specWiFiFeature = false;
663 bool hasWiFiPermission = false;
664 bool specTelephonyFeature = false; // telephony-related
665 bool reqTelephonySubFeature = false;
666 bool hasTelephonyPermission = false;
667 bool specTouchscreenFeature = false; // touchscreen-related
668 bool specMultitouchFeature = false;
669 bool reqDistinctMultitouchFeature = false;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700670 bool specScreenPortraitFeature = false;
671 bool specScreenLandscapeFeature = false;
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -0700672 bool reqScreenPortraitFeature = false;
673 bool reqScreenLandscapeFeature = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700674 // 2.2 also added some other features that apps can request, but that
675 // have no corresponding permission, so we cannot implement any
676 // back-compatibility heuristic for them. The below are thus unnecessary
677 // (but are retained here for documentary purposes.)
678 //bool specCompassFeature = false;
679 //bool specAccelerometerFeature = false;
680 //bool specProximityFeature = false;
681 //bool specAmbientLightFeature = false;
682 //bool specLiveWallpaperFeature = false;
683
Dianne Hackborn723738c2009-06-25 19:48:04 -0700684 int targetSdk = 0;
685 int smallScreen = 1;
686 int normalScreen = 1;
687 int largeScreen = 1;
Dianne Hackbornf43489d2010-08-20 12:44:33 -0700688 int xlargeScreen = 1;
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700689 int anyDensity = 1;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700690 int requiresSmallestWidthDp = 0;
691 int compatibleWidthLimitDp = 0;
692 int largestWidthLimitDp = 0;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700693 String8 pkg;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800694 String8 activityName;
695 String8 activityLabel;
696 String8 activityIcon;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700697 String8 receiverName;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700698 String8 serviceName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800699 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
700 if (code == ResXMLTree::END_TAG) {
701 depth--;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700702 if (depth < 2) {
703 withinApplication = false;
704 } else if (depth < 3) {
705 if (withinActivity && isMainActivity && isLauncherActivity) {
706 const char *aName = getComponentName(pkg, activityName);
Dianne Hackborne289bff2011-06-13 19:33:22 -0700707 printf("launchable-activity:");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700708 if (aName != NULL) {
Dianne Hackborne289bff2011-06-13 19:33:22 -0700709 printf(" name='%s' ", aName);
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700710 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700711 printf(" label='%s' icon='%s'\n",
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700712 activityLabel.string(),
713 activityIcon.string());
714 }
715 if (!hasIntentFilter) {
716 hasOtherActivities |= withinActivity;
717 hasOtherReceivers |= withinReceiver;
718 hasOtherServices |= withinService;
719 }
720 withinActivity = false;
721 withinService = false;
722 withinReceiver = false;
723 hasIntentFilter = false;
724 isMainActivity = isLauncherActivity = false;
725 } else if (depth < 4) {
726 if (withinIntentFilter) {
727 if (withinActivity) {
728 hasMainActivity |= actMainActivity;
729 hasOtherActivities |= !actMainActivity;
730 } else if (withinReceiver) {
731 hasWidgetReceivers |= actWidgetReceivers;
732 hasOtherReceivers |= !actWidgetReceivers;
733 } else if (withinService) {
734 hasImeService |= actImeService;
735 hasWallpaperService |= actWallpaperService;
736 hasOtherServices |= (!actImeService && !actWallpaperService);
737 }
738 }
739 withinIntentFilter = false;
740 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800741 continue;
742 }
743 if (code != ResXMLTree::START_TAG) {
744 continue;
745 }
746 depth++;
747 String8 tag(tree.getElementName(&len));
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700748 //printf("Depth %d, %s\n", depth, tag.string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800749 if (depth == 1) {
750 if (tag != "manifest") {
751 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
752 goto bail;
753 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700754 pkg = getAttribute(tree, NULL, "package", NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800755 printf("package: name='%s' ", pkg.string());
756 int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
757 if (error != "") {
758 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
759 goto bail;
760 }
761 if (versionCode > 0) {
762 printf("versionCode='%d' ", versionCode);
763 } else {
764 printf("versionCode='' ");
765 }
Dianne Hackborncf244ad2010-03-09 15:00:30 -0800766 String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800767 if (error != "") {
768 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
769 goto bail;
770 }
771 printf("versionName='%s'\n", versionName.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700772 } else if (depth == 2) {
773 withinApplication = false;
774 if (tag == "application") {
775 withinApplication = true;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700776
777 String8 label;
778 const size_t NL = locales.size();
779 for (size_t i=0; i<NL; i++) {
780 const char* localeStr = locales[i].string();
781 assets.setLocale(localeStr != NULL ? localeStr : "");
782 String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
783 if (llabel != "") {
784 if (localeStr == NULL || strlen(localeStr) == 0) {
785 label = llabel;
786 printf("application-label:'%s'\n", llabel.string());
787 } else {
788 if (label == "") {
789 label = llabel;
790 }
791 printf("application-label-%s:'%s'\n", localeStr,
792 llabel.string());
793 }
794 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700795 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700796
797 ResTable_config tmpConfig = config;
798 const size_t ND = densities.size();
799 for (size_t i=0; i<ND; i++) {
800 tmpConfig.density = densities[i];
801 assets.setConfiguration(tmpConfig);
802 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
803 if (icon != "") {
804 printf("application-icon-%d:'%s'\n", densities[i], icon.string());
805 }
806 }
807 assets.setConfiguration(config);
808
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700809 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
810 if (error != "") {
811 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
812 goto bail;
813 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700814 int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700815 if (error != "") {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700816 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700817 goto bail;
818 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700819 printf("application: label='%s' ", label.string());
820 printf("icon='%s'\n", icon.string());
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700821 if (testOnly != 0) {
822 printf("testOnly='%d'\n", testOnly);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700823 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700824 } else if (tag == "uses-sdk") {
825 int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
826 if (error != "") {
827 error = "";
828 String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
829 if (error != "") {
830 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
831 error.string());
832 goto bail;
833 }
Dianne Hackborn723738c2009-06-25 19:48:04 -0700834 if (name == "Donut") targetSdk = 4;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700835 printf("sdkVersion:'%s'\n", name.string());
836 } else if (code != -1) {
Dianne Hackborn723738c2009-06-25 19:48:04 -0700837 targetSdk = code;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700838 printf("sdkVersion:'%d'\n", code);
839 }
Suchi Amalapurapu75c49842009-08-14 15:13:09 -0700840 code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
841 if (code != -1) {
842 printf("maxSdkVersion:'%d'\n", code);
843 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700844 code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
845 if (error != "") {
846 error = "";
847 String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
848 if (error != "") {
849 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
850 error.string());
851 goto bail;
852 }
Dianne Hackborn723738c2009-06-25 19:48:04 -0700853 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700854 printf("targetSdkVersion:'%s'\n", name.string());
855 } else if (code != -1) {
Dianne Hackborn723738c2009-06-25 19:48:04 -0700856 if (targetSdk < code) {
857 targetSdk = code;
858 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700859 printf("targetSdkVersion:'%d'\n", code);
860 }
861 } else if (tag == "uses-configuration") {
862 int32_t reqTouchScreen = getIntegerAttribute(tree,
863 REQ_TOUCH_SCREEN_ATTR, NULL, 0);
864 int32_t reqKeyboardType = getIntegerAttribute(tree,
865 REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
866 int32_t reqHardKeyboard = getIntegerAttribute(tree,
867 REQ_HARD_KEYBOARD_ATTR, NULL, 0);
868 int32_t reqNavigation = getIntegerAttribute(tree,
869 REQ_NAVIGATION_ATTR, NULL, 0);
870 int32_t reqFiveWayNav = getIntegerAttribute(tree,
871 REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
Dianne Hackborncb2d50d2010-01-06 11:29:54 -0800872 printf("uses-configuration:");
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700873 if (reqTouchScreen != 0) {
874 printf(" reqTouchScreen='%d'", reqTouchScreen);
875 }
876 if (reqKeyboardType != 0) {
877 printf(" reqKeyboardType='%d'", reqKeyboardType);
878 }
879 if (reqHardKeyboard != 0) {
880 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
881 }
882 if (reqNavigation != 0) {
883 printf(" reqNavigation='%d'", reqNavigation);
884 }
885 if (reqFiveWayNav != 0) {
886 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
887 }
888 printf("\n");
Dianne Hackborn723738c2009-06-25 19:48:04 -0700889 } else if (tag == "supports-screens") {
890 smallScreen = getIntegerAttribute(tree,
891 SMALL_SCREEN_ATTR, NULL, 1);
892 normalScreen = getIntegerAttribute(tree,
893 NORMAL_SCREEN_ATTR, NULL, 1);
894 largeScreen = getIntegerAttribute(tree,
895 LARGE_SCREEN_ATTR, NULL, 1);
Dianne Hackbornf43489d2010-08-20 12:44:33 -0700896 xlargeScreen = getIntegerAttribute(tree,
897 XLARGE_SCREEN_ATTR, NULL, 1);
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700898 anyDensity = getIntegerAttribute(tree,
899 ANY_DENSITY_ATTR, NULL, 1);
Dianne Hackborne289bff2011-06-13 19:33:22 -0700900 requiresSmallestWidthDp = getIntegerAttribute(tree,
901 REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
902 compatibleWidthLimitDp = getIntegerAttribute(tree,
903 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
904 largestWidthLimitDp = getIntegerAttribute(tree,
905 LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
Dianne Hackborne5276a72009-08-27 16:28:44 -0700906 } else if (tag == "uses-feature") {
907 String8 name = getAttribute(tree, NAME_ATTR, &error);
Suchi Amalapurapu40b94722009-09-20 13:39:37 -0700908
909 if (name != "" && error == "") {
Dianne Hackborne5276a72009-08-27 16:28:44 -0700910 int req = getIntegerAttribute(tree,
911 REQUIRED_ATTR, NULL, 1);
Dan Morrill89d97c12010-05-03 16:13:14 -0700912
Dianne Hackborne5276a72009-08-27 16:28:44 -0700913 if (name == "android.hardware.camera") {
914 specCameraFeature = true;
Dan Morrill89d97c12010-05-03 16:13:14 -0700915 } else if (name == "android.hardware.camera.autofocus") {
916 // these have no corresponding permission to check for,
917 // but should imply the foundational camera permission
918 reqCameraAutofocusFeature = reqCameraAutofocusFeature || req;
919 specCameraAutofocusFeature = true;
920 } else if (req && (name == "android.hardware.camera.flash")) {
921 // these have no corresponding permission to check for,
922 // but should imply the foundational camera permission
923 reqCameraFlashFeature = true;
924 } else if (name == "android.hardware.location") {
925 specLocationFeature = true;
926 } else if (name == "android.hardware.location.network") {
927 specNetworkLocFeature = true;
928 reqNetworkLocFeature = reqNetworkLocFeature || req;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800929 } else if (name == "android.hardware.location.gps") {
930 specGpsFeature = true;
Dan Morrill89d97c12010-05-03 16:13:14 -0700931 reqGpsFeature = reqGpsFeature || req;
932 } else if (name == "android.hardware.bluetooth") {
933 specBluetoothFeature = true;
934 } else if (name == "android.hardware.touchscreen") {
935 specTouchscreenFeature = true;
936 } else if (name == "android.hardware.touchscreen.multitouch") {
937 specMultitouchFeature = true;
938 } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
939 reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req;
940 } else if (name == "android.hardware.microphone") {
941 specMicrophoneFeature = true;
942 } else if (name == "android.hardware.wifi") {
943 specWiFiFeature = true;
944 } else if (name == "android.hardware.telephony") {
945 specTelephonyFeature = true;
946 } else if (req && (name == "android.hardware.telephony.gsm" ||
947 name == "android.hardware.telephony.cdma")) {
948 // these have no corresponding permission to check for,
949 // but should imply the foundational telephony permission
950 reqTelephonySubFeature = true;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700951 } else if (name == "android.hardware.screen.portrait") {
952 specScreenPortraitFeature = true;
953 } else if (name == "android.hardware.screen.landscape") {
954 specScreenLandscapeFeature = true;
Dianne Hackborne5276a72009-08-27 16:28:44 -0700955 }
956 printf("uses-feature%s:'%s'\n",
957 req ? "" : "-not-required", name.string());
958 } else {
959 int vers = getIntegerAttribute(tree,
960 GL_ES_VERSION_ATTR, &error);
961 if (error == "") {
962 printf("uses-gl-es:'0x%x'\n", vers);
963 }
964 }
965 } else if (tag == "uses-permission") {
966 String8 name = getAttribute(tree, NAME_ATTR, &error);
Suchi Amalapurapu40b94722009-09-20 13:39:37 -0700967 if (name != "" && error == "") {
Dianne Hackborne5276a72009-08-27 16:28:44 -0700968 if (name == "android.permission.CAMERA") {
969 hasCameraPermission = true;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800970 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
971 hasGpsPermission = true;
Dan Morrill89d97c12010-05-03 16:13:14 -0700972 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
973 hasMockLocPermission = true;
974 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
975 hasCoarseLocPermission = true;
976 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
977 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
978 hasGeneralLocPermission = true;
979 } else if (name == "android.permission.BLUETOOTH" ||
980 name == "android.permission.BLUETOOTH_ADMIN") {
981 hasBluetoothPermission = true;
982 } else if (name == "android.permission.RECORD_AUDIO") {
983 hasRecordAudioPermission = true;
984 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
985 name == "android.permission.CHANGE_WIFI_STATE" ||
986 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
987 hasWiFiPermission = true;
988 } else if (name == "android.permission.CALL_PHONE" ||
989 name == "android.permission.CALL_PRIVILEGED" ||
990 name == "android.permission.MODIFY_PHONE_STATE" ||
991 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
992 name == "android.permission.READ_SMS" ||
993 name == "android.permission.RECEIVE_SMS" ||
994 name == "android.permission.RECEIVE_MMS" ||
995 name == "android.permission.RECEIVE_WAP_PUSH" ||
996 name == "android.permission.SEND_SMS" ||
997 name == "android.permission.WRITE_APN_SETTINGS" ||
998 name == "android.permission.WRITE_SMS") {
999 hasTelephonyPermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001000 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1001 hasWriteExternalStoragePermission = true;
1002 } else if (name == "android.permission.READ_PHONE_STATE") {
1003 hasReadPhoneStatePermission = true;
Dianne Hackborne5276a72009-08-27 16:28:44 -07001004 }
1005 printf("uses-permission:'%s'\n", name.string());
1006 } else {
1007 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1008 error.string());
1009 goto bail;
1010 }
Dianne Hackborn43b68032010-09-02 17:14:41 -07001011 } else if (tag == "uses-package") {
1012 String8 name = getAttribute(tree, NAME_ATTR, &error);
1013 if (name != "" && error == "") {
1014 printf("uses-package:'%s'\n", name.string());
1015 } else {
1016 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1017 error.string());
1018 goto bail;
1019 }
Jeff Hamiltone2c17f92010-02-12 13:45:16 -06001020 } else if (tag == "original-package") {
1021 String8 name = getAttribute(tree, NAME_ATTR, &error);
1022 if (name != "" && error == "") {
1023 printf("original-package:'%s'\n", name.string());
1024 } else {
1025 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1026 error.string());
1027 goto bail;
1028 }
Dan Morrill096b67f2010-12-13 16:25:54 -08001029 } else if (tag == "supports-gl-texture") {
Dan Morrill6f51fc12010-10-13 14:33:43 -07001030 String8 name = getAttribute(tree, NAME_ATTR, &error);
1031 if (name != "" && error == "") {
Dan Morrill096b67f2010-12-13 16:25:54 -08001032 printf("supports-gl-texture:'%s'\n", name.string());
Dan Morrill6f51fc12010-10-13 14:33:43 -07001033 } else {
1034 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1035 error.string());
1036 goto bail;
1037 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001038 } else if (tag == "compatible-screens") {
1039 printCompatibleScreens(tree);
1040 depth--;
Kenny Root56088a52011-09-29 13:49:45 -07001041 } else if (tag == "package-verifier") {
1042 String8 name = getAttribute(tree, NAME_ATTR, &error);
1043 if (name != "" && error == "") {
1044 String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
1045 if (publicKey != "" && error == "") {
1046 printf("package-verifier: name='%s' publicKey='%s'\n",
1047 name.string(), publicKey.string());
1048 }
1049 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001050 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001051 } else if (depth == 3 && withinApplication) {
1052 withinActivity = false;
1053 withinReceiver = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001054 withinService = false;
1055 hasIntentFilter = false;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001056 if(tag == "activity") {
1057 withinActivity = true;
1058 activityName = getAttribute(tree, NAME_ATTR, &error);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001059 if (error != "") {
1060 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1061 goto bail;
1062 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001063
1064 activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001065 if (error != "") {
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001066 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001067 goto bail;
1068 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001069
1070 activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1071 if (error != "") {
1072 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
1073 goto bail;
1074 }
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001075
1076 int32_t orien = getResolvedIntegerAttribute(&res, tree,
1077 SCREEN_ORIENTATION_ATTR, &error);
1078 if (error == "") {
1079 if (orien == 0 || orien == 6 || orien == 8) {
1080 // Requests landscape, sensorLandscape, or reverseLandscape.
1081 reqScreenLandscapeFeature = true;
1082 } else if (orien == 1 || orien == 7 || orien == 9) {
1083 // Requests portrait, sensorPortrait, or reversePortrait.
1084 reqScreenPortraitFeature = true;
1085 }
1086 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001087 } else if (tag == "uses-library") {
1088 String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1089 if (error != "") {
1090 fprintf(stderr, "ERROR getting 'android:name' attribute for uses-library: %s\n", error.string());
1091 goto bail;
1092 }
Dianne Hackborn49237342009-08-27 20:08:01 -07001093 int req = getIntegerAttribute(tree,
1094 REQUIRED_ATTR, NULL, 1);
1095 printf("uses-library%s:'%s'\n",
1096 req ? "" : "-not-required", libraryName.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001097 } else if (tag == "receiver") {
1098 withinReceiver = true;
1099 receiverName = getAttribute(tree, NAME_ATTR, &error);
1100
1101 if (error != "") {
1102 fprintf(stderr, "ERROR getting 'android:name' attribute for receiver: %s\n", error.string());
1103 goto bail;
1104 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001105 } else if (tag == "service") {
1106 withinService = true;
1107 serviceName = getAttribute(tree, NAME_ATTR, &error);
1108
1109 if (error != "") {
1110 fprintf(stderr, "ERROR getting 'android:name' attribute for service: %s\n", error.string());
1111 goto bail;
1112 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001113 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001114 } else if ((depth == 4) && (tag == "intent-filter")) {
1115 hasIntentFilter = true;
1116 withinIntentFilter = true;
1117 actMainActivity = actWidgetReceivers = actImeService = actWallpaperService = false;
1118 } else if ((depth == 5) && withinIntentFilter){
1119 String8 action;
1120 if (tag == "action") {
1121 action = getAttribute(tree, NAME_ATTR, &error);
1122 if (error != "") {
1123 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1124 goto bail;
1125 }
1126 if (withinActivity) {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001127 if (action == "android.intent.action.MAIN") {
1128 isMainActivity = true;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001129 actMainActivity = true;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001130 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001131 } else if (withinReceiver) {
1132 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1133 actWidgetReceivers = true;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001134 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001135 } else if (withinService) {
1136 if (action == "android.view.InputMethod") {
1137 actImeService = true;
1138 } else if (action == "android.service.wallpaper.WallpaperService") {
1139 actWallpaperService = true;
1140 }
1141 }
1142 if (action == "android.intent.action.SEARCH") {
1143 isSearchable = true;
1144 }
1145 }
1146
1147 if (tag == "category") {
1148 String8 category = getAttribute(tree, NAME_ATTR, &error);
1149 if (error != "") {
1150 fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string());
1151 goto bail;
1152 }
1153 if (withinActivity) {
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001154 if (category == "android.intent.category.LAUNCHER") {
1155 isLauncherActivity = true;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001156 }
1157 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001158 }
1159 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001160 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001161
Kenny Root063a44e2011-12-08 08:46:03 -08001162 // Pre-1.6 implicitly granted permission compatibility logic
1163 if (targetSdk < 4) {
1164 if (!hasWriteExternalStoragePermission) {
1165 printf("uses-permission:'android.permission.WRITE_EXTERNAL_STORAGE'\n");
1166 }
1167 if (!hasReadPhoneStatePermission) {
1168 printf("uses-permission:'android.permission.READ_PHONE_STATE'\n");
1169 }
1170 }
1171
Dan Morrill89d97c12010-05-03 16:13:14 -07001172 /* The following blocks handle printing "inferred" uses-features, based
1173 * on whether related features or permissions are used by the app.
1174 * Note that the various spec*Feature variables denote whether the
1175 * relevant tag was *present* in the AndroidManfest, not that it was
1176 * present and set to true.
1177 */
1178 // Camera-related back-compatibility logic
1179 if (!specCameraFeature) {
1180 if (reqCameraFlashFeature || reqCameraAutofocusFeature) {
1181 // if app requested a sub-feature (autofocus or flash) and didn't
1182 // request the base camera feature, we infer that it meant to
1183 printf("uses-feature:'android.hardware.camera'\n");
1184 } else if (hasCameraPermission) {
1185 // if app wants to use camera but didn't request the feature, we infer
1186 // that it meant to, and further that it wants autofocus
1187 // (which was the 1.0 - 1.5 behavior)
1188 printf("uses-feature:'android.hardware.camera'\n");
1189 if (!specCameraAutofocusFeature) {
1190 printf("uses-feature:'android.hardware.camera.autofocus'\n");
1191 }
1192 }
Dianne Hackborne5276a72009-08-27 16:28:44 -07001193 }
Doug Zongkerdbe7a682009-10-09 11:24:51 -07001194
Dan Morrill89d97c12010-05-03 16:13:14 -07001195 // Location-related back-compatibility logic
1196 if (!specLocationFeature &&
1197 (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission ||
1198 hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) {
1199 // if app either takes a location-related permission or requests one of the
1200 // sub-features, we infer that it also meant to request the base location feature
1201 printf("uses-feature:'android.hardware.location'\n");
1202 }
Dianne Hackbornef05e072010-03-01 17:43:39 -08001203 if (!specGpsFeature && hasGpsPermission) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001204 // if app takes GPS (FINE location) perm but does not request the GPS
1205 // feature, we infer that it meant to
Dianne Hackbornef05e072010-03-01 17:43:39 -08001206 printf("uses-feature:'android.hardware.location.gps'\n");
1207 }
Dan Morrill89d97c12010-05-03 16:13:14 -07001208 if (!specNetworkLocFeature && hasCoarseLocPermission) {
1209 // if app takes Network location (COARSE location) perm but does not request the
1210 // network location feature, we infer that it meant to
1211 printf("uses-feature:'android.hardware.location.network'\n");
1212 }
1213
1214 // Bluetooth-related compatibility logic
Dan Morrill6b22d812010-06-15 21:41:42 -07001215 if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001216 // if app takes a Bluetooth permission but does not request the Bluetooth
1217 // feature, we infer that it meant to
1218 printf("uses-feature:'android.hardware.bluetooth'\n");
1219 }
1220
1221 // Microphone-related compatibility logic
1222 if (!specMicrophoneFeature && hasRecordAudioPermission) {
1223 // if app takes the record-audio permission but does not request the microphone
1224 // feature, we infer that it meant to
1225 printf("uses-feature:'android.hardware.microphone'\n");
1226 }
1227
1228 // WiFi-related compatibility logic
1229 if (!specWiFiFeature && hasWiFiPermission) {
1230 // if app takes one of the WiFi permissions but does not request the WiFi
1231 // feature, we infer that it meant to
1232 printf("uses-feature:'android.hardware.wifi'\n");
1233 }
1234
1235 // Telephony-related compatibility logic
1236 if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) {
1237 // if app takes one of the telephony permissions or requests a sub-feature but
1238 // does not request the base telephony feature, we infer that it meant to
1239 printf("uses-feature:'android.hardware.telephony'\n");
1240 }
1241
1242 // Touchscreen-related back-compatibility logic
1243 if (!specTouchscreenFeature) { // not a typo!
1244 // all apps are presumed to require a touchscreen, unless they explicitly say
1245 // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
1246 // Note that specTouchscreenFeature is true if the tag is present, regardless
1247 // of whether its value is true or false, so this is safe
1248 printf("uses-feature:'android.hardware.touchscreen'\n");
1249 }
1250 if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
1251 // if app takes one of the telephony permissions or requests a sub-feature but
1252 // does not request the base telephony feature, we infer that it meant to
1253 printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
1254 }
Dianne Hackbornef05e072010-03-01 17:43:39 -08001255
Dianne Hackborne289bff2011-06-13 19:33:22 -07001256 // Landscape/portrait-related compatibility logic
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001257 if (!specScreenLandscapeFeature && !specScreenPortraitFeature) {
1258 // If the app has specified any activities in its manifest
1259 // that request a specific orientation, then assume that
1260 // orientation is required.
1261 if (reqScreenLandscapeFeature) {
1262 printf("uses-feature:'android.hardware.screen.landscape'\n");
1263 }
1264 if (reqScreenPortraitFeature) {
1265 printf("uses-feature:'android.hardware.screen.portrait'\n");
1266 }
Dianne Hackborne289bff2011-06-13 19:33:22 -07001267 }
1268
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001269 if (hasMainActivity) {
1270 printf("main\n");
1271 }
1272 if (hasWidgetReceivers) {
1273 printf("app-widget\n");
1274 }
1275 if (hasImeService) {
1276 printf("ime\n");
1277 }
1278 if (hasWallpaperService) {
1279 printf("wallpaper\n");
1280 }
1281 if (hasOtherActivities) {
1282 printf("other-activities\n");
1283 }
1284 if (isSearchable) {
1285 printf("search\n");
1286 }
1287 if (hasOtherReceivers) {
1288 printf("other-receivers\n");
1289 }
1290 if (hasOtherServices) {
1291 printf("other-services\n");
1292 }
1293
Dianne Hackborne289bff2011-06-13 19:33:22 -07001294 // For modern apps, if screen size buckets haven't been specified
1295 // but the new width ranges have, then infer the buckets from them.
1296 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1297 && requiresSmallestWidthDp > 0) {
1298 int compatWidth = compatibleWidthLimitDp;
1299 if (compatWidth <= 0) compatWidth = requiresSmallestWidthDp;
1300 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1301 smallScreen = -1;
1302 } else {
1303 smallScreen = 0;
1304 }
1305 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1306 normalScreen = -1;
1307 } else {
1308 normalScreen = 0;
1309 }
1310 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1311 largeScreen = -1;
1312 } else {
1313 largeScreen = 0;
1314 }
1315 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1316 xlargeScreen = -1;
1317 } else {
1318 xlargeScreen = 0;
1319 }
1320 }
1321
Dianne Hackborn723738c2009-06-25 19:48:04 -07001322 // Determine default values for any unspecified screen sizes,
1323 // based on the target SDK of the package. As of 4 (donut)
1324 // the screen size support was introduced, so all default to
1325 // enabled.
1326 if (smallScreen > 0) {
1327 smallScreen = targetSdk >= 4 ? -1 : 0;
1328 }
1329 if (normalScreen > 0) {
1330 normalScreen = -1;
1331 }
1332 if (largeScreen > 0) {
1333 largeScreen = targetSdk >= 4 ? -1 : 0;
1334 }
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001335 if (xlargeScreen > 0) {
Scott Maind58fb972010-11-04 18:32:00 -07001336 // Introduced in Gingerbread.
1337 xlargeScreen = targetSdk >= 9 ? -1 : 0;
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001338 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001339 if (anyDensity > 0) {
Dianne Hackborne289bff2011-06-13 19:33:22 -07001340 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1341 || compatibleWidthLimitDp > 0) ? -1 : 0;
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001342 }
Dianne Hackborn723738c2009-06-25 19:48:04 -07001343 printf("supports-screens:");
1344 if (smallScreen != 0) printf(" 'small'");
1345 if (normalScreen != 0) printf(" 'normal'");
1346 if (largeScreen != 0) printf(" 'large'");
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001347 if (xlargeScreen != 0) printf(" 'xlarge'");
Dianne Hackborn723738c2009-06-25 19:48:04 -07001348 printf("\n");
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001349 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
Dianne Hackborne289bff2011-06-13 19:33:22 -07001350 if (requiresSmallestWidthDp > 0) {
1351 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1352 }
1353 if (compatibleWidthLimitDp > 0) {
1354 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1355 }
1356 if (largestWidthLimitDp > 0) {
1357 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1358 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001359
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001360 printf("locales:");
Dianne Hackborne17086b2009-06-19 15:13:28 -07001361 const size_t NL = locales.size();
1362 for (size_t i=0; i<NL; i++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001363 const char* localeStr = locales[i].string();
1364 if (localeStr == NULL || strlen(localeStr) == 0) {
1365 localeStr = "--_--";
1366 }
1367 printf(" '%s'", localeStr);
1368 }
1369 printf("\n");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001370
Dianne Hackborne17086b2009-06-19 15:13:28 -07001371 printf("densities:");
1372 const size_t ND = densities.size();
1373 for (size_t i=0; i<ND; i++) {
1374 printf(" '%d'", densities[i]);
1375 }
1376 printf("\n");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001377
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001378 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1379 if (dir != NULL) {
1380 if (dir->getFileCount() > 0) {
1381 printf("native-code:");
1382 for (size_t i=0; i<dir->getFileCount(); i++) {
1383 printf(" '%s'", dir->getFileName(i).string());
1384 }
1385 printf("\n");
1386 }
1387 delete dir;
1388 }
Dan Morrille74763e2012-01-06 10:47:10 -08001389 } else if (strcmp("badger", option) == 0) {
Dianne Hackborn6c997a92012-01-31 11:27:43 -08001390 printf("%s", CONSOLE_DATA);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001391 } else if (strcmp("configurations", option) == 0) {
1392 Vector<ResTable_config> configs;
1393 res.getConfigurations(&configs);
1394 const size_t N = configs.size();
1395 for (size_t i=0; i<N; i++) {
1396 printf("%s\n", configs[i].toString().string());
1397 }
1398 } else {
1399 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
1400 goto bail;
1401 }
1402 }
1403
1404 result = NO_ERROR;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001405
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001406bail:
1407 if (asset) {
1408 delete asset;
1409 }
1410 return (result != NO_ERROR);
1411}
1412
1413
1414/*
1415 * Handle the "add" command, which wants to add files to a new or
1416 * pre-existing archive.
1417 */
1418int doAdd(Bundle* bundle)
1419{
1420 ZipFile* zip = NULL;
1421 status_t result = UNKNOWN_ERROR;
1422 const char* zipFileName;
1423
1424 if (bundle->getUpdate()) {
1425 /* avoid confusion */
1426 fprintf(stderr, "ERROR: can't use '-u' with add\n");
1427 goto bail;
1428 }
1429
1430 if (bundle->getFileSpecCount() < 1) {
1431 fprintf(stderr, "ERROR: must specify zip file name\n");
1432 goto bail;
1433 }
1434 zipFileName = bundle->getFileSpecEntry(0);
1435
1436 if (bundle->getFileSpecCount() < 2) {
1437 fprintf(stderr, "NOTE: nothing to do\n");
1438 goto bail;
1439 }
1440
1441 zip = openReadWrite(zipFileName, true);
1442 if (zip == NULL) {
1443 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
1444 goto bail;
1445 }
1446
1447 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1448 const char* fileName = bundle->getFileSpecEntry(i);
1449
1450 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
1451 printf(" '%s'... (from gzip)\n", fileName);
1452 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
1453 } else {
Doug Zongkerdbe7a682009-10-09 11:24:51 -07001454 if (bundle->getJunkPath()) {
1455 String8 storageName = String8(fileName).getPathLeaf();
1456 printf(" '%s' as '%s'...\n", fileName, storageName.string());
1457 result = zip->add(fileName, storageName.string(),
1458 bundle->getCompressionMethod(), NULL);
1459 } else {
1460 printf(" '%s'...\n", fileName);
1461 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
1462 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001463 }
1464 if (result != NO_ERROR) {
1465 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
1466 if (result == NAME_NOT_FOUND)
1467 fprintf(stderr, ": file not found\n");
1468 else if (result == ALREADY_EXISTS)
1469 fprintf(stderr, ": already exists in archive\n");
1470 else
1471 fprintf(stderr, "\n");
1472 goto bail;
1473 }
1474 }
1475
1476 result = NO_ERROR;
1477
1478bail:
1479 delete zip;
1480 return (result != NO_ERROR);
1481}
1482
1483
1484/*
1485 * Delete files from an existing archive.
1486 */
1487int doRemove(Bundle* bundle)
1488{
1489 ZipFile* zip = NULL;
1490 status_t result = UNKNOWN_ERROR;
1491 const char* zipFileName;
1492
1493 if (bundle->getFileSpecCount() < 1) {
1494 fprintf(stderr, "ERROR: must specify zip file name\n");
1495 goto bail;
1496 }
1497 zipFileName = bundle->getFileSpecEntry(0);
1498
1499 if (bundle->getFileSpecCount() < 2) {
1500 fprintf(stderr, "NOTE: nothing to do\n");
1501 goto bail;
1502 }
1503
1504 zip = openReadWrite(zipFileName, false);
1505 if (zip == NULL) {
1506 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
1507 zipFileName);
1508 goto bail;
1509 }
1510
1511 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1512 const char* fileName = bundle->getFileSpecEntry(i);
1513 ZipEntry* entry;
1514
1515 entry = zip->getEntryByName(fileName);
1516 if (entry == NULL) {
1517 printf(" '%s' NOT FOUND\n", fileName);
1518 continue;
1519 }
1520
1521 result = zip->remove(entry);
1522
1523 if (result != NO_ERROR) {
1524 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
1525 bundle->getFileSpecEntry(i), zipFileName);
1526 goto bail;
1527 }
1528 }
1529
1530 /* update the archive */
1531 zip->flush();
1532
1533bail:
1534 delete zip;
1535 return (result != NO_ERROR);
1536}
1537
1538
1539/*
1540 * Package up an asset directory and associated application files.
1541 */
1542int doPackage(Bundle* bundle)
1543{
1544 const char* outputAPKFile;
1545 int retVal = 1;
1546 status_t err;
1547 sp<AaptAssets> assets;
1548 int N;
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001549 FILE* fp;
1550 String8 dependencyFile;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001551
1552 // -c zz_ZZ means do pseudolocalization
1553 ResourceFilter filter;
1554 err = filter.parse(bundle->getConfigurations());
1555 if (err != NO_ERROR) {
1556 goto bail;
1557 }
1558 if (filter.containsPseudo()) {
1559 bundle->setPseudolocalize(true);
1560 }
1561
1562 N = bundle->getFileSpecCount();
1563 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
1564 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDir() == NULL) {
1565 fprintf(stderr, "ERROR: no input files\n");
1566 goto bail;
1567 }
1568
1569 outputAPKFile = bundle->getOutputAPKFile();
1570
1571 // Make sure the filenames provided exist and are of the appropriate type.
1572 if (outputAPKFile) {
1573 FileType type;
1574 type = getFileType(outputAPKFile);
1575 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
1576 fprintf(stderr,
1577 "ERROR: output file '%s' exists but is not regular file\n",
1578 outputAPKFile);
1579 goto bail;
1580 }
1581 }
1582
1583 // Load the assets.
1584 assets = new AaptAssets();
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001585
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001586 // Set up the resource gathering in assets if we're going to generate
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001587 // dependency files. Every time we encounter a resource while slurping
1588 // the tree, we'll add it to these stores so we have full resource paths
1589 // to write to a dependency file.
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001590 if (bundle->getGenDependencies()) {
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001591 sp<FilePathStore> resPathStore = new FilePathStore;
1592 assets->setFullResPaths(resPathStore);
1593 sp<FilePathStore> assetPathStore = new FilePathStore;
1594 assets->setFullAssetPaths(assetPathStore);
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001595 }
1596
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001597 err = assets->slurpFromArgs(bundle);
1598 if (err < 0) {
1599 goto bail;
1600 }
1601
1602 if (bundle->getVerbose()) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001603 assets->print(String8());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001604 }
1605
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001606 // If they asked for any fileAs that need to be compiled, do so.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001607 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
1608 err = buildResources(bundle, assets);
1609 if (err != 0) {
1610 goto bail;
1611 }
1612 }
1613
1614 // At this point we've read everything and processed everything. From here
1615 // on out it's just writing output files.
1616 if (SourcePos::hasErrors()) {
1617 goto bail;
1618 }
1619
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001620 // If we've been asked to generate a dependency file, do that here
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001621 if (bundle->getGenDependencies()) {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001622 // If this is the packaging step, generate the dependency file next to
1623 // the output apk (e.g. bin/resources.ap_.d)
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001624 if (outputAPKFile) {
1625 dependencyFile = String8(outputAPKFile);
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001626 // Add the .d extension to the dependency file.
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001627 dependencyFile.append(".d");
1628 } else {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001629 // Else if this is the R.java dependency generation step,
1630 // generate the dependency file in the R.java package subdirectory
1631 // e.g. gen/com/foo/app/R.java.d
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001632 dependencyFile = String8(bundle->getRClassDir());
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001633 dependencyFile.appendPath("R.java.d");
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001634 }
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001635 // Make sure we have a clean dependency file to start with
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001636 fp = fopen(dependencyFile, "w");
1637 fclose(fp);
1638 }
1639
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001640 // Write out R.java constants
1641 if (assets->getPackage() == assets->getSymbolsPrivatePackage()) {
Xavier Ducrohet63459ad2009-11-30 18:05:10 -08001642 if (bundle->getCustomPackage() == NULL) {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001643 // Write the R.java file into the appropriate class directory
1644 // e.g. gen/com/foo/app/R.java
Xavier Ducrohet63459ad2009-11-30 18:05:10 -08001645 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001646 // If we have library files, we're going to write our R.java file into
1647 // the appropriate class directory for those libraries as well.
1648 // e.g. gen/com/foo/app/lib/R.java
Josiah Gaskince89f152011-06-08 19:31:40 -07001649 if (bundle->getExtraPackages() != NULL) {
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001650 // Split on colon
Josiah Gaskince89f152011-06-08 19:31:40 -07001651 String8 libs(bundle->getExtraPackages());
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001652 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
Josiah Gaskince89f152011-06-08 19:31:40 -07001653 while (packageString != NULL) {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001654 // Write the R.java file out with the correct package name
Josiah Gaskince89f152011-06-08 19:31:40 -07001655 err = writeResourceSymbols(bundle, assets, String8(packageString), true);
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001656 packageString = strtok(NULL, ":");
Josiah Gaskince89f152011-06-08 19:31:40 -07001657 }
1658 libs.unlockBuffer();
1659 }
Xavier Ducrohet63459ad2009-11-30 18:05:10 -08001660 } else {
1661 const String8 customPkg(bundle->getCustomPackage());
1662 err = writeResourceSymbols(bundle, assets, customPkg, true);
1663 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001664 if (err < 0) {
1665 goto bail;
1666 }
1667 } else {
1668 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
1669 if (err < 0) {
1670 goto bail;
1671 }
1672 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
1673 if (err < 0) {
1674 goto bail;
1675 }
1676 }
1677
Joe Onorato1553c822009-08-30 13:36:22 -07001678 // Write out the ProGuard file
1679 err = writeProguardFile(bundle, assets);
1680 if (err < 0) {
1681 goto bail;
1682 }
1683
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001684 // Write the apk
1685 if (outputAPKFile) {
1686 err = writeAPK(bundle, assets, String8(outputAPKFile));
1687 if (err != NO_ERROR) {
1688 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
1689 goto bail;
1690 }
1691 }
1692
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001693 // If we've been asked to generate a dependency file, we need to finish up here.
1694 // the writeResourceSymbols and writeAPK functions have already written the target
1695 // half of the dependency file, now we need to write the prerequisites. (files that
1696 // the R.java file or .ap_ file depend on)
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001697 if (bundle->getGenDependencies()) {
1698 // Now that writeResourceSymbols or writeAPK has taken care of writing
1699 // the targets to our dependency file, we'll write the prereqs
1700 fp = fopen(dependencyFile, "a+");
1701 fprintf(fp, " : ");
1702 bool includeRaw = (outputAPKFile != NULL);
1703 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001704 // Also manually add the AndroidManifeset since it's not under res/ or assets/
1705 // and therefore was not added to our pathstores during slurping
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001706 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
1707 fclose(fp);
1708 }
1709
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001710 retVal = 0;
1711bail:
1712 if (SourcePos::hasErrors()) {
1713 SourcePos::printErrors(stderr);
1714 }
1715 return retVal;
1716}
Josiah Gaskin8a39da82011-06-06 17:00:35 -07001717
1718/*
1719 * Do PNG Crunching
1720 * PRECONDITIONS
1721 * -S flag points to a source directory containing drawable* folders
1722 * -C flag points to destination directory. The folder structure in the
1723 * source directory will be mirrored to the destination (cache) directory
1724 *
1725 * POSTCONDITIONS
1726 * Destination directory will be updated to match the PNG files in
1727 * the source directory.
1728 */
1729int doCrunch(Bundle* bundle)
1730{
1731 fprintf(stdout, "Crunching PNG Files in ");
1732 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
1733 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
1734
1735 updatePreProcessedCache(bundle);
1736
1737 return NO_ERROR;
1738}
Dan Morrille74763e2012-01-06 10:47:10 -08001739
1740char CONSOLE_DATA[2925] = {
1741 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1742 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1743 32, 32, 32, 32, 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, 10, 32, 32, 32, 32, 32, 32, 32,
1745 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
1746 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
1747 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1748 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1749 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
1750 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
1751 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1752 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1753 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
1754 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1755 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1756 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
1757 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
1758 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1759 10, 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, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
1761 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
1762 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
1763 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
1764 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
1765 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1766 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1767 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
1768 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
1769 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 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, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
1772 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1773 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1774 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
1775 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
1776 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1777 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1778 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
1779 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
1780 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
1781 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1782 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
1783 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1784 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1785 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
1786 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
1787 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
1788 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
1789 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
1790 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1791 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
1792 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
1793 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
1794 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1795 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
1796 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
1797 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
1798 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
1799 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
1800 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
1801 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
1802 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
1803 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
1804 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
1805 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
1806 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
1807 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
1808 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
1809 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
1810 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
1811 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
1812 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
1813 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1814 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
1815 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
1816 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
1817 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1818 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
1819 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
1820 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1821 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
1822 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
1823 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
1824 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1825 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
1826 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
1827 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1828 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
1829 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
1830 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1831 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1832 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
1833 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
1834 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1835 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1836 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
1837 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1838 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
1839 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
1840 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
1841 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1842 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1843 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
1844 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
1845 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
1846 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1847 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
1848 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1849 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1850 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
1851 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
1852 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1853 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1854 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
1855 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1856 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
1857 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
1858 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
1859 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1860 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1861 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
1862 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
1863 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
1864 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
1865 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
1866 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
1867 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
1868 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
1869 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
1870 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1871 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1872 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
1873 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
1874 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
1875 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
1876 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
1877 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1878 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1879 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
1880 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
1881 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
1882 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1883 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
1884 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1885 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1886 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
1887 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
1888 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1889 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1890 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
1891 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
1892 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1893 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
1894 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
1895 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1896 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1897 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
1898 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
1899 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1900 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1901 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1902 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1903 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
1904 };