blob: 0238119a457929dd946ba76dec8b546103a59a34 [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 Root56088a52011-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
Dan Morrill89d97c12010-05-03 16:13:14 -0700643 // This next group of variables is used to implement a group of
644 // backward-compatibility heuristics necessitated by the addition of
645 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
646 // heuristic is "if an app requests a permission but doesn't explicitly
647 // request the corresponding <uses-feature>, presume it's there anyway".
648 bool specCameraFeature = false; // camera-related
649 bool specCameraAutofocusFeature = false;
650 bool reqCameraAutofocusFeature = false;
651 bool reqCameraFlashFeature = false;
Dianne Hackborne5276a72009-08-27 16:28:44 -0700652 bool hasCameraPermission = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700653 bool specLocationFeature = false; // location-related
654 bool specNetworkLocFeature = false;
655 bool reqNetworkLocFeature = false;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800656 bool specGpsFeature = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700657 bool reqGpsFeature = false;
658 bool hasMockLocPermission = false;
659 bool hasCoarseLocPermission = false;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800660 bool hasGpsPermission = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700661 bool hasGeneralLocPermission = false;
662 bool specBluetoothFeature = false; // Bluetooth API-related
663 bool hasBluetoothPermission = false;
664 bool specMicrophoneFeature = false; // microphone-related
665 bool hasRecordAudioPermission = false;
666 bool specWiFiFeature = false;
667 bool hasWiFiPermission = false;
668 bool specTelephonyFeature = false; // telephony-related
669 bool reqTelephonySubFeature = false;
670 bool hasTelephonyPermission = false;
671 bool specTouchscreenFeature = false; // touchscreen-related
672 bool specMultitouchFeature = false;
673 bool reqDistinctMultitouchFeature = false;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700674 bool specScreenPortraitFeature = false;
675 bool specScreenLandscapeFeature = false;
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -0700676 bool reqScreenPortraitFeature = false;
677 bool reqScreenLandscapeFeature = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700678 // 2.2 also added some other features that apps can request, but that
679 // have no corresponding permission, so we cannot implement any
680 // back-compatibility heuristic for them. The below are thus unnecessary
681 // (but are retained here for documentary purposes.)
682 //bool specCompassFeature = false;
683 //bool specAccelerometerFeature = false;
684 //bool specProximityFeature = false;
685 //bool specAmbientLightFeature = false;
686 //bool specLiveWallpaperFeature = false;
687
Dianne Hackborn723738c2009-06-25 19:48:04 -0700688 int targetSdk = 0;
689 int smallScreen = 1;
690 int normalScreen = 1;
691 int largeScreen = 1;
Dianne Hackbornf43489d2010-08-20 12:44:33 -0700692 int xlargeScreen = 1;
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700693 int anyDensity = 1;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700694 int requiresSmallestWidthDp = 0;
695 int compatibleWidthLimitDp = 0;
696 int largestWidthLimitDp = 0;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700697 String8 pkg;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800698 String8 activityName;
699 String8 activityLabel;
700 String8 activityIcon;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700701 String8 receiverName;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700702 String8 serviceName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800703 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
704 if (code == ResXMLTree::END_TAG) {
705 depth--;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700706 if (depth < 2) {
707 withinApplication = false;
708 } else if (depth < 3) {
709 if (withinActivity && isMainActivity && isLauncherActivity) {
710 const char *aName = getComponentName(pkg, activityName);
Dianne Hackborne289bff2011-06-13 19:33:22 -0700711 printf("launchable-activity:");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700712 if (aName != NULL) {
Dianne Hackborne289bff2011-06-13 19:33:22 -0700713 printf(" name='%s' ", aName);
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700714 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700715 printf(" label='%s' icon='%s'\n",
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700716 activityLabel.string(),
717 activityIcon.string());
718 }
719 if (!hasIntentFilter) {
720 hasOtherActivities |= withinActivity;
721 hasOtherReceivers |= withinReceiver;
722 hasOtherServices |= withinService;
723 }
724 withinActivity = false;
725 withinService = false;
726 withinReceiver = false;
727 hasIntentFilter = false;
728 isMainActivity = isLauncherActivity = false;
729 } else if (depth < 4) {
730 if (withinIntentFilter) {
731 if (withinActivity) {
732 hasMainActivity |= actMainActivity;
733 hasOtherActivities |= !actMainActivity;
734 } else if (withinReceiver) {
735 hasWidgetReceivers |= actWidgetReceivers;
736 hasOtherReceivers |= !actWidgetReceivers;
737 } else if (withinService) {
738 hasImeService |= actImeService;
739 hasWallpaperService |= actWallpaperService;
740 hasOtherServices |= (!actImeService && !actWallpaperService);
741 }
742 }
743 withinIntentFilter = false;
744 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800745 continue;
746 }
747 if (code != ResXMLTree::START_TAG) {
748 continue;
749 }
750 depth++;
751 String8 tag(tree.getElementName(&len));
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700752 //printf("Depth %d, %s\n", depth, tag.string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800753 if (depth == 1) {
754 if (tag != "manifest") {
755 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
756 goto bail;
757 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700758 pkg = getAttribute(tree, NULL, "package", NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800759 printf("package: name='%s' ", pkg.string());
760 int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
761 if (error != "") {
762 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
763 goto bail;
764 }
765 if (versionCode > 0) {
766 printf("versionCode='%d' ", versionCode);
767 } else {
768 printf("versionCode='' ");
769 }
Dianne Hackborncf244ad2010-03-09 15:00:30 -0800770 String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800771 if (error != "") {
772 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
773 goto bail;
774 }
775 printf("versionName='%s'\n", versionName.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700776 } else if (depth == 2) {
777 withinApplication = false;
778 if (tag == "application") {
779 withinApplication = true;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700780
781 String8 label;
782 const size_t NL = locales.size();
783 for (size_t i=0; i<NL; i++) {
784 const char* localeStr = locales[i].string();
785 assets.setLocale(localeStr != NULL ? localeStr : "");
786 String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
787 if (llabel != "") {
788 if (localeStr == NULL || strlen(localeStr) == 0) {
789 label = llabel;
790 printf("application-label:'%s'\n", llabel.string());
791 } else {
792 if (label == "") {
793 label = llabel;
794 }
795 printf("application-label-%s:'%s'\n", localeStr,
796 llabel.string());
797 }
798 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700799 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700800
801 ResTable_config tmpConfig = config;
802 const size_t ND = densities.size();
803 for (size_t i=0; i<ND; i++) {
804 tmpConfig.density = densities[i];
805 assets.setConfiguration(tmpConfig);
806 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
807 if (icon != "") {
808 printf("application-icon-%d:'%s'\n", densities[i], icon.string());
809 }
810 }
811 assets.setConfiguration(config);
812
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700813 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
814 if (error != "") {
815 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
816 goto bail;
817 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700818 int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700819 if (error != "") {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700820 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700821 goto bail;
822 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700823 printf("application: label='%s' ", label.string());
824 printf("icon='%s'\n", icon.string());
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700825 if (testOnly != 0) {
826 printf("testOnly='%d'\n", testOnly);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700827 }
Dan Morrillb6ec11e2012-04-03 12:44:40 -0700828
829 int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0);
830 if (error != "") {
831 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string());
832 goto bail;
833 }
834 if (debuggable != 0) {
835 printf("application-debuggable\n");
836 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700837 } else if (tag == "uses-sdk") {
838 int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
839 if (error != "") {
840 error = "";
841 String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
842 if (error != "") {
843 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
844 error.string());
845 goto bail;
846 }
Dianne Hackborn723738c2009-06-25 19:48:04 -0700847 if (name == "Donut") targetSdk = 4;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700848 printf("sdkVersion:'%s'\n", name.string());
849 } else if (code != -1) {
Dianne Hackborn723738c2009-06-25 19:48:04 -0700850 targetSdk = code;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700851 printf("sdkVersion:'%d'\n", code);
852 }
Suchi Amalapurapu75c49842009-08-14 15:13:09 -0700853 code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
854 if (code != -1) {
855 printf("maxSdkVersion:'%d'\n", code);
856 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700857 code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
858 if (error != "") {
859 error = "";
860 String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
861 if (error != "") {
862 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
863 error.string());
864 goto bail;
865 }
Dianne Hackborn723738c2009-06-25 19:48:04 -0700866 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700867 printf("targetSdkVersion:'%s'\n", name.string());
868 } else if (code != -1) {
Dianne Hackborn723738c2009-06-25 19:48:04 -0700869 if (targetSdk < code) {
870 targetSdk = code;
871 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700872 printf("targetSdkVersion:'%d'\n", code);
873 }
874 } else if (tag == "uses-configuration") {
875 int32_t reqTouchScreen = getIntegerAttribute(tree,
876 REQ_TOUCH_SCREEN_ATTR, NULL, 0);
877 int32_t reqKeyboardType = getIntegerAttribute(tree,
878 REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
879 int32_t reqHardKeyboard = getIntegerAttribute(tree,
880 REQ_HARD_KEYBOARD_ATTR, NULL, 0);
881 int32_t reqNavigation = getIntegerAttribute(tree,
882 REQ_NAVIGATION_ATTR, NULL, 0);
883 int32_t reqFiveWayNav = getIntegerAttribute(tree,
884 REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
Dianne Hackborncb2d50d2010-01-06 11:29:54 -0800885 printf("uses-configuration:");
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700886 if (reqTouchScreen != 0) {
887 printf(" reqTouchScreen='%d'", reqTouchScreen);
888 }
889 if (reqKeyboardType != 0) {
890 printf(" reqKeyboardType='%d'", reqKeyboardType);
891 }
892 if (reqHardKeyboard != 0) {
893 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
894 }
895 if (reqNavigation != 0) {
896 printf(" reqNavigation='%d'", reqNavigation);
897 }
898 if (reqFiveWayNav != 0) {
899 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
900 }
901 printf("\n");
Dianne Hackborn723738c2009-06-25 19:48:04 -0700902 } else if (tag == "supports-screens") {
903 smallScreen = getIntegerAttribute(tree,
904 SMALL_SCREEN_ATTR, NULL, 1);
905 normalScreen = getIntegerAttribute(tree,
906 NORMAL_SCREEN_ATTR, NULL, 1);
907 largeScreen = getIntegerAttribute(tree,
908 LARGE_SCREEN_ATTR, NULL, 1);
Dianne Hackbornf43489d2010-08-20 12:44:33 -0700909 xlargeScreen = getIntegerAttribute(tree,
910 XLARGE_SCREEN_ATTR, NULL, 1);
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700911 anyDensity = getIntegerAttribute(tree,
912 ANY_DENSITY_ATTR, NULL, 1);
Dianne Hackborne289bff2011-06-13 19:33:22 -0700913 requiresSmallestWidthDp = getIntegerAttribute(tree,
914 REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
915 compatibleWidthLimitDp = getIntegerAttribute(tree,
916 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
917 largestWidthLimitDp = getIntegerAttribute(tree,
918 LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
Dianne Hackborne5276a72009-08-27 16:28:44 -0700919 } else if (tag == "uses-feature") {
920 String8 name = getAttribute(tree, NAME_ATTR, &error);
Suchi Amalapurapu40b94722009-09-20 13:39:37 -0700921
922 if (name != "" && error == "") {
Dianne Hackborne5276a72009-08-27 16:28:44 -0700923 int req = getIntegerAttribute(tree,
924 REQUIRED_ATTR, NULL, 1);
Dan Morrill89d97c12010-05-03 16:13:14 -0700925
Dianne Hackborne5276a72009-08-27 16:28:44 -0700926 if (name == "android.hardware.camera") {
927 specCameraFeature = true;
Dan Morrill89d97c12010-05-03 16:13:14 -0700928 } else if (name == "android.hardware.camera.autofocus") {
929 // these have no corresponding permission to check for,
930 // but should imply the foundational camera permission
931 reqCameraAutofocusFeature = reqCameraAutofocusFeature || req;
932 specCameraAutofocusFeature = true;
933 } else if (req && (name == "android.hardware.camera.flash")) {
934 // these have no corresponding permission to check for,
935 // but should imply the foundational camera permission
936 reqCameraFlashFeature = true;
937 } else if (name == "android.hardware.location") {
938 specLocationFeature = true;
939 } else if (name == "android.hardware.location.network") {
940 specNetworkLocFeature = true;
941 reqNetworkLocFeature = reqNetworkLocFeature || req;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800942 } else if (name == "android.hardware.location.gps") {
943 specGpsFeature = true;
Dan Morrill89d97c12010-05-03 16:13:14 -0700944 reqGpsFeature = reqGpsFeature || req;
945 } else if (name == "android.hardware.bluetooth") {
946 specBluetoothFeature = true;
947 } else if (name == "android.hardware.touchscreen") {
948 specTouchscreenFeature = true;
949 } else if (name == "android.hardware.touchscreen.multitouch") {
950 specMultitouchFeature = true;
951 } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
952 reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req;
953 } else if (name == "android.hardware.microphone") {
954 specMicrophoneFeature = true;
955 } else if (name == "android.hardware.wifi") {
956 specWiFiFeature = true;
957 } else if (name == "android.hardware.telephony") {
958 specTelephonyFeature = true;
959 } else if (req && (name == "android.hardware.telephony.gsm" ||
960 name == "android.hardware.telephony.cdma")) {
961 // these have no corresponding permission to check for,
962 // but should imply the foundational telephony permission
963 reqTelephonySubFeature = true;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700964 } else if (name == "android.hardware.screen.portrait") {
965 specScreenPortraitFeature = true;
966 } else if (name == "android.hardware.screen.landscape") {
967 specScreenLandscapeFeature = true;
Dianne Hackborne5276a72009-08-27 16:28:44 -0700968 }
969 printf("uses-feature%s:'%s'\n",
970 req ? "" : "-not-required", name.string());
971 } else {
972 int vers = getIntegerAttribute(tree,
973 GL_ES_VERSION_ATTR, &error);
974 if (error == "") {
975 printf("uses-gl-es:'0x%x'\n", vers);
976 }
977 }
978 } else if (tag == "uses-permission") {
979 String8 name = getAttribute(tree, NAME_ATTR, &error);
Suchi Amalapurapu40b94722009-09-20 13:39:37 -0700980 if (name != "" && error == "") {
Dianne Hackborne5276a72009-08-27 16:28:44 -0700981 if (name == "android.permission.CAMERA") {
982 hasCameraPermission = true;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800983 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
984 hasGpsPermission = true;
Dan Morrill89d97c12010-05-03 16:13:14 -0700985 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
986 hasMockLocPermission = true;
987 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
988 hasCoarseLocPermission = true;
989 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
990 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
991 hasGeneralLocPermission = true;
992 } else if (name == "android.permission.BLUETOOTH" ||
993 name == "android.permission.BLUETOOTH_ADMIN") {
994 hasBluetoothPermission = true;
995 } else if (name == "android.permission.RECORD_AUDIO") {
996 hasRecordAudioPermission = true;
997 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
998 name == "android.permission.CHANGE_WIFI_STATE" ||
999 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
1000 hasWiFiPermission = true;
1001 } else if (name == "android.permission.CALL_PHONE" ||
1002 name == "android.permission.CALL_PRIVILEGED" ||
1003 name == "android.permission.MODIFY_PHONE_STATE" ||
1004 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1005 name == "android.permission.READ_SMS" ||
1006 name == "android.permission.RECEIVE_SMS" ||
1007 name == "android.permission.RECEIVE_MMS" ||
1008 name == "android.permission.RECEIVE_WAP_PUSH" ||
1009 name == "android.permission.SEND_SMS" ||
1010 name == "android.permission.WRITE_APN_SETTINGS" ||
1011 name == "android.permission.WRITE_SMS") {
1012 hasTelephonyPermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001013 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1014 hasWriteExternalStoragePermission = true;
Dianne Hackborn79245122012-03-12 10:51:26 -07001015 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1016 hasReadExternalStoragePermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001017 } else if (name == "android.permission.READ_PHONE_STATE") {
1018 hasReadPhoneStatePermission = true;
Dianne Hackborne5276a72009-08-27 16:28:44 -07001019 }
1020 printf("uses-permission:'%s'\n", name.string());
1021 } else {
1022 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1023 error.string());
1024 goto bail;
1025 }
Dianne Hackborn43b68032010-09-02 17:14:41 -07001026 } else if (tag == "uses-package") {
1027 String8 name = getAttribute(tree, NAME_ATTR, &error);
1028 if (name != "" && error == "") {
1029 printf("uses-package:'%s'\n", name.string());
1030 } else {
1031 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1032 error.string());
1033 goto bail;
1034 }
Jeff Hamiltone2c17f92010-02-12 13:45:16 -06001035 } else if (tag == "original-package") {
1036 String8 name = getAttribute(tree, NAME_ATTR, &error);
1037 if (name != "" && error == "") {
1038 printf("original-package:'%s'\n", name.string());
1039 } else {
1040 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1041 error.string());
1042 goto bail;
1043 }
Dan Morrill096b67f2010-12-13 16:25:54 -08001044 } else if (tag == "supports-gl-texture") {
Dan Morrill6f51fc12010-10-13 14:33:43 -07001045 String8 name = getAttribute(tree, NAME_ATTR, &error);
1046 if (name != "" && error == "") {
Dan Morrill096b67f2010-12-13 16:25:54 -08001047 printf("supports-gl-texture:'%s'\n", name.string());
Dan Morrill6f51fc12010-10-13 14:33:43 -07001048 } else {
1049 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1050 error.string());
1051 goto bail;
1052 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001053 } else if (tag == "compatible-screens") {
1054 printCompatibleScreens(tree);
1055 depth--;
Kenny Root56088a52011-09-29 13:49:45 -07001056 } else if (tag == "package-verifier") {
1057 String8 name = getAttribute(tree, NAME_ATTR, &error);
1058 if (name != "" && error == "") {
1059 String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
1060 if (publicKey != "" && error == "") {
1061 printf("package-verifier: name='%s' publicKey='%s'\n",
1062 name.string(), publicKey.string());
1063 }
1064 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001065 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001066 } else if (depth == 3 && withinApplication) {
1067 withinActivity = false;
1068 withinReceiver = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001069 withinService = false;
1070 hasIntentFilter = false;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001071 if(tag == "activity") {
1072 withinActivity = true;
1073 activityName = getAttribute(tree, NAME_ATTR, &error);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001074 if (error != "") {
1075 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1076 goto bail;
1077 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001078
1079 activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001080 if (error != "") {
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001081 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001082 goto bail;
1083 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001084
1085 activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1086 if (error != "") {
1087 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
1088 goto bail;
1089 }
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001090
1091 int32_t orien = getResolvedIntegerAttribute(&res, tree,
1092 SCREEN_ORIENTATION_ATTR, &error);
1093 if (error == "") {
1094 if (orien == 0 || orien == 6 || orien == 8) {
1095 // Requests landscape, sensorLandscape, or reverseLandscape.
1096 reqScreenLandscapeFeature = true;
1097 } else if (orien == 1 || orien == 7 || orien == 9) {
1098 // Requests portrait, sensorPortrait, or reversePortrait.
1099 reqScreenPortraitFeature = true;
1100 }
1101 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001102 } else if (tag == "uses-library") {
1103 String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1104 if (error != "") {
1105 fprintf(stderr, "ERROR getting 'android:name' attribute for uses-library: %s\n", error.string());
1106 goto bail;
1107 }
Dianne Hackborn49237342009-08-27 20:08:01 -07001108 int req = getIntegerAttribute(tree,
1109 REQUIRED_ATTR, NULL, 1);
1110 printf("uses-library%s:'%s'\n",
1111 req ? "" : "-not-required", libraryName.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001112 } else if (tag == "receiver") {
1113 withinReceiver = true;
1114 receiverName = getAttribute(tree, NAME_ATTR, &error);
1115
1116 if (error != "") {
1117 fprintf(stderr, "ERROR getting 'android:name' attribute for receiver: %s\n", error.string());
1118 goto bail;
1119 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001120 } else if (tag == "service") {
1121 withinService = true;
1122 serviceName = getAttribute(tree, NAME_ATTR, &error);
1123
1124 if (error != "") {
1125 fprintf(stderr, "ERROR getting 'android:name' attribute for service: %s\n", error.string());
1126 goto bail;
1127 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001128 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001129 } else if ((depth == 4) && (tag == "intent-filter")) {
1130 hasIntentFilter = true;
1131 withinIntentFilter = true;
1132 actMainActivity = actWidgetReceivers = actImeService = actWallpaperService = false;
1133 } else if ((depth == 5) && withinIntentFilter){
1134 String8 action;
1135 if (tag == "action") {
1136 action = getAttribute(tree, NAME_ATTR, &error);
1137 if (error != "") {
1138 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1139 goto bail;
1140 }
1141 if (withinActivity) {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001142 if (action == "android.intent.action.MAIN") {
1143 isMainActivity = true;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001144 actMainActivity = true;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001145 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001146 } else if (withinReceiver) {
1147 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1148 actWidgetReceivers = true;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001149 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001150 } else if (withinService) {
1151 if (action == "android.view.InputMethod") {
1152 actImeService = true;
1153 } else if (action == "android.service.wallpaper.WallpaperService") {
1154 actWallpaperService = true;
1155 }
1156 }
1157 if (action == "android.intent.action.SEARCH") {
1158 isSearchable = true;
1159 }
1160 }
1161
1162 if (tag == "category") {
1163 String8 category = getAttribute(tree, NAME_ATTR, &error);
1164 if (error != "") {
1165 fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string());
1166 goto bail;
1167 }
1168 if (withinActivity) {
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001169 if (category == "android.intent.category.LAUNCHER") {
1170 isLauncherActivity = true;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001171 }
1172 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001173 }
1174 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001175 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001176
Kenny Root063a44e2011-12-08 08:46:03 -08001177 // Pre-1.6 implicitly granted permission compatibility logic
1178 if (targetSdk < 4) {
1179 if (!hasWriteExternalStoragePermission) {
1180 printf("uses-permission:'android.permission.WRITE_EXTERNAL_STORAGE'\n");
Dianne Hackborn79245122012-03-12 10:51:26 -07001181 hasWriteExternalStoragePermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001182 }
1183 if (!hasReadPhoneStatePermission) {
1184 printf("uses-permission:'android.permission.READ_PHONE_STATE'\n");
1185 }
1186 }
1187
Dianne Hackborn79245122012-03-12 10:51:26 -07001188 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1189 // force them to always take READ_EXTERNAL_STORAGE as well.
1190 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
1191 printf("uses-permission:'android.permission.READ_EXTERNAL_STORAGE'\n");
1192 }
1193
Dan Morrill89d97c12010-05-03 16:13:14 -07001194 /* The following blocks handle printing "inferred" uses-features, based
1195 * on whether related features or permissions are used by the app.
1196 * Note that the various spec*Feature variables denote whether the
1197 * relevant tag was *present* in the AndroidManfest, not that it was
1198 * present and set to true.
1199 */
1200 // Camera-related back-compatibility logic
1201 if (!specCameraFeature) {
1202 if (reqCameraFlashFeature || reqCameraAutofocusFeature) {
1203 // if app requested a sub-feature (autofocus or flash) and didn't
1204 // request the base camera feature, we infer that it meant to
1205 printf("uses-feature:'android.hardware.camera'\n");
1206 } else if (hasCameraPermission) {
1207 // if app wants to use camera but didn't request the feature, we infer
1208 // that it meant to, and further that it wants autofocus
1209 // (which was the 1.0 - 1.5 behavior)
1210 printf("uses-feature:'android.hardware.camera'\n");
1211 if (!specCameraAutofocusFeature) {
1212 printf("uses-feature:'android.hardware.camera.autofocus'\n");
1213 }
1214 }
Dianne Hackborne5276a72009-08-27 16:28:44 -07001215 }
Doug Zongkerdbe7a682009-10-09 11:24:51 -07001216
Dan Morrill89d97c12010-05-03 16:13:14 -07001217 // Location-related back-compatibility logic
1218 if (!specLocationFeature &&
1219 (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission ||
1220 hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) {
1221 // if app either takes a location-related permission or requests one of the
1222 // sub-features, we infer that it also meant to request the base location feature
1223 printf("uses-feature:'android.hardware.location'\n");
1224 }
Dianne Hackbornef05e072010-03-01 17:43:39 -08001225 if (!specGpsFeature && hasGpsPermission) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001226 // if app takes GPS (FINE location) perm but does not request the GPS
1227 // feature, we infer that it meant to
Dianne Hackbornef05e072010-03-01 17:43:39 -08001228 printf("uses-feature:'android.hardware.location.gps'\n");
1229 }
Dan Morrill89d97c12010-05-03 16:13:14 -07001230 if (!specNetworkLocFeature && hasCoarseLocPermission) {
1231 // if app takes Network location (COARSE location) perm but does not request the
1232 // network location feature, we infer that it meant to
1233 printf("uses-feature:'android.hardware.location.network'\n");
1234 }
1235
1236 // Bluetooth-related compatibility logic
Dan Morrill6b22d812010-06-15 21:41:42 -07001237 if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001238 // if app takes a Bluetooth permission but does not request the Bluetooth
1239 // feature, we infer that it meant to
1240 printf("uses-feature:'android.hardware.bluetooth'\n");
1241 }
1242
1243 // Microphone-related compatibility logic
1244 if (!specMicrophoneFeature && hasRecordAudioPermission) {
1245 // if app takes the record-audio permission but does not request the microphone
1246 // feature, we infer that it meant to
1247 printf("uses-feature:'android.hardware.microphone'\n");
1248 }
1249
1250 // WiFi-related compatibility logic
1251 if (!specWiFiFeature && hasWiFiPermission) {
1252 // if app takes one of the WiFi permissions but does not request the WiFi
1253 // feature, we infer that it meant to
1254 printf("uses-feature:'android.hardware.wifi'\n");
1255 }
1256
1257 // Telephony-related compatibility logic
1258 if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) {
1259 // if app takes one of the telephony permissions or requests a sub-feature but
1260 // does not request the base telephony feature, we infer that it meant to
1261 printf("uses-feature:'android.hardware.telephony'\n");
1262 }
1263
1264 // Touchscreen-related back-compatibility logic
1265 if (!specTouchscreenFeature) { // not a typo!
1266 // all apps are presumed to require a touchscreen, unless they explicitly say
1267 // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
1268 // Note that specTouchscreenFeature is true if the tag is present, regardless
1269 // of whether its value is true or false, so this is safe
1270 printf("uses-feature:'android.hardware.touchscreen'\n");
1271 }
1272 if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
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.touchscreen.multitouch'\n");
1276 }
Dianne Hackbornef05e072010-03-01 17:43:39 -08001277
Dianne Hackborne289bff2011-06-13 19:33:22 -07001278 // Landscape/portrait-related compatibility logic
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001279 if (!specScreenLandscapeFeature && !specScreenPortraitFeature) {
1280 // If the app has specified any activities in its manifest
1281 // that request a specific orientation, then assume that
1282 // orientation is required.
1283 if (reqScreenLandscapeFeature) {
1284 printf("uses-feature:'android.hardware.screen.landscape'\n");
1285 }
1286 if (reqScreenPortraitFeature) {
1287 printf("uses-feature:'android.hardware.screen.portrait'\n");
1288 }
Dianne Hackborne289bff2011-06-13 19:33:22 -07001289 }
1290
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001291 if (hasMainActivity) {
1292 printf("main\n");
1293 }
1294 if (hasWidgetReceivers) {
1295 printf("app-widget\n");
1296 }
1297 if (hasImeService) {
1298 printf("ime\n");
1299 }
1300 if (hasWallpaperService) {
1301 printf("wallpaper\n");
1302 }
1303 if (hasOtherActivities) {
1304 printf("other-activities\n");
1305 }
1306 if (isSearchable) {
1307 printf("search\n");
1308 }
1309 if (hasOtherReceivers) {
1310 printf("other-receivers\n");
1311 }
1312 if (hasOtherServices) {
1313 printf("other-services\n");
1314 }
1315
Dianne Hackborne289bff2011-06-13 19:33:22 -07001316 // For modern apps, if screen size buckets haven't been specified
1317 // but the new width ranges have, then infer the buckets from them.
1318 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1319 && requiresSmallestWidthDp > 0) {
1320 int compatWidth = compatibleWidthLimitDp;
1321 if (compatWidth <= 0) compatWidth = requiresSmallestWidthDp;
1322 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1323 smallScreen = -1;
1324 } else {
1325 smallScreen = 0;
1326 }
1327 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1328 normalScreen = -1;
1329 } else {
1330 normalScreen = 0;
1331 }
1332 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1333 largeScreen = -1;
1334 } else {
1335 largeScreen = 0;
1336 }
1337 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1338 xlargeScreen = -1;
1339 } else {
1340 xlargeScreen = 0;
1341 }
1342 }
1343
Dianne Hackborn723738c2009-06-25 19:48:04 -07001344 // Determine default values for any unspecified screen sizes,
1345 // based on the target SDK of the package. As of 4 (donut)
1346 // the screen size support was introduced, so all default to
1347 // enabled.
1348 if (smallScreen > 0) {
1349 smallScreen = targetSdk >= 4 ? -1 : 0;
1350 }
1351 if (normalScreen > 0) {
1352 normalScreen = -1;
1353 }
1354 if (largeScreen > 0) {
1355 largeScreen = targetSdk >= 4 ? -1 : 0;
1356 }
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001357 if (xlargeScreen > 0) {
Scott Maind58fb972010-11-04 18:32:00 -07001358 // Introduced in Gingerbread.
1359 xlargeScreen = targetSdk >= 9 ? -1 : 0;
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001360 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001361 if (anyDensity > 0) {
Dianne Hackborne289bff2011-06-13 19:33:22 -07001362 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1363 || compatibleWidthLimitDp > 0) ? -1 : 0;
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001364 }
Dianne Hackborn723738c2009-06-25 19:48:04 -07001365 printf("supports-screens:");
1366 if (smallScreen != 0) printf(" 'small'");
1367 if (normalScreen != 0) printf(" 'normal'");
1368 if (largeScreen != 0) printf(" 'large'");
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001369 if (xlargeScreen != 0) printf(" 'xlarge'");
Dianne Hackborn723738c2009-06-25 19:48:04 -07001370 printf("\n");
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001371 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
Dianne Hackborne289bff2011-06-13 19:33:22 -07001372 if (requiresSmallestWidthDp > 0) {
1373 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1374 }
1375 if (compatibleWidthLimitDp > 0) {
1376 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1377 }
1378 if (largestWidthLimitDp > 0) {
1379 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1380 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001381
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001382 printf("locales:");
Dianne Hackborne17086b2009-06-19 15:13:28 -07001383 const size_t NL = locales.size();
1384 for (size_t i=0; i<NL; i++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001385 const char* localeStr = locales[i].string();
1386 if (localeStr == NULL || strlen(localeStr) == 0) {
1387 localeStr = "--_--";
1388 }
1389 printf(" '%s'", localeStr);
1390 }
1391 printf("\n");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001392
Dianne Hackborne17086b2009-06-19 15:13:28 -07001393 printf("densities:");
1394 const size_t ND = densities.size();
1395 for (size_t i=0; i<ND; i++) {
1396 printf(" '%d'", densities[i]);
1397 }
1398 printf("\n");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001399
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001400 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1401 if (dir != NULL) {
1402 if (dir->getFileCount() > 0) {
1403 printf("native-code:");
1404 for (size_t i=0; i<dir->getFileCount(); i++) {
1405 printf(" '%s'", dir->getFileName(i).string());
1406 }
1407 printf("\n");
1408 }
1409 delete dir;
1410 }
Dan Morrille74763e2012-01-06 10:47:10 -08001411 } else if (strcmp("badger", option) == 0) {
Dianne Hackborn6c997a92012-01-31 11:27:43 -08001412 printf("%s", CONSOLE_DATA);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001413 } else if (strcmp("configurations", option) == 0) {
1414 Vector<ResTable_config> configs;
1415 res.getConfigurations(&configs);
1416 const size_t N = configs.size();
1417 for (size_t i=0; i<N; i++) {
1418 printf("%s\n", configs[i].toString().string());
1419 }
1420 } else {
1421 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
1422 goto bail;
1423 }
1424 }
1425
1426 result = NO_ERROR;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001427
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001428bail:
1429 if (asset) {
1430 delete asset;
1431 }
1432 return (result != NO_ERROR);
1433}
1434
1435
1436/*
1437 * Handle the "add" command, which wants to add files to a new or
1438 * pre-existing archive.
1439 */
1440int doAdd(Bundle* bundle)
1441{
1442 ZipFile* zip = NULL;
1443 status_t result = UNKNOWN_ERROR;
1444 const char* zipFileName;
1445
1446 if (bundle->getUpdate()) {
1447 /* avoid confusion */
1448 fprintf(stderr, "ERROR: can't use '-u' with add\n");
1449 goto bail;
1450 }
1451
1452 if (bundle->getFileSpecCount() < 1) {
1453 fprintf(stderr, "ERROR: must specify zip file name\n");
1454 goto bail;
1455 }
1456 zipFileName = bundle->getFileSpecEntry(0);
1457
1458 if (bundle->getFileSpecCount() < 2) {
1459 fprintf(stderr, "NOTE: nothing to do\n");
1460 goto bail;
1461 }
1462
1463 zip = openReadWrite(zipFileName, true);
1464 if (zip == NULL) {
1465 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
1466 goto bail;
1467 }
1468
1469 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1470 const char* fileName = bundle->getFileSpecEntry(i);
1471
1472 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
1473 printf(" '%s'... (from gzip)\n", fileName);
1474 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
1475 } else {
Doug Zongkerdbe7a682009-10-09 11:24:51 -07001476 if (bundle->getJunkPath()) {
1477 String8 storageName = String8(fileName).getPathLeaf();
1478 printf(" '%s' as '%s'...\n", fileName, storageName.string());
1479 result = zip->add(fileName, storageName.string(),
1480 bundle->getCompressionMethod(), NULL);
1481 } else {
1482 printf(" '%s'...\n", fileName);
1483 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
1484 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001485 }
1486 if (result != NO_ERROR) {
1487 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
1488 if (result == NAME_NOT_FOUND)
1489 fprintf(stderr, ": file not found\n");
1490 else if (result == ALREADY_EXISTS)
1491 fprintf(stderr, ": already exists in archive\n");
1492 else
1493 fprintf(stderr, "\n");
1494 goto bail;
1495 }
1496 }
1497
1498 result = NO_ERROR;
1499
1500bail:
1501 delete zip;
1502 return (result != NO_ERROR);
1503}
1504
1505
1506/*
1507 * Delete files from an existing archive.
1508 */
1509int doRemove(Bundle* bundle)
1510{
1511 ZipFile* zip = NULL;
1512 status_t result = UNKNOWN_ERROR;
1513 const char* zipFileName;
1514
1515 if (bundle->getFileSpecCount() < 1) {
1516 fprintf(stderr, "ERROR: must specify zip file name\n");
1517 goto bail;
1518 }
1519 zipFileName = bundle->getFileSpecEntry(0);
1520
1521 if (bundle->getFileSpecCount() < 2) {
1522 fprintf(stderr, "NOTE: nothing to do\n");
1523 goto bail;
1524 }
1525
1526 zip = openReadWrite(zipFileName, false);
1527 if (zip == NULL) {
1528 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
1529 zipFileName);
1530 goto bail;
1531 }
1532
1533 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1534 const char* fileName = bundle->getFileSpecEntry(i);
1535 ZipEntry* entry;
1536
1537 entry = zip->getEntryByName(fileName);
1538 if (entry == NULL) {
1539 printf(" '%s' NOT FOUND\n", fileName);
1540 continue;
1541 }
1542
1543 result = zip->remove(entry);
1544
1545 if (result != NO_ERROR) {
1546 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
1547 bundle->getFileSpecEntry(i), zipFileName);
1548 goto bail;
1549 }
1550 }
1551
1552 /* update the archive */
1553 zip->flush();
1554
1555bail:
1556 delete zip;
1557 return (result != NO_ERROR);
1558}
1559
1560
1561/*
1562 * Package up an asset directory and associated application files.
1563 */
1564int doPackage(Bundle* bundle)
1565{
1566 const char* outputAPKFile;
1567 int retVal = 1;
1568 status_t err;
1569 sp<AaptAssets> assets;
1570 int N;
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001571 FILE* fp;
1572 String8 dependencyFile;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001573
1574 // -c zz_ZZ means do pseudolocalization
1575 ResourceFilter filter;
1576 err = filter.parse(bundle->getConfigurations());
1577 if (err != NO_ERROR) {
1578 goto bail;
1579 }
1580 if (filter.containsPseudo()) {
1581 bundle->setPseudolocalize(true);
1582 }
1583
1584 N = bundle->getFileSpecCount();
1585 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
1586 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDir() == NULL) {
1587 fprintf(stderr, "ERROR: no input files\n");
1588 goto bail;
1589 }
1590
1591 outputAPKFile = bundle->getOutputAPKFile();
1592
1593 // Make sure the filenames provided exist and are of the appropriate type.
1594 if (outputAPKFile) {
1595 FileType type;
1596 type = getFileType(outputAPKFile);
1597 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
1598 fprintf(stderr,
1599 "ERROR: output file '%s' exists but is not regular file\n",
1600 outputAPKFile);
1601 goto bail;
1602 }
1603 }
1604
1605 // Load the assets.
1606 assets = new AaptAssets();
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001607
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001608 // Set up the resource gathering in assets if we're going to generate
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001609 // dependency files. Every time we encounter a resource while slurping
1610 // the tree, we'll add it to these stores so we have full resource paths
1611 // to write to a dependency file.
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001612 if (bundle->getGenDependencies()) {
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001613 sp<FilePathStore> resPathStore = new FilePathStore;
1614 assets->setFullResPaths(resPathStore);
1615 sp<FilePathStore> assetPathStore = new FilePathStore;
1616 assets->setFullAssetPaths(assetPathStore);
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001617 }
1618
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001619 err = assets->slurpFromArgs(bundle);
1620 if (err < 0) {
1621 goto bail;
1622 }
1623
1624 if (bundle->getVerbose()) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001625 assets->print(String8());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001626 }
1627
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001628 // If they asked for any fileAs that need to be compiled, do so.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001629 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
1630 err = buildResources(bundle, assets);
1631 if (err != 0) {
1632 goto bail;
1633 }
1634 }
1635
1636 // At this point we've read everything and processed everything. From here
1637 // on out it's just writing output files.
1638 if (SourcePos::hasErrors()) {
1639 goto bail;
1640 }
1641
Dianne Hackborn1644c6d2012-02-06 15:33:21 -08001642 // Update symbols with information about which ones are needed as Java symbols.
1643 assets->applyJavaSymbols();
1644 if (SourcePos::hasErrors()) {
1645 goto bail;
1646 }
1647
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001648 // If we've been asked to generate a dependency file, do that here
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001649 if (bundle->getGenDependencies()) {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001650 // If this is the packaging step, generate the dependency file next to
1651 // the output apk (e.g. bin/resources.ap_.d)
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001652 if (outputAPKFile) {
1653 dependencyFile = String8(outputAPKFile);
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001654 // Add the .d extension to the dependency file.
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001655 dependencyFile.append(".d");
1656 } else {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001657 // Else if this is the R.java dependency generation step,
1658 // generate the dependency file in the R.java package subdirectory
1659 // e.g. gen/com/foo/app/R.java.d
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001660 dependencyFile = String8(bundle->getRClassDir());
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001661 dependencyFile.appendPath("R.java.d");
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001662 }
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001663 // Make sure we have a clean dependency file to start with
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001664 fp = fopen(dependencyFile, "w");
1665 fclose(fp);
1666 }
1667
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001668 // Write out R.java constants
Dianne Hackborn1644c6d2012-02-06 15:33:21 -08001669 if (!assets->havePrivateSymbols()) {
Xavier Ducrohet63459ad2009-11-30 18:05:10 -08001670 if (bundle->getCustomPackage() == NULL) {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001671 // Write the R.java file into the appropriate class directory
1672 // e.g. gen/com/foo/app/R.java
Xavier Ducrohet63459ad2009-11-30 18:05:10 -08001673 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001674 // If we have library files, we're going to write our R.java file into
1675 // the appropriate class directory for those libraries as well.
1676 // e.g. gen/com/foo/app/lib/R.java
Josiah Gaskince89f152011-06-08 19:31:40 -07001677 if (bundle->getExtraPackages() != NULL) {
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001678 // Split on colon
Josiah Gaskince89f152011-06-08 19:31:40 -07001679 String8 libs(bundle->getExtraPackages());
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001680 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
Josiah Gaskince89f152011-06-08 19:31:40 -07001681 while (packageString != NULL) {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001682 // Write the R.java file out with the correct package name
Josiah Gaskince89f152011-06-08 19:31:40 -07001683 err = writeResourceSymbols(bundle, assets, String8(packageString), true);
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001684 packageString = strtok(NULL, ":");
Josiah Gaskince89f152011-06-08 19:31:40 -07001685 }
1686 libs.unlockBuffer();
1687 }
Xavier Ducrohet63459ad2009-11-30 18:05:10 -08001688 } else {
1689 const String8 customPkg(bundle->getCustomPackage());
1690 err = writeResourceSymbols(bundle, assets, customPkg, true);
1691 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001692 if (err < 0) {
1693 goto bail;
1694 }
1695 } else {
1696 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
1697 if (err < 0) {
1698 goto bail;
1699 }
1700 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
1701 if (err < 0) {
1702 goto bail;
1703 }
1704 }
1705
Joe Onorato1553c822009-08-30 13:36:22 -07001706 // Write out the ProGuard file
1707 err = writeProguardFile(bundle, assets);
1708 if (err < 0) {
1709 goto bail;
1710 }
1711
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001712 // Write the apk
1713 if (outputAPKFile) {
1714 err = writeAPK(bundle, assets, String8(outputAPKFile));
1715 if (err != NO_ERROR) {
1716 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
1717 goto bail;
1718 }
1719 }
1720
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001721 // If we've been asked to generate a dependency file, we need to finish up here.
1722 // the writeResourceSymbols and writeAPK functions have already written the target
1723 // half of the dependency file, now we need to write the prerequisites. (files that
1724 // the R.java file or .ap_ file depend on)
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001725 if (bundle->getGenDependencies()) {
1726 // Now that writeResourceSymbols or writeAPK has taken care of writing
1727 // the targets to our dependency file, we'll write the prereqs
1728 fp = fopen(dependencyFile, "a+");
1729 fprintf(fp, " : ");
1730 bool includeRaw = (outputAPKFile != NULL);
1731 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001732 // Also manually add the AndroidManifeset since it's not under res/ or assets/
1733 // and therefore was not added to our pathstores during slurping
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001734 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
1735 fclose(fp);
1736 }
1737
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001738 retVal = 0;
1739bail:
1740 if (SourcePos::hasErrors()) {
1741 SourcePos::printErrors(stderr);
1742 }
1743 return retVal;
1744}
Josiah Gaskin8a39da82011-06-06 17:00:35 -07001745
1746/*
1747 * Do PNG Crunching
1748 * PRECONDITIONS
1749 * -S flag points to a source directory containing drawable* folders
1750 * -C flag points to destination directory. The folder structure in the
1751 * source directory will be mirrored to the destination (cache) directory
1752 *
1753 * POSTCONDITIONS
1754 * Destination directory will be updated to match the PNG files in
1755 * the source directory.
1756 */
1757int doCrunch(Bundle* bundle)
1758{
1759 fprintf(stdout, "Crunching PNG Files in ");
1760 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
1761 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
1762
1763 updatePreProcessedCache(bundle);
1764
1765 return NO_ERROR;
1766}
Dan Morrille74763e2012-01-06 10:47:10 -08001767
1768char CONSOLE_DATA[2925] = {
1769 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1770 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1771 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1772 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1773 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
1774 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
1775 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1776 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1777 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
1778 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
1779 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1780 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1781 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
1782 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1783 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1784 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
1785 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
1786 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1787 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1788 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
1789 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
1790 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
1791 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
1792 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
1793 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1794 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1795 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
1796 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
1797 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
1798 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1799 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
1800 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1801 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1802 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
1803 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
1804 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1805 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1806 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
1807 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
1808 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
1809 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1810 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
1811 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1812 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1813 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
1814 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
1815 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
1816 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
1817 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
1818 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1819 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
1820 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
1821 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
1822 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1823 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
1824 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
1825 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
1826 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
1827 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
1828 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
1829 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
1830 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
1831 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
1832 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
1833 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
1834 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
1835 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
1836 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
1837 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
1838 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
1839 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
1840 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
1841 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1842 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
1843 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
1844 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
1845 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1846 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
1847 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
1848 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1849 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
1850 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
1851 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
1852 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1853 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
1854 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
1855 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1856 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
1857 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
1858 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1859 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1860 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
1861 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
1862 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1863 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1864 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
1865 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1866 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
1867 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
1868 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
1869 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1870 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1871 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
1872 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
1873 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
1874 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1875 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
1876 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1877 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1878 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
1879 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
1880 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1881 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1882 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
1883 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1884 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
1885 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
1886 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
1887 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1888 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1889 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
1890 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
1891 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
1892 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
1893 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
1894 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
1895 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
1896 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
1897 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
1898 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1899 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1900 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
1901 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
1902 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
1903 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
1904 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
1905 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1906 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1907 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
1908 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
1909 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
1910 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1911 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
1912 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1913 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1914 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
1915 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
1916 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1917 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1918 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
1919 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
1920 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1921 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
1922 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
1923 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1924 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1925 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
1926 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
1927 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1928 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1929 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1930 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1931 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
1932 };