blob: 689aa8e3979cb6261a062cd93bf74a5b47e121bc [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
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -0700642 // Implement transition to read and write call log.
643 bool hasReadContactsPermission = false;
644 bool hasWriteContactsPermission = false;
645 bool hasReadCallLogPermission = false;
646 bool hasWriteCallLogPermission = false;
647
Dan Morrill89d97c12010-05-03 16:13:14 -0700648 // This next group of variables is used to implement a group of
649 // backward-compatibility heuristics necessitated by the addition of
650 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
651 // heuristic is "if an app requests a permission but doesn't explicitly
652 // request the corresponding <uses-feature>, presume it's there anyway".
653 bool specCameraFeature = false; // camera-related
654 bool specCameraAutofocusFeature = false;
655 bool reqCameraAutofocusFeature = false;
656 bool reqCameraFlashFeature = false;
Dianne Hackborne5276a72009-08-27 16:28:44 -0700657 bool hasCameraPermission = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700658 bool specLocationFeature = false; // location-related
659 bool specNetworkLocFeature = false;
660 bool reqNetworkLocFeature = false;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800661 bool specGpsFeature = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700662 bool reqGpsFeature = false;
663 bool hasMockLocPermission = false;
664 bool hasCoarseLocPermission = false;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800665 bool hasGpsPermission = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700666 bool hasGeneralLocPermission = false;
667 bool specBluetoothFeature = false; // Bluetooth API-related
668 bool hasBluetoothPermission = false;
669 bool specMicrophoneFeature = false; // microphone-related
670 bool hasRecordAudioPermission = false;
671 bool specWiFiFeature = false;
672 bool hasWiFiPermission = false;
673 bool specTelephonyFeature = false; // telephony-related
674 bool reqTelephonySubFeature = false;
675 bool hasTelephonyPermission = false;
676 bool specTouchscreenFeature = false; // touchscreen-related
677 bool specMultitouchFeature = false;
678 bool reqDistinctMultitouchFeature = false;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700679 bool specScreenPortraitFeature = false;
680 bool specScreenLandscapeFeature = false;
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -0700681 bool reqScreenPortraitFeature = false;
682 bool reqScreenLandscapeFeature = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700683 // 2.2 also added some other features that apps can request, but that
684 // have no corresponding permission, so we cannot implement any
685 // back-compatibility heuristic for them. The below are thus unnecessary
686 // (but are retained here for documentary purposes.)
687 //bool specCompassFeature = false;
688 //bool specAccelerometerFeature = false;
689 //bool specProximityFeature = false;
690 //bool specAmbientLightFeature = false;
691 //bool specLiveWallpaperFeature = false;
692
Dianne Hackborn723738c2009-06-25 19:48:04 -0700693 int targetSdk = 0;
694 int smallScreen = 1;
695 int normalScreen = 1;
696 int largeScreen = 1;
Dianne Hackbornf43489d2010-08-20 12:44:33 -0700697 int xlargeScreen = 1;
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700698 int anyDensity = 1;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700699 int requiresSmallestWidthDp = 0;
700 int compatibleWidthLimitDp = 0;
701 int largestWidthLimitDp = 0;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700702 String8 pkg;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800703 String8 activityName;
704 String8 activityLabel;
705 String8 activityIcon;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700706 String8 receiverName;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700707 String8 serviceName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800708 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
709 if (code == ResXMLTree::END_TAG) {
710 depth--;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700711 if (depth < 2) {
712 withinApplication = false;
713 } else if (depth < 3) {
714 if (withinActivity && isMainActivity && isLauncherActivity) {
715 const char *aName = getComponentName(pkg, activityName);
Dianne Hackborne289bff2011-06-13 19:33:22 -0700716 printf("launchable-activity:");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700717 if (aName != NULL) {
Dianne Hackborne289bff2011-06-13 19:33:22 -0700718 printf(" name='%s' ", aName);
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700719 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700720 printf(" label='%s' icon='%s'\n",
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700721 activityLabel.string(),
722 activityIcon.string());
723 }
724 if (!hasIntentFilter) {
725 hasOtherActivities |= withinActivity;
726 hasOtherReceivers |= withinReceiver;
727 hasOtherServices |= withinService;
728 }
729 withinActivity = false;
730 withinService = false;
731 withinReceiver = false;
732 hasIntentFilter = false;
733 isMainActivity = isLauncherActivity = false;
734 } else if (depth < 4) {
735 if (withinIntentFilter) {
736 if (withinActivity) {
737 hasMainActivity |= actMainActivity;
738 hasOtherActivities |= !actMainActivity;
739 } else if (withinReceiver) {
740 hasWidgetReceivers |= actWidgetReceivers;
741 hasOtherReceivers |= !actWidgetReceivers;
742 } else if (withinService) {
743 hasImeService |= actImeService;
744 hasWallpaperService |= actWallpaperService;
745 hasOtherServices |= (!actImeService && !actWallpaperService);
746 }
747 }
748 withinIntentFilter = false;
749 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800750 continue;
751 }
752 if (code != ResXMLTree::START_TAG) {
753 continue;
754 }
755 depth++;
756 String8 tag(tree.getElementName(&len));
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700757 //printf("Depth %d, %s\n", depth, tag.string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800758 if (depth == 1) {
759 if (tag != "manifest") {
760 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
761 goto bail;
762 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700763 pkg = getAttribute(tree, NULL, "package", NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800764 printf("package: name='%s' ", pkg.string());
765 int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
766 if (error != "") {
767 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
768 goto bail;
769 }
770 if (versionCode > 0) {
771 printf("versionCode='%d' ", versionCode);
772 } else {
773 printf("versionCode='' ");
774 }
Dianne Hackborncf244ad2010-03-09 15:00:30 -0800775 String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800776 if (error != "") {
777 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
778 goto bail;
779 }
780 printf("versionName='%s'\n", versionName.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700781 } else if (depth == 2) {
782 withinApplication = false;
783 if (tag == "application") {
784 withinApplication = true;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700785
786 String8 label;
787 const size_t NL = locales.size();
788 for (size_t i=0; i<NL; i++) {
789 const char* localeStr = locales[i].string();
790 assets.setLocale(localeStr != NULL ? localeStr : "");
791 String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
792 if (llabel != "") {
793 if (localeStr == NULL || strlen(localeStr) == 0) {
794 label = llabel;
795 printf("application-label:'%s'\n", llabel.string());
796 } else {
797 if (label == "") {
798 label = llabel;
799 }
800 printf("application-label-%s:'%s'\n", localeStr,
801 llabel.string());
802 }
803 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700804 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700805
806 ResTable_config tmpConfig = config;
807 const size_t ND = densities.size();
808 for (size_t i=0; i<ND; i++) {
809 tmpConfig.density = densities[i];
810 assets.setConfiguration(tmpConfig);
811 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
812 if (icon != "") {
813 printf("application-icon-%d:'%s'\n", densities[i], icon.string());
814 }
815 }
816 assets.setConfiguration(config);
817
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700818 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
819 if (error != "") {
820 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
821 goto bail;
822 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700823 int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700824 if (error != "") {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700825 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700826 goto bail;
827 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700828 printf("application: label='%s' ", label.string());
829 printf("icon='%s'\n", icon.string());
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700830 if (testOnly != 0) {
831 printf("testOnly='%d'\n", testOnly);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700832 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700833 } else if (tag == "uses-sdk") {
834 int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
835 if (error != "") {
836 error = "";
837 String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
838 if (error != "") {
839 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
840 error.string());
841 goto bail;
842 }
Dianne Hackborn723738c2009-06-25 19:48:04 -0700843 if (name == "Donut") targetSdk = 4;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700844 printf("sdkVersion:'%s'\n", name.string());
845 } else if (code != -1) {
Dianne Hackborn723738c2009-06-25 19:48:04 -0700846 targetSdk = code;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700847 printf("sdkVersion:'%d'\n", code);
848 }
Suchi Amalapurapu75c49842009-08-14 15:13:09 -0700849 code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
850 if (code != -1) {
851 printf("maxSdkVersion:'%d'\n", code);
852 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700853 code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
854 if (error != "") {
855 error = "";
856 String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
857 if (error != "") {
858 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
859 error.string());
860 goto bail;
861 }
Dianne Hackborn723738c2009-06-25 19:48:04 -0700862 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700863 printf("targetSdkVersion:'%s'\n", name.string());
864 } else if (code != -1) {
Dianne Hackborn723738c2009-06-25 19:48:04 -0700865 if (targetSdk < code) {
866 targetSdk = code;
867 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700868 printf("targetSdkVersion:'%d'\n", code);
869 }
870 } else if (tag == "uses-configuration") {
871 int32_t reqTouchScreen = getIntegerAttribute(tree,
872 REQ_TOUCH_SCREEN_ATTR, NULL, 0);
873 int32_t reqKeyboardType = getIntegerAttribute(tree,
874 REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
875 int32_t reqHardKeyboard = getIntegerAttribute(tree,
876 REQ_HARD_KEYBOARD_ATTR, NULL, 0);
877 int32_t reqNavigation = getIntegerAttribute(tree,
878 REQ_NAVIGATION_ATTR, NULL, 0);
879 int32_t reqFiveWayNav = getIntegerAttribute(tree,
880 REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
Dianne Hackborncb2d50d2010-01-06 11:29:54 -0800881 printf("uses-configuration:");
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700882 if (reqTouchScreen != 0) {
883 printf(" reqTouchScreen='%d'", reqTouchScreen);
884 }
885 if (reqKeyboardType != 0) {
886 printf(" reqKeyboardType='%d'", reqKeyboardType);
887 }
888 if (reqHardKeyboard != 0) {
889 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
890 }
891 if (reqNavigation != 0) {
892 printf(" reqNavigation='%d'", reqNavigation);
893 }
894 if (reqFiveWayNav != 0) {
895 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
896 }
897 printf("\n");
Dianne Hackborn723738c2009-06-25 19:48:04 -0700898 } else if (tag == "supports-screens") {
899 smallScreen = getIntegerAttribute(tree,
900 SMALL_SCREEN_ATTR, NULL, 1);
901 normalScreen = getIntegerAttribute(tree,
902 NORMAL_SCREEN_ATTR, NULL, 1);
903 largeScreen = getIntegerAttribute(tree,
904 LARGE_SCREEN_ATTR, NULL, 1);
Dianne Hackbornf43489d2010-08-20 12:44:33 -0700905 xlargeScreen = getIntegerAttribute(tree,
906 XLARGE_SCREEN_ATTR, NULL, 1);
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700907 anyDensity = getIntegerAttribute(tree,
908 ANY_DENSITY_ATTR, NULL, 1);
Dianne Hackborne289bff2011-06-13 19:33:22 -0700909 requiresSmallestWidthDp = getIntegerAttribute(tree,
910 REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
911 compatibleWidthLimitDp = getIntegerAttribute(tree,
912 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
913 largestWidthLimitDp = getIntegerAttribute(tree,
914 LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
Dianne Hackborne5276a72009-08-27 16:28:44 -0700915 } else if (tag == "uses-feature") {
916 String8 name = getAttribute(tree, NAME_ATTR, &error);
Suchi Amalapurapu40b94722009-09-20 13:39:37 -0700917
918 if (name != "" && error == "") {
Dianne Hackborne5276a72009-08-27 16:28:44 -0700919 int req = getIntegerAttribute(tree,
920 REQUIRED_ATTR, NULL, 1);
Dan Morrill89d97c12010-05-03 16:13:14 -0700921
Dianne Hackborne5276a72009-08-27 16:28:44 -0700922 if (name == "android.hardware.camera") {
923 specCameraFeature = true;
Dan Morrill89d97c12010-05-03 16:13:14 -0700924 } else if (name == "android.hardware.camera.autofocus") {
925 // these have no corresponding permission to check for,
926 // but should imply the foundational camera permission
927 reqCameraAutofocusFeature = reqCameraAutofocusFeature || req;
928 specCameraAutofocusFeature = true;
929 } else if (req && (name == "android.hardware.camera.flash")) {
930 // these have no corresponding permission to check for,
931 // but should imply the foundational camera permission
932 reqCameraFlashFeature = true;
933 } else if (name == "android.hardware.location") {
934 specLocationFeature = true;
935 } else if (name == "android.hardware.location.network") {
936 specNetworkLocFeature = true;
937 reqNetworkLocFeature = reqNetworkLocFeature || req;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800938 } else if (name == "android.hardware.location.gps") {
939 specGpsFeature = true;
Dan Morrill89d97c12010-05-03 16:13:14 -0700940 reqGpsFeature = reqGpsFeature || req;
941 } else if (name == "android.hardware.bluetooth") {
942 specBluetoothFeature = true;
943 } else if (name == "android.hardware.touchscreen") {
944 specTouchscreenFeature = true;
945 } else if (name == "android.hardware.touchscreen.multitouch") {
946 specMultitouchFeature = true;
947 } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
948 reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req;
949 } else if (name == "android.hardware.microphone") {
950 specMicrophoneFeature = true;
951 } else if (name == "android.hardware.wifi") {
952 specWiFiFeature = true;
953 } else if (name == "android.hardware.telephony") {
954 specTelephonyFeature = true;
955 } else if (req && (name == "android.hardware.telephony.gsm" ||
956 name == "android.hardware.telephony.cdma")) {
957 // these have no corresponding permission to check for,
958 // but should imply the foundational telephony permission
959 reqTelephonySubFeature = true;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700960 } else if (name == "android.hardware.screen.portrait") {
961 specScreenPortraitFeature = true;
962 } else if (name == "android.hardware.screen.landscape") {
963 specScreenLandscapeFeature = true;
Dianne Hackborne5276a72009-08-27 16:28:44 -0700964 }
965 printf("uses-feature%s:'%s'\n",
966 req ? "" : "-not-required", name.string());
967 } else {
968 int vers = getIntegerAttribute(tree,
969 GL_ES_VERSION_ATTR, &error);
970 if (error == "") {
971 printf("uses-gl-es:'0x%x'\n", vers);
972 }
973 }
974 } else if (tag == "uses-permission") {
975 String8 name = getAttribute(tree, NAME_ATTR, &error);
Suchi Amalapurapu40b94722009-09-20 13:39:37 -0700976 if (name != "" && error == "") {
Dianne Hackborne5276a72009-08-27 16:28:44 -0700977 if (name == "android.permission.CAMERA") {
978 hasCameraPermission = true;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800979 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
980 hasGpsPermission = true;
Dan Morrill89d97c12010-05-03 16:13:14 -0700981 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
982 hasMockLocPermission = true;
983 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
984 hasCoarseLocPermission = true;
985 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
986 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
987 hasGeneralLocPermission = true;
988 } else if (name == "android.permission.BLUETOOTH" ||
989 name == "android.permission.BLUETOOTH_ADMIN") {
990 hasBluetoothPermission = true;
991 } else if (name == "android.permission.RECORD_AUDIO") {
992 hasRecordAudioPermission = true;
993 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
994 name == "android.permission.CHANGE_WIFI_STATE" ||
995 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
996 hasWiFiPermission = true;
997 } else if (name == "android.permission.CALL_PHONE" ||
998 name == "android.permission.CALL_PRIVILEGED" ||
999 name == "android.permission.MODIFY_PHONE_STATE" ||
1000 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1001 name == "android.permission.READ_SMS" ||
1002 name == "android.permission.RECEIVE_SMS" ||
1003 name == "android.permission.RECEIVE_MMS" ||
1004 name == "android.permission.RECEIVE_WAP_PUSH" ||
1005 name == "android.permission.SEND_SMS" ||
1006 name == "android.permission.WRITE_APN_SETTINGS" ||
1007 name == "android.permission.WRITE_SMS") {
1008 hasTelephonyPermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001009 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1010 hasWriteExternalStoragePermission = true;
Dianne Hackborn79245122012-03-12 10:51:26 -07001011 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1012 hasReadExternalStoragePermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001013 } else if (name == "android.permission.READ_PHONE_STATE") {
1014 hasReadPhoneStatePermission = true;
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001015 } else if (name == "android.permission.READ_CONTACTS") {
1016 hasReadContactsPermission = true;
1017 } else if (name == "android.permission.WRITE_CONTACTS") {
1018 hasWriteContactsPermission = true;
1019 } else if (name == "android.permission.READ_CALL_LOG") {
1020 hasReadCallLogPermission = true;
1021 } else if (name == "android.permission.WRITE_CALL_LOG") {
1022 hasWriteCallLogPermission = true;
Dianne Hackborne5276a72009-08-27 16:28:44 -07001023 }
1024 printf("uses-permission:'%s'\n", name.string());
1025 } else {
1026 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1027 error.string());
1028 goto bail;
1029 }
Dianne Hackborn43b68032010-09-02 17:14:41 -07001030 } else if (tag == "uses-package") {
1031 String8 name = getAttribute(tree, NAME_ATTR, &error);
1032 if (name != "" && error == "") {
1033 printf("uses-package:'%s'\n", name.string());
1034 } else {
1035 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1036 error.string());
1037 goto bail;
1038 }
Jeff Hamiltone2c17f92010-02-12 13:45:16 -06001039 } else if (tag == "original-package") {
1040 String8 name = getAttribute(tree, NAME_ATTR, &error);
1041 if (name != "" && error == "") {
1042 printf("original-package:'%s'\n", name.string());
1043 } else {
1044 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1045 error.string());
1046 goto bail;
1047 }
Dan Morrill096b67f2010-12-13 16:25:54 -08001048 } else if (tag == "supports-gl-texture") {
Dan Morrill6f51fc12010-10-13 14:33:43 -07001049 String8 name = getAttribute(tree, NAME_ATTR, &error);
1050 if (name != "" && error == "") {
Dan Morrill096b67f2010-12-13 16:25:54 -08001051 printf("supports-gl-texture:'%s'\n", name.string());
Dan Morrill6f51fc12010-10-13 14:33:43 -07001052 } else {
1053 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1054 error.string());
1055 goto bail;
1056 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001057 } else if (tag == "compatible-screens") {
1058 printCompatibleScreens(tree);
1059 depth--;
Kenny Root56088a52011-09-29 13:49:45 -07001060 } else if (tag == "package-verifier") {
1061 String8 name = getAttribute(tree, NAME_ATTR, &error);
1062 if (name != "" && error == "") {
1063 String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
1064 if (publicKey != "" && error == "") {
1065 printf("package-verifier: name='%s' publicKey='%s'\n",
1066 name.string(), publicKey.string());
1067 }
1068 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001069 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001070 } else if (depth == 3 && withinApplication) {
1071 withinActivity = false;
1072 withinReceiver = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001073 withinService = false;
1074 hasIntentFilter = false;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001075 if(tag == "activity") {
1076 withinActivity = true;
1077 activityName = getAttribute(tree, NAME_ATTR, &error);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001078 if (error != "") {
1079 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1080 goto bail;
1081 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001082
1083 activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001084 if (error != "") {
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001085 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001086 goto bail;
1087 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001088
1089 activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1090 if (error != "") {
1091 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
1092 goto bail;
1093 }
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001094
1095 int32_t orien = getResolvedIntegerAttribute(&res, tree,
1096 SCREEN_ORIENTATION_ATTR, &error);
1097 if (error == "") {
1098 if (orien == 0 || orien == 6 || orien == 8) {
1099 // Requests landscape, sensorLandscape, or reverseLandscape.
1100 reqScreenLandscapeFeature = true;
1101 } else if (orien == 1 || orien == 7 || orien == 9) {
1102 // Requests portrait, sensorPortrait, or reversePortrait.
1103 reqScreenPortraitFeature = true;
1104 }
1105 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001106 } else if (tag == "uses-library") {
1107 String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1108 if (error != "") {
1109 fprintf(stderr, "ERROR getting 'android:name' attribute for uses-library: %s\n", error.string());
1110 goto bail;
1111 }
Dianne Hackborn49237342009-08-27 20:08:01 -07001112 int req = getIntegerAttribute(tree,
1113 REQUIRED_ATTR, NULL, 1);
1114 printf("uses-library%s:'%s'\n",
1115 req ? "" : "-not-required", libraryName.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001116 } else if (tag == "receiver") {
1117 withinReceiver = true;
1118 receiverName = getAttribute(tree, NAME_ATTR, &error);
1119
1120 if (error != "") {
1121 fprintf(stderr, "ERROR getting 'android:name' attribute for receiver: %s\n", error.string());
1122 goto bail;
1123 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001124 } else if (tag == "service") {
1125 withinService = true;
1126 serviceName = getAttribute(tree, NAME_ATTR, &error);
1127
1128 if (error != "") {
1129 fprintf(stderr, "ERROR getting 'android:name' attribute for service: %s\n", error.string());
1130 goto bail;
1131 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001132 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001133 } else if ((depth == 4) && (tag == "intent-filter")) {
1134 hasIntentFilter = true;
1135 withinIntentFilter = true;
1136 actMainActivity = actWidgetReceivers = actImeService = actWallpaperService = false;
1137 } else if ((depth == 5) && withinIntentFilter){
1138 String8 action;
1139 if (tag == "action") {
1140 action = getAttribute(tree, NAME_ATTR, &error);
1141 if (error != "") {
1142 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1143 goto bail;
1144 }
1145 if (withinActivity) {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001146 if (action == "android.intent.action.MAIN") {
1147 isMainActivity = true;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001148 actMainActivity = true;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001149 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001150 } else if (withinReceiver) {
1151 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1152 actWidgetReceivers = true;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001153 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001154 } else if (withinService) {
1155 if (action == "android.view.InputMethod") {
1156 actImeService = true;
1157 } else if (action == "android.service.wallpaper.WallpaperService") {
1158 actWallpaperService = true;
1159 }
1160 }
1161 if (action == "android.intent.action.SEARCH") {
1162 isSearchable = true;
1163 }
1164 }
1165
1166 if (tag == "category") {
1167 String8 category = getAttribute(tree, NAME_ATTR, &error);
1168 if (error != "") {
1169 fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string());
1170 goto bail;
1171 }
1172 if (withinActivity) {
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001173 if (category == "android.intent.category.LAUNCHER") {
1174 isLauncherActivity = true;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001175 }
1176 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001177 }
1178 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001179 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001180
Kenny Root063a44e2011-12-08 08:46:03 -08001181 // Pre-1.6 implicitly granted permission compatibility logic
1182 if (targetSdk < 4) {
1183 if (!hasWriteExternalStoragePermission) {
1184 printf("uses-permission:'android.permission.WRITE_EXTERNAL_STORAGE'\n");
Dianne Hackborn79245122012-03-12 10:51:26 -07001185 hasWriteExternalStoragePermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001186 }
1187 if (!hasReadPhoneStatePermission) {
1188 printf("uses-permission:'android.permission.READ_PHONE_STATE'\n");
1189 }
1190 }
1191
Dianne Hackborn79245122012-03-12 10:51:26 -07001192 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1193 // force them to always take READ_EXTERNAL_STORAGE as well.
1194 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
1195 printf("uses-permission:'android.permission.READ_EXTERNAL_STORAGE'\n");
1196 }
1197
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001198 // Pre-JellyBean call log permission compatibility.
1199 if (targetSdk < 16) {
1200 if (!hasReadCallLogPermission && hasReadContactsPermission) {
1201 printf("uses-permission:'android.permission.READ_CALL_LOG'\n");
1202 }
1203 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
1204 printf("uses-permission:'android.permission.WRITE_CALL_LOG'\n");
1205 }
1206 }
1207
Dan Morrill89d97c12010-05-03 16:13:14 -07001208 /* The following blocks handle printing "inferred" uses-features, based
1209 * on whether related features or permissions are used by the app.
1210 * Note that the various spec*Feature variables denote whether the
1211 * relevant tag was *present* in the AndroidManfest, not that it was
1212 * present and set to true.
1213 */
1214 // Camera-related back-compatibility logic
1215 if (!specCameraFeature) {
1216 if (reqCameraFlashFeature || reqCameraAutofocusFeature) {
1217 // if app requested a sub-feature (autofocus or flash) and didn't
1218 // request the base camera feature, we infer that it meant to
1219 printf("uses-feature:'android.hardware.camera'\n");
1220 } else if (hasCameraPermission) {
1221 // if app wants to use camera but didn't request the feature, we infer
1222 // that it meant to, and further that it wants autofocus
1223 // (which was the 1.0 - 1.5 behavior)
1224 printf("uses-feature:'android.hardware.camera'\n");
1225 if (!specCameraAutofocusFeature) {
1226 printf("uses-feature:'android.hardware.camera.autofocus'\n");
1227 }
1228 }
Dianne Hackborne5276a72009-08-27 16:28:44 -07001229 }
Doug Zongkerdbe7a682009-10-09 11:24:51 -07001230
Dan Morrill89d97c12010-05-03 16:13:14 -07001231 // Location-related back-compatibility logic
1232 if (!specLocationFeature &&
1233 (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission ||
1234 hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) {
1235 // if app either takes a location-related permission or requests one of the
1236 // sub-features, we infer that it also meant to request the base location feature
1237 printf("uses-feature:'android.hardware.location'\n");
1238 }
Dianne Hackbornef05e072010-03-01 17:43:39 -08001239 if (!specGpsFeature && hasGpsPermission) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001240 // if app takes GPS (FINE location) perm but does not request the GPS
1241 // feature, we infer that it meant to
Dianne Hackbornef05e072010-03-01 17:43:39 -08001242 printf("uses-feature:'android.hardware.location.gps'\n");
1243 }
Dan Morrill89d97c12010-05-03 16:13:14 -07001244 if (!specNetworkLocFeature && hasCoarseLocPermission) {
1245 // if app takes Network location (COARSE location) perm but does not request the
1246 // network location feature, we infer that it meant to
1247 printf("uses-feature:'android.hardware.location.network'\n");
1248 }
1249
1250 // Bluetooth-related compatibility logic
Dan Morrill6b22d812010-06-15 21:41:42 -07001251 if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001252 // if app takes a Bluetooth permission but does not request the Bluetooth
1253 // feature, we infer that it meant to
1254 printf("uses-feature:'android.hardware.bluetooth'\n");
1255 }
1256
1257 // Microphone-related compatibility logic
1258 if (!specMicrophoneFeature && hasRecordAudioPermission) {
1259 // if app takes the record-audio permission but does not request the microphone
1260 // feature, we infer that it meant to
1261 printf("uses-feature:'android.hardware.microphone'\n");
1262 }
1263
1264 // WiFi-related compatibility logic
1265 if (!specWiFiFeature && hasWiFiPermission) {
1266 // if app takes one of the WiFi permissions but does not request the WiFi
1267 // feature, we infer that it meant to
1268 printf("uses-feature:'android.hardware.wifi'\n");
1269 }
1270
1271 // Telephony-related compatibility logic
1272 if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) {
1273 // if app takes one of the telephony permissions or requests a sub-feature but
1274 // does not request the base telephony feature, we infer that it meant to
1275 printf("uses-feature:'android.hardware.telephony'\n");
1276 }
1277
1278 // Touchscreen-related back-compatibility logic
1279 if (!specTouchscreenFeature) { // not a typo!
1280 // all apps are presumed to require a touchscreen, unless they explicitly say
1281 // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
1282 // Note that specTouchscreenFeature is true if the tag is present, regardless
1283 // of whether its value is true or false, so this is safe
1284 printf("uses-feature:'android.hardware.touchscreen'\n");
1285 }
1286 if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
1287 // if app takes one of the telephony permissions or requests a sub-feature but
1288 // does not request the base telephony feature, we infer that it meant to
1289 printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
1290 }
Dianne Hackbornef05e072010-03-01 17:43:39 -08001291
Dianne Hackborne289bff2011-06-13 19:33:22 -07001292 // Landscape/portrait-related compatibility logic
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001293 if (!specScreenLandscapeFeature && !specScreenPortraitFeature) {
1294 // If the app has specified any activities in its manifest
1295 // that request a specific orientation, then assume that
1296 // orientation is required.
1297 if (reqScreenLandscapeFeature) {
1298 printf("uses-feature:'android.hardware.screen.landscape'\n");
1299 }
1300 if (reqScreenPortraitFeature) {
1301 printf("uses-feature:'android.hardware.screen.portrait'\n");
1302 }
Dianne Hackborne289bff2011-06-13 19:33:22 -07001303 }
1304
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001305 if (hasMainActivity) {
1306 printf("main\n");
1307 }
1308 if (hasWidgetReceivers) {
1309 printf("app-widget\n");
1310 }
1311 if (hasImeService) {
1312 printf("ime\n");
1313 }
1314 if (hasWallpaperService) {
1315 printf("wallpaper\n");
1316 }
1317 if (hasOtherActivities) {
1318 printf("other-activities\n");
1319 }
1320 if (isSearchable) {
1321 printf("search\n");
1322 }
1323 if (hasOtherReceivers) {
1324 printf("other-receivers\n");
1325 }
1326 if (hasOtherServices) {
1327 printf("other-services\n");
1328 }
1329
Dianne Hackborne289bff2011-06-13 19:33:22 -07001330 // For modern apps, if screen size buckets haven't been specified
1331 // but the new width ranges have, then infer the buckets from them.
1332 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1333 && requiresSmallestWidthDp > 0) {
1334 int compatWidth = compatibleWidthLimitDp;
1335 if (compatWidth <= 0) compatWidth = requiresSmallestWidthDp;
1336 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1337 smallScreen = -1;
1338 } else {
1339 smallScreen = 0;
1340 }
1341 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1342 normalScreen = -1;
1343 } else {
1344 normalScreen = 0;
1345 }
1346 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1347 largeScreen = -1;
1348 } else {
1349 largeScreen = 0;
1350 }
1351 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1352 xlargeScreen = -1;
1353 } else {
1354 xlargeScreen = 0;
1355 }
1356 }
1357
Dianne Hackborn723738c2009-06-25 19:48:04 -07001358 // Determine default values for any unspecified screen sizes,
1359 // based on the target SDK of the package. As of 4 (donut)
1360 // the screen size support was introduced, so all default to
1361 // enabled.
1362 if (smallScreen > 0) {
1363 smallScreen = targetSdk >= 4 ? -1 : 0;
1364 }
1365 if (normalScreen > 0) {
1366 normalScreen = -1;
1367 }
1368 if (largeScreen > 0) {
1369 largeScreen = targetSdk >= 4 ? -1 : 0;
1370 }
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001371 if (xlargeScreen > 0) {
Scott Maind58fb972010-11-04 18:32:00 -07001372 // Introduced in Gingerbread.
1373 xlargeScreen = targetSdk >= 9 ? -1 : 0;
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001374 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001375 if (anyDensity > 0) {
Dianne Hackborne289bff2011-06-13 19:33:22 -07001376 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1377 || compatibleWidthLimitDp > 0) ? -1 : 0;
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001378 }
Dianne Hackborn723738c2009-06-25 19:48:04 -07001379 printf("supports-screens:");
1380 if (smallScreen != 0) printf(" 'small'");
1381 if (normalScreen != 0) printf(" 'normal'");
1382 if (largeScreen != 0) printf(" 'large'");
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001383 if (xlargeScreen != 0) printf(" 'xlarge'");
Dianne Hackborn723738c2009-06-25 19:48:04 -07001384 printf("\n");
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001385 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
Dianne Hackborne289bff2011-06-13 19:33:22 -07001386 if (requiresSmallestWidthDp > 0) {
1387 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1388 }
1389 if (compatibleWidthLimitDp > 0) {
1390 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1391 }
1392 if (largestWidthLimitDp > 0) {
1393 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1394 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001395
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001396 printf("locales:");
Dianne Hackborne17086b2009-06-19 15:13:28 -07001397 const size_t NL = locales.size();
1398 for (size_t i=0; i<NL; i++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001399 const char* localeStr = locales[i].string();
1400 if (localeStr == NULL || strlen(localeStr) == 0) {
1401 localeStr = "--_--";
1402 }
1403 printf(" '%s'", localeStr);
1404 }
1405 printf("\n");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001406
Dianne Hackborne17086b2009-06-19 15:13:28 -07001407 printf("densities:");
1408 const size_t ND = densities.size();
1409 for (size_t i=0; i<ND; i++) {
1410 printf(" '%d'", densities[i]);
1411 }
1412 printf("\n");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001413
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001414 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1415 if (dir != NULL) {
1416 if (dir->getFileCount() > 0) {
1417 printf("native-code:");
1418 for (size_t i=0; i<dir->getFileCount(); i++) {
1419 printf(" '%s'", dir->getFileName(i).string());
1420 }
1421 printf("\n");
1422 }
1423 delete dir;
1424 }
Dan Morrille74763e2012-01-06 10:47:10 -08001425 } else if (strcmp("badger", option) == 0) {
Dianne Hackborn6c997a92012-01-31 11:27:43 -08001426 printf("%s", CONSOLE_DATA);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001427 } else if (strcmp("configurations", option) == 0) {
1428 Vector<ResTable_config> configs;
1429 res.getConfigurations(&configs);
1430 const size_t N = configs.size();
1431 for (size_t i=0; i<N; i++) {
1432 printf("%s\n", configs[i].toString().string());
1433 }
1434 } else {
1435 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
1436 goto bail;
1437 }
1438 }
1439
1440 result = NO_ERROR;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001441
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001442bail:
1443 if (asset) {
1444 delete asset;
1445 }
1446 return (result != NO_ERROR);
1447}
1448
1449
1450/*
1451 * Handle the "add" command, which wants to add files to a new or
1452 * pre-existing archive.
1453 */
1454int doAdd(Bundle* bundle)
1455{
1456 ZipFile* zip = NULL;
1457 status_t result = UNKNOWN_ERROR;
1458 const char* zipFileName;
1459
1460 if (bundle->getUpdate()) {
1461 /* avoid confusion */
1462 fprintf(stderr, "ERROR: can't use '-u' with add\n");
1463 goto bail;
1464 }
1465
1466 if (bundle->getFileSpecCount() < 1) {
1467 fprintf(stderr, "ERROR: must specify zip file name\n");
1468 goto bail;
1469 }
1470 zipFileName = bundle->getFileSpecEntry(0);
1471
1472 if (bundle->getFileSpecCount() < 2) {
1473 fprintf(stderr, "NOTE: nothing to do\n");
1474 goto bail;
1475 }
1476
1477 zip = openReadWrite(zipFileName, true);
1478 if (zip == NULL) {
1479 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
1480 goto bail;
1481 }
1482
1483 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1484 const char* fileName = bundle->getFileSpecEntry(i);
1485
1486 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
1487 printf(" '%s'... (from gzip)\n", fileName);
1488 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
1489 } else {
Doug Zongkerdbe7a682009-10-09 11:24:51 -07001490 if (bundle->getJunkPath()) {
1491 String8 storageName = String8(fileName).getPathLeaf();
1492 printf(" '%s' as '%s'...\n", fileName, storageName.string());
1493 result = zip->add(fileName, storageName.string(),
1494 bundle->getCompressionMethod(), NULL);
1495 } else {
1496 printf(" '%s'...\n", fileName);
1497 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
1498 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001499 }
1500 if (result != NO_ERROR) {
1501 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
1502 if (result == NAME_NOT_FOUND)
1503 fprintf(stderr, ": file not found\n");
1504 else if (result == ALREADY_EXISTS)
1505 fprintf(stderr, ": already exists in archive\n");
1506 else
1507 fprintf(stderr, "\n");
1508 goto bail;
1509 }
1510 }
1511
1512 result = NO_ERROR;
1513
1514bail:
1515 delete zip;
1516 return (result != NO_ERROR);
1517}
1518
1519
1520/*
1521 * Delete files from an existing archive.
1522 */
1523int doRemove(Bundle* bundle)
1524{
1525 ZipFile* zip = NULL;
1526 status_t result = UNKNOWN_ERROR;
1527 const char* zipFileName;
1528
1529 if (bundle->getFileSpecCount() < 1) {
1530 fprintf(stderr, "ERROR: must specify zip file name\n");
1531 goto bail;
1532 }
1533 zipFileName = bundle->getFileSpecEntry(0);
1534
1535 if (bundle->getFileSpecCount() < 2) {
1536 fprintf(stderr, "NOTE: nothing to do\n");
1537 goto bail;
1538 }
1539
1540 zip = openReadWrite(zipFileName, false);
1541 if (zip == NULL) {
1542 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
1543 zipFileName);
1544 goto bail;
1545 }
1546
1547 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1548 const char* fileName = bundle->getFileSpecEntry(i);
1549 ZipEntry* entry;
1550
1551 entry = zip->getEntryByName(fileName);
1552 if (entry == NULL) {
1553 printf(" '%s' NOT FOUND\n", fileName);
1554 continue;
1555 }
1556
1557 result = zip->remove(entry);
1558
1559 if (result != NO_ERROR) {
1560 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
1561 bundle->getFileSpecEntry(i), zipFileName);
1562 goto bail;
1563 }
1564 }
1565
1566 /* update the archive */
1567 zip->flush();
1568
1569bail:
1570 delete zip;
1571 return (result != NO_ERROR);
1572}
1573
1574
1575/*
1576 * Package up an asset directory and associated application files.
1577 */
1578int doPackage(Bundle* bundle)
1579{
1580 const char* outputAPKFile;
1581 int retVal = 1;
1582 status_t err;
1583 sp<AaptAssets> assets;
1584 int N;
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001585 FILE* fp;
1586 String8 dependencyFile;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001587
1588 // -c zz_ZZ means do pseudolocalization
1589 ResourceFilter filter;
1590 err = filter.parse(bundle->getConfigurations());
1591 if (err != NO_ERROR) {
1592 goto bail;
1593 }
1594 if (filter.containsPseudo()) {
1595 bundle->setPseudolocalize(true);
1596 }
1597
1598 N = bundle->getFileSpecCount();
1599 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
1600 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDir() == NULL) {
1601 fprintf(stderr, "ERROR: no input files\n");
1602 goto bail;
1603 }
1604
1605 outputAPKFile = bundle->getOutputAPKFile();
1606
1607 // Make sure the filenames provided exist and are of the appropriate type.
1608 if (outputAPKFile) {
1609 FileType type;
1610 type = getFileType(outputAPKFile);
1611 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
1612 fprintf(stderr,
1613 "ERROR: output file '%s' exists but is not regular file\n",
1614 outputAPKFile);
1615 goto bail;
1616 }
1617 }
1618
1619 // Load the assets.
1620 assets = new AaptAssets();
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001621
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001622 // Set up the resource gathering in assets if we're going to generate
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001623 // dependency files. Every time we encounter a resource while slurping
1624 // the tree, we'll add it to these stores so we have full resource paths
1625 // to write to a dependency file.
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001626 if (bundle->getGenDependencies()) {
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001627 sp<FilePathStore> resPathStore = new FilePathStore;
1628 assets->setFullResPaths(resPathStore);
1629 sp<FilePathStore> assetPathStore = new FilePathStore;
1630 assets->setFullAssetPaths(assetPathStore);
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001631 }
1632
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001633 err = assets->slurpFromArgs(bundle);
1634 if (err < 0) {
1635 goto bail;
1636 }
1637
1638 if (bundle->getVerbose()) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001639 assets->print(String8());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001640 }
1641
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001642 // If they asked for any fileAs that need to be compiled, do so.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001643 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
1644 err = buildResources(bundle, assets);
1645 if (err != 0) {
1646 goto bail;
1647 }
1648 }
1649
1650 // At this point we've read everything and processed everything. From here
1651 // on out it's just writing output files.
1652 if (SourcePos::hasErrors()) {
1653 goto bail;
1654 }
1655
Dianne Hackborn1644c6d2012-02-06 15:33:21 -08001656 // Update symbols with information about which ones are needed as Java symbols.
1657 assets->applyJavaSymbols();
1658 if (SourcePos::hasErrors()) {
1659 goto bail;
1660 }
1661
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001662 // If we've been asked to generate a dependency file, do that here
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001663 if (bundle->getGenDependencies()) {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001664 // If this is the packaging step, generate the dependency file next to
1665 // the output apk (e.g. bin/resources.ap_.d)
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001666 if (outputAPKFile) {
1667 dependencyFile = String8(outputAPKFile);
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001668 // Add the .d extension to the dependency file.
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001669 dependencyFile.append(".d");
1670 } else {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001671 // Else if this is the R.java dependency generation step,
1672 // generate the dependency file in the R.java package subdirectory
1673 // e.g. gen/com/foo/app/R.java.d
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001674 dependencyFile = String8(bundle->getRClassDir());
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001675 dependencyFile.appendPath("R.java.d");
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001676 }
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001677 // Make sure we have a clean dependency file to start with
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001678 fp = fopen(dependencyFile, "w");
1679 fclose(fp);
1680 }
1681
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001682 // Write out R.java constants
Dianne Hackborn1644c6d2012-02-06 15:33:21 -08001683 if (!assets->havePrivateSymbols()) {
Xavier Ducrohet63459ad2009-11-30 18:05:10 -08001684 if (bundle->getCustomPackage() == NULL) {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001685 // Write the R.java file into the appropriate class directory
1686 // e.g. gen/com/foo/app/R.java
Xavier Ducrohet63459ad2009-11-30 18:05:10 -08001687 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001688 // If we have library files, we're going to write our R.java file into
1689 // the appropriate class directory for those libraries as well.
1690 // e.g. gen/com/foo/app/lib/R.java
Josiah Gaskince89f152011-06-08 19:31:40 -07001691 if (bundle->getExtraPackages() != NULL) {
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001692 // Split on colon
Josiah Gaskince89f152011-06-08 19:31:40 -07001693 String8 libs(bundle->getExtraPackages());
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001694 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
Josiah Gaskince89f152011-06-08 19:31:40 -07001695 while (packageString != NULL) {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001696 // Write the R.java file out with the correct package name
Josiah Gaskince89f152011-06-08 19:31:40 -07001697 err = writeResourceSymbols(bundle, assets, String8(packageString), true);
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001698 packageString = strtok(NULL, ":");
Josiah Gaskince89f152011-06-08 19:31:40 -07001699 }
1700 libs.unlockBuffer();
1701 }
Xavier Ducrohet63459ad2009-11-30 18:05:10 -08001702 } else {
1703 const String8 customPkg(bundle->getCustomPackage());
1704 err = writeResourceSymbols(bundle, assets, customPkg, true);
1705 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001706 if (err < 0) {
1707 goto bail;
1708 }
1709 } else {
1710 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
1711 if (err < 0) {
1712 goto bail;
1713 }
1714 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
1715 if (err < 0) {
1716 goto bail;
1717 }
1718 }
1719
Joe Onorato1553c822009-08-30 13:36:22 -07001720 // Write out the ProGuard file
1721 err = writeProguardFile(bundle, assets);
1722 if (err < 0) {
1723 goto bail;
1724 }
1725
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001726 // Write the apk
1727 if (outputAPKFile) {
1728 err = writeAPK(bundle, assets, String8(outputAPKFile));
1729 if (err != NO_ERROR) {
1730 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
1731 goto bail;
1732 }
1733 }
1734
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001735 // If we've been asked to generate a dependency file, we need to finish up here.
1736 // the writeResourceSymbols and writeAPK functions have already written the target
1737 // half of the dependency file, now we need to write the prerequisites. (files that
1738 // the R.java file or .ap_ file depend on)
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001739 if (bundle->getGenDependencies()) {
1740 // Now that writeResourceSymbols or writeAPK has taken care of writing
1741 // the targets to our dependency file, we'll write the prereqs
1742 fp = fopen(dependencyFile, "a+");
1743 fprintf(fp, " : ");
1744 bool includeRaw = (outputAPKFile != NULL);
1745 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001746 // Also manually add the AndroidManifeset since it's not under res/ or assets/
1747 // and therefore was not added to our pathstores during slurping
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001748 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
1749 fclose(fp);
1750 }
1751
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001752 retVal = 0;
1753bail:
1754 if (SourcePos::hasErrors()) {
1755 SourcePos::printErrors(stderr);
1756 }
1757 return retVal;
1758}
Josiah Gaskin8a39da82011-06-06 17:00:35 -07001759
1760/*
1761 * Do PNG Crunching
1762 * PRECONDITIONS
1763 * -S flag points to a source directory containing drawable* folders
1764 * -C flag points to destination directory. The folder structure in the
1765 * source directory will be mirrored to the destination (cache) directory
1766 *
1767 * POSTCONDITIONS
1768 * Destination directory will be updated to match the PNG files in
1769 * the source directory.
1770 */
1771int doCrunch(Bundle* bundle)
1772{
1773 fprintf(stdout, "Crunching PNG Files in ");
1774 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
1775 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
1776
1777 updatePreProcessedCache(bundle);
1778
1779 return NO_ERROR;
1780}
Dan Morrille74763e2012-01-06 10:47:10 -08001781
1782char CONSOLE_DATA[2925] = {
1783 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1784 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1785 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1786 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1787 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
1788 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
1789 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1790 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1791 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
1792 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
1793 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1794 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1795 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
1796 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1797 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1798 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
1799 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
1800 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1801 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1802 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
1803 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
1804 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
1805 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
1806 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
1807 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1808 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1809 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
1810 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
1811 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
1812 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1813 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
1814 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1815 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1816 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
1817 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
1818 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1819 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1820 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
1821 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
1822 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
1823 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1824 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
1825 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1826 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1827 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
1828 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
1829 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
1830 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
1831 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
1832 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1833 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
1834 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
1835 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
1836 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1837 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
1838 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
1839 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
1840 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
1841 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
1842 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
1843 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
1844 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
1845 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
1846 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
1847 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
1848 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
1849 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
1850 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
1851 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
1852 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
1853 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
1854 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
1855 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1856 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
1857 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
1858 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
1859 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1860 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
1861 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
1862 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1863 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
1864 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
1865 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
1866 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1867 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
1868 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
1869 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1870 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
1871 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
1872 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1873 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1874 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
1875 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
1876 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1877 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1878 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
1879 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1880 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
1881 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
1882 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
1883 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1884 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1885 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
1886 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
1887 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
1888 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1889 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
1890 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1891 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1892 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
1893 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
1894 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1895 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1896 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
1897 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1898 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
1899 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
1900 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
1901 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1902 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1903 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
1904 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
1905 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
1906 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
1907 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
1908 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
1909 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
1910 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
1911 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
1912 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1913 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1914 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
1915 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
1916 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
1917 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
1918 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
1919 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1920 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1921 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
1922 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
1923 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
1924 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1925 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
1926 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1927 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1928 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
1929 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
1930 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1931 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1932 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
1933 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
1934 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1935 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
1936 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
1937 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1938 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1939 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
1940 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
1941 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1942 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1943 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1944 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1945 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
1946 };