blob: cbd591f971a93bdb314dc2ec1b00ebf0aa7b9b9b [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,
Dan Morrillb6ec11e2012-04-03 12:44:40 -0700348 DEBUGGABLE_ATTR = 0x0101000f,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800349 VERSION_CODE_ATTR = 0x0101021b,
350 VERSION_NAME_ATTR = 0x0101021c,
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -0700351 SCREEN_ORIENTATION_ATTR = 0x0101001e,
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700352 MIN_SDK_VERSION_ATTR = 0x0101020c,
Suchi Amalapurapu75c49842009-08-14 15:13:09 -0700353 MAX_SDK_VERSION_ATTR = 0x01010271,
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700354 REQ_TOUCH_SCREEN_ATTR = 0x01010227,
355 REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
356 REQ_HARD_KEYBOARD_ATTR = 0x01010229,
357 REQ_NAVIGATION_ATTR = 0x0101022a,
358 REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
359 TARGET_SDK_VERSION_ATTR = 0x01010270,
360 TEST_ONLY_ATTR = 0x01010272,
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700361 ANY_DENSITY_ATTR = 0x0101026c,
Dianne Hackborne5276a72009-08-27 16:28:44 -0700362 GL_ES_VERSION_ATTR = 0x01010281,
Dianne Hackborn723738c2009-06-25 19:48:04 -0700363 SMALL_SCREEN_ATTR = 0x01010284,
364 NORMAL_SCREEN_ATTR = 0x01010285,
365 LARGE_SCREEN_ATTR = 0x01010286,
Dianne Hackbornf43489d2010-08-20 12:44:33 -0700366 XLARGE_SCREEN_ATTR = 0x010102bf,
Dianne Hackborne5276a72009-08-27 16:28:44 -0700367 REQUIRED_ATTR = 0x0101028e,
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700368 SCREEN_SIZE_ATTR = 0x010102ca,
369 SCREEN_DENSITY_ATTR = 0x010102cb,
Dianne Hackborne289bff2011-06-13 19:33:22 -0700370 REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
371 COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
372 LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
Kenny Root56088a552011-09-29 13:49:45 -0700373 PUBLIC_KEY_ATTR = 0x010103a6,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800374};
375
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700376const char *getComponentName(String8 &pkgName, String8 &componentName) {
377 ssize_t idx = componentName.find(".");
378 String8 retStr(pkgName);
379 if (idx == 0) {
380 retStr += componentName;
381 } else if (idx < 0) {
382 retStr += ".";
383 retStr += componentName;
384 } else {
385 return componentName.string();
386 }
387 return retStr.string();
388}
389
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700390static void printCompatibleScreens(ResXMLTree& tree) {
391 size_t len;
392 ResXMLTree::event_code_t code;
393 int depth = 0;
394 bool first = true;
395 printf("compatible-screens:");
396 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
397 if (code == ResXMLTree::END_TAG) {
398 depth--;
399 if (depth < 0) {
400 break;
401 }
402 continue;
403 }
404 if (code != ResXMLTree::START_TAG) {
405 continue;
406 }
407 depth++;
408 String8 tag(tree.getElementName(&len));
409 if (tag == "screen") {
410 int32_t screenSize = getIntegerAttribute(tree,
411 SCREEN_SIZE_ATTR, NULL, -1);
412 int32_t screenDensity = getIntegerAttribute(tree,
413 SCREEN_DENSITY_ATTR, NULL, -1);
414 if (screenSize > 0 && screenDensity > 0) {
415 if (!first) {
416 printf(",");
417 }
418 first = false;
419 printf("'%d/%d'", screenSize, screenDensity);
420 }
421 }
422 }
423 printf("\n");
424}
425
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800426/*
427 * Handle the "dump" command, to extract select data from an archive.
428 */
Dan Morrille74763e2012-01-06 10:47:10 -0800429extern char CONSOLE_DATA[2925]; // see EOF
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800430int doDump(Bundle* bundle)
431{
432 status_t result = UNKNOWN_ERROR;
433 Asset* asset = NULL;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700434
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800435 if (bundle->getFileSpecCount() < 1) {
436 fprintf(stderr, "ERROR: no dump option specified\n");
437 return 1;
438 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700439
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800440 if (bundle->getFileSpecCount() < 2) {
441 fprintf(stderr, "ERROR: no dump file specified\n");
442 return 1;
443 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700444
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800445 const char* option = bundle->getFileSpecEntry(0);
446 const char* filename = bundle->getFileSpecEntry(1);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700447
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800448 AssetManager assets;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700449 void* assetsCookie;
450 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800451 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
452 return 1;
453 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700454
Dianne Hackborne289bff2011-06-13 19:33:22 -0700455 // Make a dummy config for retrieving resources... we need to supply
456 // non-default values for some configs so that we can retrieve resources
457 // in the app that don't have a default. The most important of these is
458 // the API version because key resources like icons will have an implicit
459 // version if they are using newer config types like density.
460 ResTable_config config;
461 config.language[0] = 'e';
462 config.language[1] = 'n';
463 config.country[0] = 'U';
464 config.country[1] = 'S';
465 config.orientation = ResTable_config::ORIENTATION_PORT;
466 config.density = ResTable_config::DENSITY_MEDIUM;
467 config.sdkVersion = 10000; // Very high.
468 config.screenWidthDp = 320;
469 config.screenHeightDp = 480;
470 config.smallestScreenWidthDp = 320;
471 assets.setConfiguration(config);
472
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800473 const ResTable& res = assets.getResources(false);
474 if (&res == NULL) {
475 fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
476 goto bail;
477 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700478
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800479 if (strcmp("resources", option) == 0) {
Steve Blockf1ff21a2010-06-14 17:34:04 +0100480#ifndef HAVE_ANDROID_OS
Dianne Hackborne17086b2009-06-19 15:13:28 -0700481 res.print(bundle->getValues());
Steve Blockf1ff21a2010-06-14 17:34:04 +0100482#endif
Dianne Hackborn6c997a92012-01-31 11:27:43 -0800483
484 } else if (strcmp("strings", option) == 0) {
485 const ResStringPool* pool = res.getTableStringBlock(0);
486 printStringPool(pool);
487
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800488 } else if (strcmp("xmltree", option) == 0) {
489 if (bundle->getFileSpecCount() < 3) {
490 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
491 goto bail;
492 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700493
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800494 for (int i=2; i<bundle->getFileSpecCount(); i++) {
495 const char* resname = bundle->getFileSpecEntry(i);
496 ResXMLTree tree;
497 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
498 if (asset == NULL) {
Kenny Root44b283d2009-09-01 19:03:11 -0500499 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800500 goto bail;
501 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700502
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800503 if (tree.setTo(asset->getBuffer(true),
504 asset->getLength()) != NO_ERROR) {
505 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
506 goto bail;
507 }
508 tree.restart();
509 printXMLBlock(&tree);
Kenny Root19138462009-12-04 09:38:48 -0800510 tree.uninit();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800511 delete asset;
512 asset = NULL;
513 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700514
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800515 } else if (strcmp("xmlstrings", option) == 0) {
516 if (bundle->getFileSpecCount() < 3) {
517 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
518 goto bail;
519 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700520
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800521 for (int i=2; i<bundle->getFileSpecCount(); i++) {
522 const char* resname = bundle->getFileSpecEntry(i);
523 ResXMLTree tree;
524 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
525 if (asset == NULL) {
Kenny Root44b283d2009-09-01 19:03:11 -0500526 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800527 goto bail;
528 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700529
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800530 if (tree.setTo(asset->getBuffer(true),
531 asset->getLength()) != NO_ERROR) {
532 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
533 goto bail;
534 }
535 printStringPool(&tree.getStrings());
536 delete asset;
537 asset = NULL;
538 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700539
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800540 } else {
541 ResXMLTree tree;
542 asset = assets.openNonAsset("AndroidManifest.xml",
543 Asset::ACCESS_BUFFER);
544 if (asset == NULL) {
545 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
546 goto bail;
547 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700548
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800549 if (tree.setTo(asset->getBuffer(true),
550 asset->getLength()) != NO_ERROR) {
551 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
552 goto bail;
553 }
554 tree.restart();
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700555
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800556 if (strcmp("permissions", option) == 0) {
557 size_t len;
558 ResXMLTree::event_code_t code;
559 int depth = 0;
560 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
561 if (code == ResXMLTree::END_TAG) {
562 depth--;
563 continue;
564 }
565 if (code != ResXMLTree::START_TAG) {
566 continue;
567 }
568 depth++;
569 String8 tag(tree.getElementName(&len));
570 //printf("Depth %d tag %s\n", depth, tag.string());
571 if (depth == 1) {
572 if (tag != "manifest") {
573 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
574 goto bail;
575 }
576 String8 pkg = getAttribute(tree, NULL, "package", NULL);
577 printf("package: %s\n", pkg.string());
578 } else if (depth == 2 && tag == "permission") {
579 String8 error;
580 String8 name = getAttribute(tree, NAME_ATTR, &error);
581 if (error != "") {
582 fprintf(stderr, "ERROR: %s\n", error.string());
583 goto bail;
584 }
585 printf("permission: %s\n", name.string());
586 } else if (depth == 2 && tag == "uses-permission") {
587 String8 error;
588 String8 name = getAttribute(tree, NAME_ATTR, &error);
589 if (error != "") {
590 fprintf(stderr, "ERROR: %s\n", error.string());
591 goto bail;
592 }
593 printf("uses-permission: %s\n", name.string());
594 }
595 }
596 } else if (strcmp("badging", option) == 0) {
Dianne Hackborne289bff2011-06-13 19:33:22 -0700597 Vector<String8> locales;
598 res.getLocales(&locales);
599
600 Vector<ResTable_config> configs;
601 res.getConfigurations(&configs);
602 SortedVector<int> densities;
603 const size_t NC = configs.size();
604 for (size_t i=0; i<NC; i++) {
605 int dens = configs[i].density;
606 if (dens == 0) dens = 160;
607 densities.add(dens);
608 }
609
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800610 size_t len;
611 ResXMLTree::event_code_t code;
612 int depth = 0;
613 String8 error;
614 bool withinActivity = false;
615 bool isMainActivity = false;
616 bool isLauncherActivity = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700617 bool isSearchable = false;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700618 bool withinApplication = false;
619 bool withinReceiver = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700620 bool withinService = false;
621 bool withinIntentFilter = false;
622 bool hasMainActivity = false;
623 bool hasOtherActivities = false;
624 bool hasOtherReceivers = false;
625 bool hasOtherServices = false;
626 bool hasWallpaperService = false;
627 bool hasImeService = false;
628 bool hasWidgetReceivers = false;
629 bool hasIntentFilter = false;
630 bool actMainActivity = false;
631 bool actWidgetReceivers = false;
632 bool actImeService = false;
633 bool actWallpaperService = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700634
Kenny Root063a44e2011-12-08 08:46:03 -0800635 // These two implement the implicit permissions that are granted
636 // to pre-1.6 applications.
637 bool hasWriteExternalStoragePermission = false;
638 bool hasReadPhoneStatePermission = false;
639
Dianne Hackborn79245122012-03-12 10:51:26 -0700640 // If an app requests write storage, they will also get read storage.
641 bool hasReadExternalStoragePermission = false;
642
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -0700643 // Implement transition to read and write call log.
644 bool hasReadContactsPermission = false;
645 bool hasWriteContactsPermission = false;
646 bool hasReadCallLogPermission = false;
647 bool hasWriteCallLogPermission = false;
648
Dan Morrill89d97c12010-05-03 16:13:14 -0700649 // This next group of variables is used to implement a group of
650 // backward-compatibility heuristics necessitated by the addition of
651 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
652 // heuristic is "if an app requests a permission but doesn't explicitly
653 // request the corresponding <uses-feature>, presume it's there anyway".
654 bool specCameraFeature = false; // camera-related
655 bool specCameraAutofocusFeature = false;
656 bool reqCameraAutofocusFeature = false;
657 bool reqCameraFlashFeature = false;
Dianne Hackborne5276a72009-08-27 16:28:44 -0700658 bool hasCameraPermission = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700659 bool specLocationFeature = false; // location-related
660 bool specNetworkLocFeature = false;
661 bool reqNetworkLocFeature = false;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800662 bool specGpsFeature = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700663 bool reqGpsFeature = false;
664 bool hasMockLocPermission = false;
665 bool hasCoarseLocPermission = false;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800666 bool hasGpsPermission = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700667 bool hasGeneralLocPermission = false;
668 bool specBluetoothFeature = false; // Bluetooth API-related
669 bool hasBluetoothPermission = false;
670 bool specMicrophoneFeature = false; // microphone-related
671 bool hasRecordAudioPermission = false;
672 bool specWiFiFeature = false;
673 bool hasWiFiPermission = false;
674 bool specTelephonyFeature = false; // telephony-related
675 bool reqTelephonySubFeature = false;
676 bool hasTelephonyPermission = false;
677 bool specTouchscreenFeature = false; // touchscreen-related
678 bool specMultitouchFeature = false;
679 bool reqDistinctMultitouchFeature = false;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700680 bool specScreenPortraitFeature = false;
681 bool specScreenLandscapeFeature = false;
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -0700682 bool reqScreenPortraitFeature = false;
683 bool reqScreenLandscapeFeature = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700684 // 2.2 also added some other features that apps can request, but that
685 // have no corresponding permission, so we cannot implement any
686 // back-compatibility heuristic for them. The below are thus unnecessary
687 // (but are retained here for documentary purposes.)
688 //bool specCompassFeature = false;
689 //bool specAccelerometerFeature = false;
690 //bool specProximityFeature = false;
691 //bool specAmbientLightFeature = false;
692 //bool specLiveWallpaperFeature = false;
693
Dianne Hackborn723738c2009-06-25 19:48:04 -0700694 int targetSdk = 0;
695 int smallScreen = 1;
696 int normalScreen = 1;
697 int largeScreen = 1;
Dianne Hackbornf43489d2010-08-20 12:44:33 -0700698 int xlargeScreen = 1;
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700699 int anyDensity = 1;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700700 int requiresSmallestWidthDp = 0;
701 int compatibleWidthLimitDp = 0;
702 int largestWidthLimitDp = 0;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700703 String8 pkg;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800704 String8 activityName;
705 String8 activityLabel;
706 String8 activityIcon;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700707 String8 receiverName;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700708 String8 serviceName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800709 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
710 if (code == ResXMLTree::END_TAG) {
711 depth--;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700712 if (depth < 2) {
713 withinApplication = false;
714 } else if (depth < 3) {
715 if (withinActivity && isMainActivity && isLauncherActivity) {
716 const char *aName = getComponentName(pkg, activityName);
Dianne Hackborne289bff2011-06-13 19:33:22 -0700717 printf("launchable-activity:");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700718 if (aName != NULL) {
Dianne Hackborne289bff2011-06-13 19:33:22 -0700719 printf(" name='%s' ", aName);
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700720 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700721 printf(" label='%s' icon='%s'\n",
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700722 activityLabel.string(),
723 activityIcon.string());
724 }
725 if (!hasIntentFilter) {
726 hasOtherActivities |= withinActivity;
727 hasOtherReceivers |= withinReceiver;
728 hasOtherServices |= withinService;
729 }
730 withinActivity = false;
731 withinService = false;
732 withinReceiver = false;
733 hasIntentFilter = false;
734 isMainActivity = isLauncherActivity = false;
735 } else if (depth < 4) {
736 if (withinIntentFilter) {
737 if (withinActivity) {
738 hasMainActivity |= actMainActivity;
739 hasOtherActivities |= !actMainActivity;
740 } else if (withinReceiver) {
741 hasWidgetReceivers |= actWidgetReceivers;
742 hasOtherReceivers |= !actWidgetReceivers;
743 } else if (withinService) {
744 hasImeService |= actImeService;
745 hasWallpaperService |= actWallpaperService;
746 hasOtherServices |= (!actImeService && !actWallpaperService);
747 }
748 }
749 withinIntentFilter = false;
750 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800751 continue;
752 }
753 if (code != ResXMLTree::START_TAG) {
754 continue;
755 }
756 depth++;
757 String8 tag(tree.getElementName(&len));
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700758 //printf("Depth %d, %s\n", depth, tag.string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800759 if (depth == 1) {
760 if (tag != "manifest") {
761 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
762 goto bail;
763 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700764 pkg = getAttribute(tree, NULL, "package", NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800765 printf("package: name='%s' ", pkg.string());
766 int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
767 if (error != "") {
768 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
769 goto bail;
770 }
771 if (versionCode > 0) {
772 printf("versionCode='%d' ", versionCode);
773 } else {
774 printf("versionCode='' ");
775 }
Dianne Hackborncf244ad2010-03-09 15:00:30 -0800776 String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800777 if (error != "") {
778 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
779 goto bail;
780 }
781 printf("versionName='%s'\n", versionName.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700782 } else if (depth == 2) {
783 withinApplication = false;
784 if (tag == "application") {
785 withinApplication = true;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700786
787 String8 label;
788 const size_t NL = locales.size();
789 for (size_t i=0; i<NL; i++) {
790 const char* localeStr = locales[i].string();
791 assets.setLocale(localeStr != NULL ? localeStr : "");
792 String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
793 if (llabel != "") {
794 if (localeStr == NULL || strlen(localeStr) == 0) {
795 label = llabel;
796 printf("application-label:'%s'\n", llabel.string());
797 } else {
798 if (label == "") {
799 label = llabel;
800 }
801 printf("application-label-%s:'%s'\n", localeStr,
802 llabel.string());
803 }
804 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700805 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700806
807 ResTable_config tmpConfig = config;
808 const size_t ND = densities.size();
809 for (size_t i=0; i<ND; i++) {
810 tmpConfig.density = densities[i];
811 assets.setConfiguration(tmpConfig);
812 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
813 if (icon != "") {
814 printf("application-icon-%d:'%s'\n", densities[i], icon.string());
815 }
816 }
817 assets.setConfiguration(config);
818
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700819 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
820 if (error != "") {
821 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
822 goto bail;
823 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700824 int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700825 if (error != "") {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700826 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700827 goto bail;
828 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700829 printf("application: label='%s' ", label.string());
830 printf("icon='%s'\n", icon.string());
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700831 if (testOnly != 0) {
832 printf("testOnly='%d'\n", testOnly);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700833 }
Dan Morrillb6ec11e2012-04-03 12:44:40 -0700834
835 int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0);
836 if (error != "") {
837 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string());
838 goto bail;
839 }
840 if (debuggable != 0) {
841 printf("application-debuggable\n");
842 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700843 } else if (tag == "uses-sdk") {
844 int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
845 if (error != "") {
846 error = "";
847 String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
848 if (error != "") {
849 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
850 error.string());
851 goto bail;
852 }
Dianne Hackborn723738c2009-06-25 19:48:04 -0700853 if (name == "Donut") targetSdk = 4;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700854 printf("sdkVersion:'%s'\n", name.string());
855 } else if (code != -1) {
Dianne Hackborn723738c2009-06-25 19:48:04 -0700856 targetSdk = code;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700857 printf("sdkVersion:'%d'\n", code);
858 }
Suchi Amalapurapu75c49842009-08-14 15:13:09 -0700859 code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
860 if (code != -1) {
861 printf("maxSdkVersion:'%d'\n", code);
862 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700863 code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
864 if (error != "") {
865 error = "";
866 String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
867 if (error != "") {
868 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
869 error.string());
870 goto bail;
871 }
Dianne Hackborn723738c2009-06-25 19:48:04 -0700872 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700873 printf("targetSdkVersion:'%s'\n", name.string());
874 } else if (code != -1) {
Dianne Hackborn723738c2009-06-25 19:48:04 -0700875 if (targetSdk < code) {
876 targetSdk = code;
877 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700878 printf("targetSdkVersion:'%d'\n", code);
879 }
880 } else if (tag == "uses-configuration") {
881 int32_t reqTouchScreen = getIntegerAttribute(tree,
882 REQ_TOUCH_SCREEN_ATTR, NULL, 0);
883 int32_t reqKeyboardType = getIntegerAttribute(tree,
884 REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
885 int32_t reqHardKeyboard = getIntegerAttribute(tree,
886 REQ_HARD_KEYBOARD_ATTR, NULL, 0);
887 int32_t reqNavigation = getIntegerAttribute(tree,
888 REQ_NAVIGATION_ATTR, NULL, 0);
889 int32_t reqFiveWayNav = getIntegerAttribute(tree,
890 REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
Dianne Hackborncb2d50d2010-01-06 11:29:54 -0800891 printf("uses-configuration:");
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700892 if (reqTouchScreen != 0) {
893 printf(" reqTouchScreen='%d'", reqTouchScreen);
894 }
895 if (reqKeyboardType != 0) {
896 printf(" reqKeyboardType='%d'", reqKeyboardType);
897 }
898 if (reqHardKeyboard != 0) {
899 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
900 }
901 if (reqNavigation != 0) {
902 printf(" reqNavigation='%d'", reqNavigation);
903 }
904 if (reqFiveWayNav != 0) {
905 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
906 }
907 printf("\n");
Dianne Hackborn723738c2009-06-25 19:48:04 -0700908 } else if (tag == "supports-screens") {
909 smallScreen = getIntegerAttribute(tree,
910 SMALL_SCREEN_ATTR, NULL, 1);
911 normalScreen = getIntegerAttribute(tree,
912 NORMAL_SCREEN_ATTR, NULL, 1);
913 largeScreen = getIntegerAttribute(tree,
914 LARGE_SCREEN_ATTR, NULL, 1);
Dianne Hackbornf43489d2010-08-20 12:44:33 -0700915 xlargeScreen = getIntegerAttribute(tree,
916 XLARGE_SCREEN_ATTR, NULL, 1);
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700917 anyDensity = getIntegerAttribute(tree,
918 ANY_DENSITY_ATTR, NULL, 1);
Dianne Hackborne289bff2011-06-13 19:33:22 -0700919 requiresSmallestWidthDp = getIntegerAttribute(tree,
920 REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
921 compatibleWidthLimitDp = getIntegerAttribute(tree,
922 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
923 largestWidthLimitDp = getIntegerAttribute(tree,
924 LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
Dianne Hackborne5276a72009-08-27 16:28:44 -0700925 } else if (tag == "uses-feature") {
926 String8 name = getAttribute(tree, NAME_ATTR, &error);
Suchi Amalapurapu40b94722009-09-20 13:39:37 -0700927
928 if (name != "" && error == "") {
Dianne Hackborne5276a72009-08-27 16:28:44 -0700929 int req = getIntegerAttribute(tree,
930 REQUIRED_ATTR, NULL, 1);
Dan Morrill89d97c12010-05-03 16:13:14 -0700931
Dianne Hackborne5276a72009-08-27 16:28:44 -0700932 if (name == "android.hardware.camera") {
933 specCameraFeature = true;
Dan Morrill89d97c12010-05-03 16:13:14 -0700934 } else if (name == "android.hardware.camera.autofocus") {
935 // these have no corresponding permission to check for,
936 // but should imply the foundational camera permission
937 reqCameraAutofocusFeature = reqCameraAutofocusFeature || req;
938 specCameraAutofocusFeature = true;
939 } else if (req && (name == "android.hardware.camera.flash")) {
940 // these have no corresponding permission to check for,
941 // but should imply the foundational camera permission
942 reqCameraFlashFeature = true;
943 } else if (name == "android.hardware.location") {
944 specLocationFeature = true;
945 } else if (name == "android.hardware.location.network") {
946 specNetworkLocFeature = true;
947 reqNetworkLocFeature = reqNetworkLocFeature || req;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800948 } else if (name == "android.hardware.location.gps") {
949 specGpsFeature = true;
Dan Morrill89d97c12010-05-03 16:13:14 -0700950 reqGpsFeature = reqGpsFeature || req;
951 } else if (name == "android.hardware.bluetooth") {
952 specBluetoothFeature = true;
953 } else if (name == "android.hardware.touchscreen") {
954 specTouchscreenFeature = true;
955 } else if (name == "android.hardware.touchscreen.multitouch") {
956 specMultitouchFeature = true;
957 } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
958 reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req;
959 } else if (name == "android.hardware.microphone") {
960 specMicrophoneFeature = true;
961 } else if (name == "android.hardware.wifi") {
962 specWiFiFeature = true;
963 } else if (name == "android.hardware.telephony") {
964 specTelephonyFeature = true;
965 } else if (req && (name == "android.hardware.telephony.gsm" ||
966 name == "android.hardware.telephony.cdma")) {
967 // these have no corresponding permission to check for,
968 // but should imply the foundational telephony permission
969 reqTelephonySubFeature = true;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700970 } else if (name == "android.hardware.screen.portrait") {
971 specScreenPortraitFeature = true;
972 } else if (name == "android.hardware.screen.landscape") {
973 specScreenLandscapeFeature = true;
Dianne Hackborne5276a72009-08-27 16:28:44 -0700974 }
975 printf("uses-feature%s:'%s'\n",
976 req ? "" : "-not-required", name.string());
977 } else {
978 int vers = getIntegerAttribute(tree,
979 GL_ES_VERSION_ATTR, &error);
980 if (error == "") {
981 printf("uses-gl-es:'0x%x'\n", vers);
982 }
983 }
984 } else if (tag == "uses-permission") {
985 String8 name = getAttribute(tree, NAME_ATTR, &error);
Suchi Amalapurapu40b94722009-09-20 13:39:37 -0700986 if (name != "" && error == "") {
Dianne Hackborne5276a72009-08-27 16:28:44 -0700987 if (name == "android.permission.CAMERA") {
988 hasCameraPermission = true;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800989 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
990 hasGpsPermission = true;
Dan Morrill89d97c12010-05-03 16:13:14 -0700991 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
992 hasMockLocPermission = true;
993 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
994 hasCoarseLocPermission = true;
995 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
996 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
997 hasGeneralLocPermission = true;
998 } else if (name == "android.permission.BLUETOOTH" ||
999 name == "android.permission.BLUETOOTH_ADMIN") {
1000 hasBluetoothPermission = true;
1001 } else if (name == "android.permission.RECORD_AUDIO") {
1002 hasRecordAudioPermission = true;
1003 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1004 name == "android.permission.CHANGE_WIFI_STATE" ||
1005 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
1006 hasWiFiPermission = true;
1007 } else if (name == "android.permission.CALL_PHONE" ||
1008 name == "android.permission.CALL_PRIVILEGED" ||
1009 name == "android.permission.MODIFY_PHONE_STATE" ||
1010 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1011 name == "android.permission.READ_SMS" ||
1012 name == "android.permission.RECEIVE_SMS" ||
1013 name == "android.permission.RECEIVE_MMS" ||
1014 name == "android.permission.RECEIVE_WAP_PUSH" ||
1015 name == "android.permission.SEND_SMS" ||
1016 name == "android.permission.WRITE_APN_SETTINGS" ||
1017 name == "android.permission.WRITE_SMS") {
1018 hasTelephonyPermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001019 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1020 hasWriteExternalStoragePermission = true;
Dianne Hackborn79245122012-03-12 10:51:26 -07001021 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1022 hasReadExternalStoragePermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001023 } else if (name == "android.permission.READ_PHONE_STATE") {
1024 hasReadPhoneStatePermission = true;
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001025 } else if (name == "android.permission.READ_CONTACTS") {
1026 hasReadContactsPermission = true;
1027 } else if (name == "android.permission.WRITE_CONTACTS") {
1028 hasWriteContactsPermission = true;
1029 } else if (name == "android.permission.READ_CALL_LOG") {
1030 hasReadCallLogPermission = true;
1031 } else if (name == "android.permission.WRITE_CALL_LOG") {
1032 hasWriteCallLogPermission = true;
Dianne Hackborne5276a72009-08-27 16:28:44 -07001033 }
1034 printf("uses-permission:'%s'\n", name.string());
1035 } else {
1036 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1037 error.string());
1038 goto bail;
1039 }
Dianne Hackborn43b68032010-09-02 17:14:41 -07001040 } else if (tag == "uses-package") {
1041 String8 name = getAttribute(tree, NAME_ATTR, &error);
1042 if (name != "" && error == "") {
1043 printf("uses-package:'%s'\n", name.string());
1044 } else {
1045 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1046 error.string());
1047 goto bail;
1048 }
Jeff Hamiltone2c17f92010-02-12 13:45:16 -06001049 } else if (tag == "original-package") {
1050 String8 name = getAttribute(tree, NAME_ATTR, &error);
1051 if (name != "" && error == "") {
1052 printf("original-package:'%s'\n", name.string());
1053 } else {
1054 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1055 error.string());
1056 goto bail;
1057 }
Dan Morrill096b67f2010-12-13 16:25:54 -08001058 } else if (tag == "supports-gl-texture") {
Dan Morrill6f51fc12010-10-13 14:33:43 -07001059 String8 name = getAttribute(tree, NAME_ATTR, &error);
1060 if (name != "" && error == "") {
Dan Morrill096b67f2010-12-13 16:25:54 -08001061 printf("supports-gl-texture:'%s'\n", name.string());
Dan Morrill6f51fc12010-10-13 14:33:43 -07001062 } else {
1063 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1064 error.string());
1065 goto bail;
1066 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001067 } else if (tag == "compatible-screens") {
1068 printCompatibleScreens(tree);
1069 depth--;
Kenny Root56088a552011-09-29 13:49:45 -07001070 } else if (tag == "package-verifier") {
1071 String8 name = getAttribute(tree, NAME_ATTR, &error);
1072 if (name != "" && error == "") {
1073 String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
1074 if (publicKey != "" && error == "") {
1075 printf("package-verifier: name='%s' publicKey='%s'\n",
1076 name.string(), publicKey.string());
1077 }
1078 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001079 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001080 } else if (depth == 3 && withinApplication) {
1081 withinActivity = false;
1082 withinReceiver = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001083 withinService = false;
1084 hasIntentFilter = false;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001085 if(tag == "activity") {
1086 withinActivity = true;
1087 activityName = getAttribute(tree, NAME_ATTR, &error);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001088 if (error != "") {
1089 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1090 goto bail;
1091 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001092
1093 activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001094 if (error != "") {
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001095 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001096 goto bail;
1097 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001098
1099 activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1100 if (error != "") {
1101 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
1102 goto bail;
1103 }
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001104
1105 int32_t orien = getResolvedIntegerAttribute(&res, tree,
1106 SCREEN_ORIENTATION_ATTR, &error);
1107 if (error == "") {
1108 if (orien == 0 || orien == 6 || orien == 8) {
1109 // Requests landscape, sensorLandscape, or reverseLandscape.
1110 reqScreenLandscapeFeature = true;
1111 } else if (orien == 1 || orien == 7 || orien == 9) {
1112 // Requests portrait, sensorPortrait, or reversePortrait.
1113 reqScreenPortraitFeature = true;
1114 }
1115 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001116 } else if (tag == "uses-library") {
1117 String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1118 if (error != "") {
1119 fprintf(stderr, "ERROR getting 'android:name' attribute for uses-library: %s\n", error.string());
1120 goto bail;
1121 }
Dianne Hackborn49237342009-08-27 20:08:01 -07001122 int req = getIntegerAttribute(tree,
1123 REQUIRED_ATTR, NULL, 1);
1124 printf("uses-library%s:'%s'\n",
1125 req ? "" : "-not-required", libraryName.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001126 } else if (tag == "receiver") {
1127 withinReceiver = true;
1128 receiverName = getAttribute(tree, NAME_ATTR, &error);
1129
1130 if (error != "") {
1131 fprintf(stderr, "ERROR getting 'android:name' attribute for receiver: %s\n", error.string());
1132 goto bail;
1133 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001134 } else if (tag == "service") {
1135 withinService = true;
1136 serviceName = getAttribute(tree, NAME_ATTR, &error);
1137
1138 if (error != "") {
1139 fprintf(stderr, "ERROR getting 'android:name' attribute for service: %s\n", error.string());
1140 goto bail;
1141 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001142 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001143 } else if ((depth == 4) && (tag == "intent-filter")) {
1144 hasIntentFilter = true;
1145 withinIntentFilter = true;
1146 actMainActivity = actWidgetReceivers = actImeService = actWallpaperService = false;
1147 } else if ((depth == 5) && withinIntentFilter){
1148 String8 action;
1149 if (tag == "action") {
1150 action = getAttribute(tree, NAME_ATTR, &error);
1151 if (error != "") {
1152 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1153 goto bail;
1154 }
1155 if (withinActivity) {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001156 if (action == "android.intent.action.MAIN") {
1157 isMainActivity = true;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001158 actMainActivity = true;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001159 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001160 } else if (withinReceiver) {
1161 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1162 actWidgetReceivers = true;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001163 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001164 } else if (withinService) {
1165 if (action == "android.view.InputMethod") {
1166 actImeService = true;
1167 } else if (action == "android.service.wallpaper.WallpaperService") {
1168 actWallpaperService = true;
1169 }
1170 }
1171 if (action == "android.intent.action.SEARCH") {
1172 isSearchable = true;
1173 }
1174 }
1175
1176 if (tag == "category") {
1177 String8 category = getAttribute(tree, NAME_ATTR, &error);
1178 if (error != "") {
1179 fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string());
1180 goto bail;
1181 }
1182 if (withinActivity) {
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001183 if (category == "android.intent.category.LAUNCHER") {
1184 isLauncherActivity = true;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001185 }
1186 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001187 }
1188 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001189 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001190
Kenny Root063a44e2011-12-08 08:46:03 -08001191 // Pre-1.6 implicitly granted permission compatibility logic
1192 if (targetSdk < 4) {
1193 if (!hasWriteExternalStoragePermission) {
1194 printf("uses-permission:'android.permission.WRITE_EXTERNAL_STORAGE'\n");
Dianne Hackborn79245122012-03-12 10:51:26 -07001195 hasWriteExternalStoragePermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001196 }
1197 if (!hasReadPhoneStatePermission) {
1198 printf("uses-permission:'android.permission.READ_PHONE_STATE'\n");
1199 }
1200 }
1201
Dianne Hackborn79245122012-03-12 10:51:26 -07001202 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1203 // force them to always take READ_EXTERNAL_STORAGE as well.
1204 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
1205 printf("uses-permission:'android.permission.READ_EXTERNAL_STORAGE'\n");
1206 }
1207
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001208 // Pre-JellyBean call log permission compatibility.
1209 if (targetSdk < 16) {
1210 if (!hasReadCallLogPermission && hasReadContactsPermission) {
1211 printf("uses-permission:'android.permission.READ_CALL_LOG'\n");
1212 }
1213 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
1214 printf("uses-permission:'android.permission.WRITE_CALL_LOG'\n");
1215 }
1216 }
1217
Dan Morrill89d97c12010-05-03 16:13:14 -07001218 /* The following blocks handle printing "inferred" uses-features, based
1219 * on whether related features or permissions are used by the app.
1220 * Note that the various spec*Feature variables denote whether the
1221 * relevant tag was *present* in the AndroidManfest, not that it was
1222 * present and set to true.
1223 */
1224 // Camera-related back-compatibility logic
1225 if (!specCameraFeature) {
1226 if (reqCameraFlashFeature || reqCameraAutofocusFeature) {
1227 // if app requested a sub-feature (autofocus or flash) and didn't
1228 // request the base camera feature, we infer that it meant to
1229 printf("uses-feature:'android.hardware.camera'\n");
1230 } else if (hasCameraPermission) {
1231 // if app wants to use camera but didn't request the feature, we infer
1232 // that it meant to, and further that it wants autofocus
1233 // (which was the 1.0 - 1.5 behavior)
1234 printf("uses-feature:'android.hardware.camera'\n");
1235 if (!specCameraAutofocusFeature) {
1236 printf("uses-feature:'android.hardware.camera.autofocus'\n");
1237 }
1238 }
Dianne Hackborne5276a72009-08-27 16:28:44 -07001239 }
Doug Zongkerdbe7a682009-10-09 11:24:51 -07001240
Dan Morrill89d97c12010-05-03 16:13:14 -07001241 // Location-related back-compatibility logic
1242 if (!specLocationFeature &&
1243 (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission ||
1244 hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) {
1245 // if app either takes a location-related permission or requests one of the
1246 // sub-features, we infer that it also meant to request the base location feature
1247 printf("uses-feature:'android.hardware.location'\n");
1248 }
Dianne Hackbornef05e072010-03-01 17:43:39 -08001249 if (!specGpsFeature && hasGpsPermission) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001250 // if app takes GPS (FINE location) perm but does not request the GPS
1251 // feature, we infer that it meant to
Dianne Hackbornef05e072010-03-01 17:43:39 -08001252 printf("uses-feature:'android.hardware.location.gps'\n");
1253 }
Dan Morrill89d97c12010-05-03 16:13:14 -07001254 if (!specNetworkLocFeature && hasCoarseLocPermission) {
1255 // if app takes Network location (COARSE location) perm but does not request the
1256 // network location feature, we infer that it meant to
1257 printf("uses-feature:'android.hardware.location.network'\n");
1258 }
1259
1260 // Bluetooth-related compatibility logic
Dan Morrill6b22d812010-06-15 21:41:42 -07001261 if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001262 // if app takes a Bluetooth permission but does not request the Bluetooth
1263 // feature, we infer that it meant to
1264 printf("uses-feature:'android.hardware.bluetooth'\n");
1265 }
1266
1267 // Microphone-related compatibility logic
1268 if (!specMicrophoneFeature && hasRecordAudioPermission) {
1269 // if app takes the record-audio permission but does not request the microphone
1270 // feature, we infer that it meant to
1271 printf("uses-feature:'android.hardware.microphone'\n");
1272 }
1273
1274 // WiFi-related compatibility logic
1275 if (!specWiFiFeature && hasWiFiPermission) {
1276 // if app takes one of the WiFi permissions but does not request the WiFi
1277 // feature, we infer that it meant to
1278 printf("uses-feature:'android.hardware.wifi'\n");
1279 }
1280
1281 // Telephony-related compatibility logic
1282 if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) {
1283 // if app takes one of the telephony permissions or requests a sub-feature but
1284 // does not request the base telephony feature, we infer that it meant to
1285 printf("uses-feature:'android.hardware.telephony'\n");
1286 }
1287
1288 // Touchscreen-related back-compatibility logic
1289 if (!specTouchscreenFeature) { // not a typo!
1290 // all apps are presumed to require a touchscreen, unless they explicitly say
1291 // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
1292 // Note that specTouchscreenFeature is true if the tag is present, regardless
1293 // of whether its value is true or false, so this is safe
1294 printf("uses-feature:'android.hardware.touchscreen'\n");
1295 }
1296 if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
1297 // if app takes one of the telephony permissions or requests a sub-feature but
1298 // does not request the base telephony feature, we infer that it meant to
1299 printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
1300 }
Dianne Hackbornef05e072010-03-01 17:43:39 -08001301
Dianne Hackborne289bff2011-06-13 19:33:22 -07001302 // Landscape/portrait-related compatibility logic
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001303 if (!specScreenLandscapeFeature && !specScreenPortraitFeature) {
1304 // If the app has specified any activities in its manifest
1305 // that request a specific orientation, then assume that
1306 // orientation is required.
1307 if (reqScreenLandscapeFeature) {
1308 printf("uses-feature:'android.hardware.screen.landscape'\n");
1309 }
1310 if (reqScreenPortraitFeature) {
1311 printf("uses-feature:'android.hardware.screen.portrait'\n");
1312 }
Dianne Hackborne289bff2011-06-13 19:33:22 -07001313 }
1314
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001315 if (hasMainActivity) {
1316 printf("main\n");
1317 }
1318 if (hasWidgetReceivers) {
1319 printf("app-widget\n");
1320 }
1321 if (hasImeService) {
1322 printf("ime\n");
1323 }
1324 if (hasWallpaperService) {
1325 printf("wallpaper\n");
1326 }
1327 if (hasOtherActivities) {
1328 printf("other-activities\n");
1329 }
1330 if (isSearchable) {
1331 printf("search\n");
1332 }
1333 if (hasOtherReceivers) {
1334 printf("other-receivers\n");
1335 }
1336 if (hasOtherServices) {
1337 printf("other-services\n");
1338 }
1339
Dianne Hackborne289bff2011-06-13 19:33:22 -07001340 // For modern apps, if screen size buckets haven't been specified
1341 // but the new width ranges have, then infer the buckets from them.
1342 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1343 && requiresSmallestWidthDp > 0) {
1344 int compatWidth = compatibleWidthLimitDp;
1345 if (compatWidth <= 0) compatWidth = requiresSmallestWidthDp;
1346 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1347 smallScreen = -1;
1348 } else {
1349 smallScreen = 0;
1350 }
1351 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1352 normalScreen = -1;
1353 } else {
1354 normalScreen = 0;
1355 }
1356 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1357 largeScreen = -1;
1358 } else {
1359 largeScreen = 0;
1360 }
1361 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1362 xlargeScreen = -1;
1363 } else {
1364 xlargeScreen = 0;
1365 }
1366 }
1367
Dianne Hackborn723738c2009-06-25 19:48:04 -07001368 // Determine default values for any unspecified screen sizes,
1369 // based on the target SDK of the package. As of 4 (donut)
1370 // the screen size support was introduced, so all default to
1371 // enabled.
1372 if (smallScreen > 0) {
1373 smallScreen = targetSdk >= 4 ? -1 : 0;
1374 }
1375 if (normalScreen > 0) {
1376 normalScreen = -1;
1377 }
1378 if (largeScreen > 0) {
1379 largeScreen = targetSdk >= 4 ? -1 : 0;
1380 }
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001381 if (xlargeScreen > 0) {
Scott Maind58fb972010-11-04 18:32:00 -07001382 // Introduced in Gingerbread.
1383 xlargeScreen = targetSdk >= 9 ? -1 : 0;
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001384 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001385 if (anyDensity > 0) {
Dianne Hackborne289bff2011-06-13 19:33:22 -07001386 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1387 || compatibleWidthLimitDp > 0) ? -1 : 0;
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001388 }
Dianne Hackborn723738c2009-06-25 19:48:04 -07001389 printf("supports-screens:");
1390 if (smallScreen != 0) printf(" 'small'");
1391 if (normalScreen != 0) printf(" 'normal'");
1392 if (largeScreen != 0) printf(" 'large'");
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001393 if (xlargeScreen != 0) printf(" 'xlarge'");
Dianne Hackborn723738c2009-06-25 19:48:04 -07001394 printf("\n");
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001395 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
Dianne Hackborne289bff2011-06-13 19:33:22 -07001396 if (requiresSmallestWidthDp > 0) {
1397 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1398 }
1399 if (compatibleWidthLimitDp > 0) {
1400 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1401 }
1402 if (largestWidthLimitDp > 0) {
1403 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1404 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001405
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001406 printf("locales:");
Dianne Hackborne17086b2009-06-19 15:13:28 -07001407 const size_t NL = locales.size();
1408 for (size_t i=0; i<NL; i++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001409 const char* localeStr = locales[i].string();
1410 if (localeStr == NULL || strlen(localeStr) == 0) {
1411 localeStr = "--_--";
1412 }
1413 printf(" '%s'", localeStr);
1414 }
1415 printf("\n");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001416
Dianne Hackborne17086b2009-06-19 15:13:28 -07001417 printf("densities:");
1418 const size_t ND = densities.size();
1419 for (size_t i=0; i<ND; i++) {
1420 printf(" '%d'", densities[i]);
1421 }
1422 printf("\n");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001423
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001424 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1425 if (dir != NULL) {
1426 if (dir->getFileCount() > 0) {
1427 printf("native-code:");
1428 for (size_t i=0; i<dir->getFileCount(); i++) {
1429 printf(" '%s'", dir->getFileName(i).string());
1430 }
1431 printf("\n");
1432 }
1433 delete dir;
1434 }
Dan Morrille74763e2012-01-06 10:47:10 -08001435 } else if (strcmp("badger", option) == 0) {
Dianne Hackborn6c997a92012-01-31 11:27:43 -08001436 printf("%s", CONSOLE_DATA);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001437 } else if (strcmp("configurations", option) == 0) {
1438 Vector<ResTable_config> configs;
1439 res.getConfigurations(&configs);
1440 const size_t N = configs.size();
1441 for (size_t i=0; i<N; i++) {
1442 printf("%s\n", configs[i].toString().string());
1443 }
1444 } else {
1445 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
1446 goto bail;
1447 }
1448 }
1449
1450 result = NO_ERROR;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001451
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001452bail:
1453 if (asset) {
1454 delete asset;
1455 }
1456 return (result != NO_ERROR);
1457}
1458
1459
1460/*
1461 * Handle the "add" command, which wants to add files to a new or
1462 * pre-existing archive.
1463 */
1464int doAdd(Bundle* bundle)
1465{
1466 ZipFile* zip = NULL;
1467 status_t result = UNKNOWN_ERROR;
1468 const char* zipFileName;
1469
1470 if (bundle->getUpdate()) {
1471 /* avoid confusion */
1472 fprintf(stderr, "ERROR: can't use '-u' with add\n");
1473 goto bail;
1474 }
1475
1476 if (bundle->getFileSpecCount() < 1) {
1477 fprintf(stderr, "ERROR: must specify zip file name\n");
1478 goto bail;
1479 }
1480 zipFileName = bundle->getFileSpecEntry(0);
1481
1482 if (bundle->getFileSpecCount() < 2) {
1483 fprintf(stderr, "NOTE: nothing to do\n");
1484 goto bail;
1485 }
1486
1487 zip = openReadWrite(zipFileName, true);
1488 if (zip == NULL) {
1489 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
1490 goto bail;
1491 }
1492
1493 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1494 const char* fileName = bundle->getFileSpecEntry(i);
1495
1496 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
1497 printf(" '%s'... (from gzip)\n", fileName);
1498 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
1499 } else {
Doug Zongkerdbe7a682009-10-09 11:24:51 -07001500 if (bundle->getJunkPath()) {
1501 String8 storageName = String8(fileName).getPathLeaf();
1502 printf(" '%s' as '%s'...\n", fileName, storageName.string());
1503 result = zip->add(fileName, storageName.string(),
1504 bundle->getCompressionMethod(), NULL);
1505 } else {
1506 printf(" '%s'...\n", fileName);
1507 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
1508 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001509 }
1510 if (result != NO_ERROR) {
1511 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
1512 if (result == NAME_NOT_FOUND)
1513 fprintf(stderr, ": file not found\n");
1514 else if (result == ALREADY_EXISTS)
1515 fprintf(stderr, ": already exists in archive\n");
1516 else
1517 fprintf(stderr, "\n");
1518 goto bail;
1519 }
1520 }
1521
1522 result = NO_ERROR;
1523
1524bail:
1525 delete zip;
1526 return (result != NO_ERROR);
1527}
1528
1529
1530/*
1531 * Delete files from an existing archive.
1532 */
1533int doRemove(Bundle* bundle)
1534{
1535 ZipFile* zip = NULL;
1536 status_t result = UNKNOWN_ERROR;
1537 const char* zipFileName;
1538
1539 if (bundle->getFileSpecCount() < 1) {
1540 fprintf(stderr, "ERROR: must specify zip file name\n");
1541 goto bail;
1542 }
1543 zipFileName = bundle->getFileSpecEntry(0);
1544
1545 if (bundle->getFileSpecCount() < 2) {
1546 fprintf(stderr, "NOTE: nothing to do\n");
1547 goto bail;
1548 }
1549
1550 zip = openReadWrite(zipFileName, false);
1551 if (zip == NULL) {
1552 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
1553 zipFileName);
1554 goto bail;
1555 }
1556
1557 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1558 const char* fileName = bundle->getFileSpecEntry(i);
1559 ZipEntry* entry;
1560
1561 entry = zip->getEntryByName(fileName);
1562 if (entry == NULL) {
1563 printf(" '%s' NOT FOUND\n", fileName);
1564 continue;
1565 }
1566
1567 result = zip->remove(entry);
1568
1569 if (result != NO_ERROR) {
1570 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
1571 bundle->getFileSpecEntry(i), zipFileName);
1572 goto bail;
1573 }
1574 }
1575
1576 /* update the archive */
1577 zip->flush();
1578
1579bail:
1580 delete zip;
1581 return (result != NO_ERROR);
1582}
1583
1584
1585/*
1586 * Package up an asset directory and associated application files.
1587 */
1588int doPackage(Bundle* bundle)
1589{
1590 const char* outputAPKFile;
1591 int retVal = 1;
1592 status_t err;
1593 sp<AaptAssets> assets;
1594 int N;
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001595 FILE* fp;
1596 String8 dependencyFile;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001597
1598 // -c zz_ZZ means do pseudolocalization
1599 ResourceFilter filter;
1600 err = filter.parse(bundle->getConfigurations());
1601 if (err != NO_ERROR) {
1602 goto bail;
1603 }
1604 if (filter.containsPseudo()) {
1605 bundle->setPseudolocalize(true);
1606 }
1607
1608 N = bundle->getFileSpecCount();
1609 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
1610 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDir() == NULL) {
1611 fprintf(stderr, "ERROR: no input files\n");
1612 goto bail;
1613 }
1614
1615 outputAPKFile = bundle->getOutputAPKFile();
1616
1617 // Make sure the filenames provided exist and are of the appropriate type.
1618 if (outputAPKFile) {
1619 FileType type;
1620 type = getFileType(outputAPKFile);
1621 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
1622 fprintf(stderr,
1623 "ERROR: output file '%s' exists but is not regular file\n",
1624 outputAPKFile);
1625 goto bail;
1626 }
1627 }
1628
1629 // Load the assets.
1630 assets = new AaptAssets();
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001631
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001632 // Set up the resource gathering in assets if we're going to generate
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001633 // dependency files. Every time we encounter a resource while slurping
1634 // the tree, we'll add it to these stores so we have full resource paths
1635 // to write to a dependency file.
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001636 if (bundle->getGenDependencies()) {
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001637 sp<FilePathStore> resPathStore = new FilePathStore;
1638 assets->setFullResPaths(resPathStore);
1639 sp<FilePathStore> assetPathStore = new FilePathStore;
1640 assets->setFullAssetPaths(assetPathStore);
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001641 }
1642
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001643 err = assets->slurpFromArgs(bundle);
1644 if (err < 0) {
1645 goto bail;
1646 }
1647
1648 if (bundle->getVerbose()) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001649 assets->print(String8());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001650 }
1651
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001652 // If they asked for any fileAs that need to be compiled, do so.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001653 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
1654 err = buildResources(bundle, assets);
1655 if (err != 0) {
1656 goto bail;
1657 }
1658 }
1659
1660 // At this point we've read everything and processed everything. From here
1661 // on out it's just writing output files.
1662 if (SourcePos::hasErrors()) {
1663 goto bail;
1664 }
1665
Dianne Hackborn1644c6d72012-02-06 15:33:21 -08001666 // Update symbols with information about which ones are needed as Java symbols.
1667 assets->applyJavaSymbols();
1668 if (SourcePos::hasErrors()) {
1669 goto bail;
1670 }
1671
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001672 // If we've been asked to generate a dependency file, do that here
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001673 if (bundle->getGenDependencies()) {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001674 // If this is the packaging step, generate the dependency file next to
1675 // the output apk (e.g. bin/resources.ap_.d)
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001676 if (outputAPKFile) {
1677 dependencyFile = String8(outputAPKFile);
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001678 // Add the .d extension to the dependency file.
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001679 dependencyFile.append(".d");
1680 } else {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001681 // Else if this is the R.java dependency generation step,
1682 // generate the dependency file in the R.java package subdirectory
1683 // e.g. gen/com/foo/app/R.java.d
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001684 dependencyFile = String8(bundle->getRClassDir());
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001685 dependencyFile.appendPath("R.java.d");
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001686 }
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001687 // Make sure we have a clean dependency file to start with
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001688 fp = fopen(dependencyFile, "w");
1689 fclose(fp);
1690 }
1691
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001692 // Write out R.java constants
Dianne Hackborn1644c6d72012-02-06 15:33:21 -08001693 if (!assets->havePrivateSymbols()) {
Xavier Ducrohet63459ad2009-11-30 18:05:10 -08001694 if (bundle->getCustomPackage() == NULL) {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001695 // Write the R.java file into the appropriate class directory
1696 // e.g. gen/com/foo/app/R.java
Xavier Ducrohet63459ad2009-11-30 18:05:10 -08001697 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001698 // If we have library files, we're going to write our R.java file into
1699 // the appropriate class directory for those libraries as well.
1700 // e.g. gen/com/foo/app/lib/R.java
Josiah Gaskince89f152011-06-08 19:31:40 -07001701 if (bundle->getExtraPackages() != NULL) {
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001702 // Split on colon
Josiah Gaskince89f152011-06-08 19:31:40 -07001703 String8 libs(bundle->getExtraPackages());
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001704 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
Josiah Gaskince89f152011-06-08 19:31:40 -07001705 while (packageString != NULL) {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001706 // Write the R.java file out with the correct package name
Josiah Gaskince89f152011-06-08 19:31:40 -07001707 err = writeResourceSymbols(bundle, assets, String8(packageString), true);
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001708 packageString = strtok(NULL, ":");
Josiah Gaskince89f152011-06-08 19:31:40 -07001709 }
1710 libs.unlockBuffer();
1711 }
Xavier Ducrohet63459ad2009-11-30 18:05:10 -08001712 } else {
1713 const String8 customPkg(bundle->getCustomPackage());
1714 err = writeResourceSymbols(bundle, assets, customPkg, true);
1715 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001716 if (err < 0) {
1717 goto bail;
1718 }
1719 } else {
1720 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
1721 if (err < 0) {
1722 goto bail;
1723 }
1724 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
1725 if (err < 0) {
1726 goto bail;
1727 }
1728 }
1729
Joe Onorato1553c822009-08-30 13:36:22 -07001730 // Write out the ProGuard file
1731 err = writeProguardFile(bundle, assets);
1732 if (err < 0) {
1733 goto bail;
1734 }
1735
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001736 // Write the apk
1737 if (outputAPKFile) {
1738 err = writeAPK(bundle, assets, String8(outputAPKFile));
1739 if (err != NO_ERROR) {
1740 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
1741 goto bail;
1742 }
1743 }
1744
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001745 // If we've been asked to generate a dependency file, we need to finish up here.
1746 // the writeResourceSymbols and writeAPK functions have already written the target
1747 // half of the dependency file, now we need to write the prerequisites. (files that
1748 // the R.java file or .ap_ file depend on)
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001749 if (bundle->getGenDependencies()) {
1750 // Now that writeResourceSymbols or writeAPK has taken care of writing
1751 // the targets to our dependency file, we'll write the prereqs
1752 fp = fopen(dependencyFile, "a+");
1753 fprintf(fp, " : ");
1754 bool includeRaw = (outputAPKFile != NULL);
1755 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001756 // Also manually add the AndroidManifeset since it's not under res/ or assets/
1757 // and therefore was not added to our pathstores during slurping
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001758 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
1759 fclose(fp);
1760 }
1761
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001762 retVal = 0;
1763bail:
1764 if (SourcePos::hasErrors()) {
1765 SourcePos::printErrors(stderr);
1766 }
1767 return retVal;
1768}
Josiah Gaskin8a39da82011-06-06 17:00:35 -07001769
1770/*
1771 * Do PNG Crunching
1772 * PRECONDITIONS
1773 * -S flag points to a source directory containing drawable* folders
1774 * -C flag points to destination directory. The folder structure in the
1775 * source directory will be mirrored to the destination (cache) directory
1776 *
1777 * POSTCONDITIONS
1778 * Destination directory will be updated to match the PNG files in
1779 * the source directory.
1780 */
1781int doCrunch(Bundle* bundle)
1782{
1783 fprintf(stdout, "Crunching PNG Files in ");
1784 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
1785 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
1786
1787 updatePreProcessedCache(bundle);
1788
1789 return NO_ERROR;
1790}
Dan Morrille74763e2012-01-06 10:47:10 -08001791
1792char CONSOLE_DATA[2925] = {
1793 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1794 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1795 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1796 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1797 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
1798 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
1799 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1800 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1801 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
1802 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
1803 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1804 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1805 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
1806 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1807 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1808 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
1809 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
1810 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1811 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1812 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
1813 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
1814 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
1815 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
1816 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
1817 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1818 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1819 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
1820 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
1821 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
1822 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1823 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
1824 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1825 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1826 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
1827 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
1828 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1829 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1830 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
1831 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
1832 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
1833 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1834 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
1835 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1836 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1837 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
1838 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
1839 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
1840 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
1841 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
1842 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1843 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
1844 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
1845 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
1846 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1847 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
1848 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
1849 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
1850 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
1851 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
1852 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
1853 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
1854 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
1855 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
1856 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
1857 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
1858 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
1859 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
1860 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
1861 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
1862 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
1863 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
1864 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
1865 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1866 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
1867 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
1868 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
1869 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1870 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
1871 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
1872 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1873 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
1874 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
1875 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
1876 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1877 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
1878 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
1879 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1880 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
1881 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
1882 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1883 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1884 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
1885 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
1886 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1887 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1888 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
1889 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1890 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
1891 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
1892 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
1893 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1894 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1895 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
1896 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
1897 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
1898 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1899 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
1900 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1901 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1902 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
1903 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
1904 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1905 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1906 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
1907 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1908 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
1909 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
1910 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
1911 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1912 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1913 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
1914 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
1915 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
1916 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
1917 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
1918 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
1919 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
1920 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
1921 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
1922 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1923 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1924 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
1925 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
1926 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
1927 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
1928 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
1929 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1930 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1931 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
1932 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
1933 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
1934 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1935 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
1936 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1937 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1938 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
1939 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
1940 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1941 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1942 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
1943 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
1944 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1945 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
1946 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
1947 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1948 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1949 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
1950 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
1951 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1952 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1953 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1954 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1955 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
1956 };