blob: a4473c8c6951066f5dc4f7a942520f73e824149c [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
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 Root56088a52011-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 Hackborn4aa14b92012-04-13 19:18:41 -07001195 printf("uses-implied-permission:'android.permission.WRITE_EXTERNAL_STORAGE'," \
1196 "'targetSdkVersion < 4'\n");
Dianne Hackborn79245122012-03-12 10:51:26 -07001197 hasWriteExternalStoragePermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001198 }
1199 if (!hasReadPhoneStatePermission) {
1200 printf("uses-permission:'android.permission.READ_PHONE_STATE'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001201 printf("uses-implied-permission:'android.permission.READ_PHONE_STATE'," \
1202 "'targetSdkVersion < 4'\n");
Kenny Root063a44e2011-12-08 08:46:03 -08001203 }
1204 }
1205
Dianne Hackborn79245122012-03-12 10:51:26 -07001206 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1207 // force them to always take READ_EXTERNAL_STORAGE as well.
1208 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
1209 printf("uses-permission:'android.permission.READ_EXTERNAL_STORAGE'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001210 printf("uses-implied-permission:'android.permission.READ_EXTERNAL_STORAGE'," \
1211 "'requested WRITE_EXTERNAL_STORAGE'\n");
Dianne Hackborn79245122012-03-12 10:51:26 -07001212 }
1213
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001214 // Pre-JellyBean call log permission compatibility.
1215 if (targetSdk < 16) {
1216 if (!hasReadCallLogPermission && hasReadContactsPermission) {
1217 printf("uses-permission:'android.permission.READ_CALL_LOG'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001218 printf("uses-implied-permission:'android.permission.READ_CALL_LOG'," \
1219 "'targetSdkVersion < 16 and requested READ_CONTACTS'\n");
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001220 }
1221 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
1222 printf("uses-permission:'android.permission.WRITE_CALL_LOG'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001223 printf("uses-implied-permission:'android.permission.WRITE_CALL_LOG'," \
1224 "'targetSdkVersion < 16 and requested WRITE_CONTACTS'\n");
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001225 }
1226 }
1227
Dan Morrill89d97c12010-05-03 16:13:14 -07001228 /* The following blocks handle printing "inferred" uses-features, based
1229 * on whether related features or permissions are used by the app.
1230 * Note that the various spec*Feature variables denote whether the
1231 * relevant tag was *present* in the AndroidManfest, not that it was
1232 * present and set to true.
1233 */
1234 // Camera-related back-compatibility logic
1235 if (!specCameraFeature) {
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001236 if (reqCameraFlashFeature) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001237 // if app requested a sub-feature (autofocus or flash) and didn't
1238 // request the base camera feature, we infer that it meant to
1239 printf("uses-feature:'android.hardware.camera'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001240 printf("uses-implied-feature:'android.hardware.camera'," \
1241 "'requested android.hardware.camera.flash feature'\n");
1242 } else if (reqCameraAutofocusFeature) {
1243 // if app requested a sub-feature (autofocus or flash) and didn't
1244 // request the base camera feature, we infer that it meant to
1245 printf("uses-feature:'android.hardware.camera'\n");
1246 printf("uses-implied-feature:'android.hardware.camera'," \
1247 "'requested android.hardware.camera.autofocus feature'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001248 } else if (hasCameraPermission) {
1249 // if app wants to use camera but didn't request the feature, we infer
1250 // that it meant to, and further that it wants autofocus
1251 // (which was the 1.0 - 1.5 behavior)
1252 printf("uses-feature:'android.hardware.camera'\n");
1253 if (!specCameraAutofocusFeature) {
1254 printf("uses-feature:'android.hardware.camera.autofocus'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001255 printf("uses-implied-feature:'android.hardware.camera.autofocus'," \
1256 "'requested android.permission.CAMERA permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001257 }
1258 }
Dianne Hackborne5276a72009-08-27 16:28:44 -07001259 }
Doug Zongkerdbe7a682009-10-09 11:24:51 -07001260
Dan Morrill89d97c12010-05-03 16:13:14 -07001261 // Location-related back-compatibility logic
1262 if (!specLocationFeature &&
1263 (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission ||
1264 hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) {
1265 // if app either takes a location-related permission or requests one of the
1266 // sub-features, we infer that it also meant to request the base location feature
1267 printf("uses-feature:'android.hardware.location'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001268 printf("uses-implied-feature:'android.hardware.location'," \
1269 "'requested a location access permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001270 }
Dianne Hackbornef05e072010-03-01 17:43:39 -08001271 if (!specGpsFeature && hasGpsPermission) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001272 // if app takes GPS (FINE location) perm but does not request the GPS
1273 // feature, we infer that it meant to
Dianne Hackbornef05e072010-03-01 17:43:39 -08001274 printf("uses-feature:'android.hardware.location.gps'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001275 printf("uses-implied-feature:'android.hardware.location.gps'," \
1276 "'requested android.permission.ACCESS_FINE_LOCATION permission'\n");
Dianne Hackbornef05e072010-03-01 17:43:39 -08001277 }
Dan Morrill89d97c12010-05-03 16:13:14 -07001278 if (!specNetworkLocFeature && hasCoarseLocPermission) {
1279 // if app takes Network location (COARSE location) perm but does not request the
1280 // network location feature, we infer that it meant to
1281 printf("uses-feature:'android.hardware.location.network'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001282 printf("uses-implied-feature:'android.hardware.location.network'," \
1283 "'requested android.permission.ACCESS_COURSE_LOCATION permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001284 }
1285
1286 // Bluetooth-related compatibility logic
Dan Morrill6b22d812010-06-15 21:41:42 -07001287 if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001288 // if app takes a Bluetooth permission but does not request the Bluetooth
1289 // feature, we infer that it meant to
1290 printf("uses-feature:'android.hardware.bluetooth'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001291 printf("uses-implied-feature:'android.hardware.bluetooth'," \
1292 "'requested android.permission.BLUETOOTH or android.permission.BLUETOOTH_ADMIN " \
1293 "permission and targetSdkVersion > 4'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001294 }
1295
1296 // Microphone-related compatibility logic
1297 if (!specMicrophoneFeature && hasRecordAudioPermission) {
1298 // if app takes the record-audio permission but does not request the microphone
1299 // feature, we infer that it meant to
1300 printf("uses-feature:'android.hardware.microphone'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001301 printf("uses-implied-feature:'android.hardware.microphone'," \
1302 "'requested android.permission.RECORD_AUDIO permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001303 }
1304
1305 // WiFi-related compatibility logic
1306 if (!specWiFiFeature && hasWiFiPermission) {
1307 // if app takes one of the WiFi permissions but does not request the WiFi
1308 // feature, we infer that it meant to
1309 printf("uses-feature:'android.hardware.wifi'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001310 printf("uses-implied-feature:'android.hardware.wifi'," \
1311 "'requested android.permission.ACCESS_WIFI_STATE, " \
1312 "android.permission.CHANGE_WIFI_STATE, or " \
1313 "android.permission.CHANGE_WIFI_MULTICAST_STATE permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001314 }
1315
1316 // Telephony-related compatibility logic
1317 if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) {
1318 // if app takes one of the telephony permissions or requests a sub-feature but
1319 // does not request the base telephony feature, we infer that it meant to
1320 printf("uses-feature:'android.hardware.telephony'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001321 printf("uses-implied-feature:'android.hardware.telephony'," \
1322 "'requested a telephony-related permission or feature'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001323 }
1324
1325 // Touchscreen-related back-compatibility logic
1326 if (!specTouchscreenFeature) { // not a typo!
1327 // all apps are presumed to require a touchscreen, unless they explicitly say
1328 // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
1329 // Note that specTouchscreenFeature is true if the tag is present, regardless
1330 // of whether its value is true or false, so this is safe
1331 printf("uses-feature:'android.hardware.touchscreen'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001332 printf("uses-implied-feature:'android.hardware.touchscreen'," \
1333 "'assumed you require a touch screen unless explicitly made optional'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001334 }
1335 if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
1336 // if app takes one of the telephony permissions or requests a sub-feature but
1337 // does not request the base telephony feature, we infer that it meant to
1338 printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001339 printf("uses-implied-feature:'android.hardware.touchscreen.multitouch'," \
1340 "'requested android.hardware.touchscreen.multitouch.distinct feature'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001341 }
Dianne Hackbornef05e072010-03-01 17:43:39 -08001342
Dianne Hackborne289bff2011-06-13 19:33:22 -07001343 // Landscape/portrait-related compatibility logic
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001344 if (!specScreenLandscapeFeature && !specScreenPortraitFeature) {
1345 // If the app has specified any activities in its manifest
1346 // that request a specific orientation, then assume that
1347 // orientation is required.
1348 if (reqScreenLandscapeFeature) {
1349 printf("uses-feature:'android.hardware.screen.landscape'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001350 printf("uses-implied-feature:'android.hardware.screen.landscape'," \
1351 "'one or more activities have specified a landscape orientation'\n");
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001352 }
1353 if (reqScreenPortraitFeature) {
1354 printf("uses-feature:'android.hardware.screen.portrait'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001355 printf("uses-implied-feature:'android.hardware.screen.portrait'," \
1356 "'one or more activities have specified a portrait orientation'\n");
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001357 }
Dianne Hackborne289bff2011-06-13 19:33:22 -07001358 }
1359
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001360 if (hasMainActivity) {
1361 printf("main\n");
1362 }
1363 if (hasWidgetReceivers) {
1364 printf("app-widget\n");
1365 }
1366 if (hasImeService) {
1367 printf("ime\n");
1368 }
1369 if (hasWallpaperService) {
1370 printf("wallpaper\n");
1371 }
1372 if (hasOtherActivities) {
1373 printf("other-activities\n");
1374 }
1375 if (isSearchable) {
1376 printf("search\n");
1377 }
1378 if (hasOtherReceivers) {
1379 printf("other-receivers\n");
1380 }
1381 if (hasOtherServices) {
1382 printf("other-services\n");
1383 }
1384
Dianne Hackborne289bff2011-06-13 19:33:22 -07001385 // For modern apps, if screen size buckets haven't been specified
1386 // but the new width ranges have, then infer the buckets from them.
1387 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1388 && requiresSmallestWidthDp > 0) {
1389 int compatWidth = compatibleWidthLimitDp;
1390 if (compatWidth <= 0) compatWidth = requiresSmallestWidthDp;
1391 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1392 smallScreen = -1;
1393 } else {
1394 smallScreen = 0;
1395 }
1396 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1397 normalScreen = -1;
1398 } else {
1399 normalScreen = 0;
1400 }
1401 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1402 largeScreen = -1;
1403 } else {
1404 largeScreen = 0;
1405 }
1406 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1407 xlargeScreen = -1;
1408 } else {
1409 xlargeScreen = 0;
1410 }
1411 }
1412
Dianne Hackborn723738c2009-06-25 19:48:04 -07001413 // Determine default values for any unspecified screen sizes,
1414 // based on the target SDK of the package. As of 4 (donut)
1415 // the screen size support was introduced, so all default to
1416 // enabled.
1417 if (smallScreen > 0) {
1418 smallScreen = targetSdk >= 4 ? -1 : 0;
1419 }
1420 if (normalScreen > 0) {
1421 normalScreen = -1;
1422 }
1423 if (largeScreen > 0) {
1424 largeScreen = targetSdk >= 4 ? -1 : 0;
1425 }
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001426 if (xlargeScreen > 0) {
Scott Maind58fb972010-11-04 18:32:00 -07001427 // Introduced in Gingerbread.
1428 xlargeScreen = targetSdk >= 9 ? -1 : 0;
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001429 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001430 if (anyDensity > 0) {
Dianne Hackborne289bff2011-06-13 19:33:22 -07001431 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1432 || compatibleWidthLimitDp > 0) ? -1 : 0;
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001433 }
Dianne Hackborn723738c2009-06-25 19:48:04 -07001434 printf("supports-screens:");
1435 if (smallScreen != 0) printf(" 'small'");
1436 if (normalScreen != 0) printf(" 'normal'");
1437 if (largeScreen != 0) printf(" 'large'");
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001438 if (xlargeScreen != 0) printf(" 'xlarge'");
Dianne Hackborn723738c2009-06-25 19:48:04 -07001439 printf("\n");
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001440 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
Dianne Hackborne289bff2011-06-13 19:33:22 -07001441 if (requiresSmallestWidthDp > 0) {
1442 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1443 }
1444 if (compatibleWidthLimitDp > 0) {
1445 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1446 }
1447 if (largestWidthLimitDp > 0) {
1448 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1449 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001450
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001451 printf("locales:");
Dianne Hackborne17086b2009-06-19 15:13:28 -07001452 const size_t NL = locales.size();
1453 for (size_t i=0; i<NL; i++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001454 const char* localeStr = locales[i].string();
1455 if (localeStr == NULL || strlen(localeStr) == 0) {
1456 localeStr = "--_--";
1457 }
1458 printf(" '%s'", localeStr);
1459 }
1460 printf("\n");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001461
Dianne Hackborne17086b2009-06-19 15:13:28 -07001462 printf("densities:");
1463 const size_t ND = densities.size();
1464 for (size_t i=0; i<ND; i++) {
1465 printf(" '%d'", densities[i]);
1466 }
1467 printf("\n");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001468
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001469 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1470 if (dir != NULL) {
1471 if (dir->getFileCount() > 0) {
1472 printf("native-code:");
1473 for (size_t i=0; i<dir->getFileCount(); i++) {
1474 printf(" '%s'", dir->getFileName(i).string());
1475 }
1476 printf("\n");
1477 }
1478 delete dir;
1479 }
Dan Morrille74763e2012-01-06 10:47:10 -08001480 } else if (strcmp("badger", option) == 0) {
Dianne Hackborn6c997a92012-01-31 11:27:43 -08001481 printf("%s", CONSOLE_DATA);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001482 } else if (strcmp("configurations", option) == 0) {
1483 Vector<ResTable_config> configs;
1484 res.getConfigurations(&configs);
1485 const size_t N = configs.size();
1486 for (size_t i=0; i<N; i++) {
1487 printf("%s\n", configs[i].toString().string());
1488 }
1489 } else {
1490 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
1491 goto bail;
1492 }
1493 }
1494
1495 result = NO_ERROR;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001496
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001497bail:
1498 if (asset) {
1499 delete asset;
1500 }
1501 return (result != NO_ERROR);
1502}
1503
1504
1505/*
1506 * Handle the "add" command, which wants to add files to a new or
1507 * pre-existing archive.
1508 */
1509int doAdd(Bundle* bundle)
1510{
1511 ZipFile* zip = NULL;
1512 status_t result = UNKNOWN_ERROR;
1513 const char* zipFileName;
1514
1515 if (bundle->getUpdate()) {
1516 /* avoid confusion */
1517 fprintf(stderr, "ERROR: can't use '-u' with add\n");
1518 goto bail;
1519 }
1520
1521 if (bundle->getFileSpecCount() < 1) {
1522 fprintf(stderr, "ERROR: must specify zip file name\n");
1523 goto bail;
1524 }
1525 zipFileName = bundle->getFileSpecEntry(0);
1526
1527 if (bundle->getFileSpecCount() < 2) {
1528 fprintf(stderr, "NOTE: nothing to do\n");
1529 goto bail;
1530 }
1531
1532 zip = openReadWrite(zipFileName, true);
1533 if (zip == NULL) {
1534 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
1535 goto bail;
1536 }
1537
1538 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1539 const char* fileName = bundle->getFileSpecEntry(i);
1540
1541 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
1542 printf(" '%s'... (from gzip)\n", fileName);
1543 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
1544 } else {
Doug Zongkerdbe7a682009-10-09 11:24:51 -07001545 if (bundle->getJunkPath()) {
1546 String8 storageName = String8(fileName).getPathLeaf();
1547 printf(" '%s' as '%s'...\n", fileName, storageName.string());
1548 result = zip->add(fileName, storageName.string(),
1549 bundle->getCompressionMethod(), NULL);
1550 } else {
1551 printf(" '%s'...\n", fileName);
1552 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
1553 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001554 }
1555 if (result != NO_ERROR) {
1556 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
1557 if (result == NAME_NOT_FOUND)
1558 fprintf(stderr, ": file not found\n");
1559 else if (result == ALREADY_EXISTS)
1560 fprintf(stderr, ": already exists in archive\n");
1561 else
1562 fprintf(stderr, "\n");
1563 goto bail;
1564 }
1565 }
1566
1567 result = NO_ERROR;
1568
1569bail:
1570 delete zip;
1571 return (result != NO_ERROR);
1572}
1573
1574
1575/*
1576 * Delete files from an existing archive.
1577 */
1578int doRemove(Bundle* bundle)
1579{
1580 ZipFile* zip = NULL;
1581 status_t result = UNKNOWN_ERROR;
1582 const char* zipFileName;
1583
1584 if (bundle->getFileSpecCount() < 1) {
1585 fprintf(stderr, "ERROR: must specify zip file name\n");
1586 goto bail;
1587 }
1588 zipFileName = bundle->getFileSpecEntry(0);
1589
1590 if (bundle->getFileSpecCount() < 2) {
1591 fprintf(stderr, "NOTE: nothing to do\n");
1592 goto bail;
1593 }
1594
1595 zip = openReadWrite(zipFileName, false);
1596 if (zip == NULL) {
1597 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
1598 zipFileName);
1599 goto bail;
1600 }
1601
1602 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1603 const char* fileName = bundle->getFileSpecEntry(i);
1604 ZipEntry* entry;
1605
1606 entry = zip->getEntryByName(fileName);
1607 if (entry == NULL) {
1608 printf(" '%s' NOT FOUND\n", fileName);
1609 continue;
1610 }
1611
1612 result = zip->remove(entry);
1613
1614 if (result != NO_ERROR) {
1615 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
1616 bundle->getFileSpecEntry(i), zipFileName);
1617 goto bail;
1618 }
1619 }
1620
1621 /* update the archive */
1622 zip->flush();
1623
1624bail:
1625 delete zip;
1626 return (result != NO_ERROR);
1627}
1628
1629
1630/*
1631 * Package up an asset directory and associated application files.
1632 */
1633int doPackage(Bundle* bundle)
1634{
1635 const char* outputAPKFile;
1636 int retVal = 1;
1637 status_t err;
1638 sp<AaptAssets> assets;
1639 int N;
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001640 FILE* fp;
1641 String8 dependencyFile;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001642
1643 // -c zz_ZZ means do pseudolocalization
1644 ResourceFilter filter;
1645 err = filter.parse(bundle->getConfigurations());
1646 if (err != NO_ERROR) {
1647 goto bail;
1648 }
1649 if (filter.containsPseudo()) {
1650 bundle->setPseudolocalize(true);
1651 }
1652
1653 N = bundle->getFileSpecCount();
1654 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
1655 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDir() == NULL) {
1656 fprintf(stderr, "ERROR: no input files\n");
1657 goto bail;
1658 }
1659
1660 outputAPKFile = bundle->getOutputAPKFile();
1661
1662 // Make sure the filenames provided exist and are of the appropriate type.
1663 if (outputAPKFile) {
1664 FileType type;
1665 type = getFileType(outputAPKFile);
1666 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
1667 fprintf(stderr,
1668 "ERROR: output file '%s' exists but is not regular file\n",
1669 outputAPKFile);
1670 goto bail;
1671 }
1672 }
1673
1674 // Load the assets.
1675 assets = new AaptAssets();
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001676
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001677 // Set up the resource gathering in assets if we're going to generate
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001678 // dependency files. Every time we encounter a resource while slurping
1679 // the tree, we'll add it to these stores so we have full resource paths
1680 // to write to a dependency file.
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001681 if (bundle->getGenDependencies()) {
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001682 sp<FilePathStore> resPathStore = new FilePathStore;
1683 assets->setFullResPaths(resPathStore);
1684 sp<FilePathStore> assetPathStore = new FilePathStore;
1685 assets->setFullAssetPaths(assetPathStore);
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001686 }
1687
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001688 err = assets->slurpFromArgs(bundle);
1689 if (err < 0) {
1690 goto bail;
1691 }
1692
1693 if (bundle->getVerbose()) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001694 assets->print(String8());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001695 }
1696
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001697 // If they asked for any fileAs that need to be compiled, do so.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001698 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
1699 err = buildResources(bundle, assets);
1700 if (err != 0) {
1701 goto bail;
1702 }
1703 }
1704
1705 // At this point we've read everything and processed everything. From here
1706 // on out it's just writing output files.
1707 if (SourcePos::hasErrors()) {
1708 goto bail;
1709 }
1710
Dianne Hackborn1644c6d2012-02-06 15:33:21 -08001711 // Update symbols with information about which ones are needed as Java symbols.
1712 assets->applyJavaSymbols();
1713 if (SourcePos::hasErrors()) {
1714 goto bail;
1715 }
1716
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001717 // If we've been asked to generate a dependency file, do that here
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001718 if (bundle->getGenDependencies()) {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001719 // If this is the packaging step, generate the dependency file next to
1720 // the output apk (e.g. bin/resources.ap_.d)
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001721 if (outputAPKFile) {
1722 dependencyFile = String8(outputAPKFile);
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001723 // Add the .d extension to the dependency file.
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001724 dependencyFile.append(".d");
1725 } else {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001726 // Else if this is the R.java dependency generation step,
1727 // generate the dependency file in the R.java package subdirectory
1728 // e.g. gen/com/foo/app/R.java.d
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001729 dependencyFile = String8(bundle->getRClassDir());
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001730 dependencyFile.appendPath("R.java.d");
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001731 }
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001732 // Make sure we have a clean dependency file to start with
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001733 fp = fopen(dependencyFile, "w");
1734 fclose(fp);
1735 }
1736
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001737 // Write out R.java constants
Dianne Hackborn1644c6d2012-02-06 15:33:21 -08001738 if (!assets->havePrivateSymbols()) {
Xavier Ducrohet63459ad2009-11-30 18:05:10 -08001739 if (bundle->getCustomPackage() == NULL) {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001740 // Write the R.java file into the appropriate class directory
1741 // e.g. gen/com/foo/app/R.java
Xavier Ducrohet63459ad2009-11-30 18:05:10 -08001742 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001743 // If we have library files, we're going to write our R.java file into
1744 // the appropriate class directory for those libraries as well.
1745 // e.g. gen/com/foo/app/lib/R.java
Josiah Gaskince89f152011-06-08 19:31:40 -07001746 if (bundle->getExtraPackages() != NULL) {
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001747 // Split on colon
Josiah Gaskince89f152011-06-08 19:31:40 -07001748 String8 libs(bundle->getExtraPackages());
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001749 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
Josiah Gaskince89f152011-06-08 19:31:40 -07001750 while (packageString != NULL) {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001751 // Write the R.java file out with the correct package name
Josiah Gaskince89f152011-06-08 19:31:40 -07001752 err = writeResourceSymbols(bundle, assets, String8(packageString), true);
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001753 packageString = strtok(NULL, ":");
Josiah Gaskince89f152011-06-08 19:31:40 -07001754 }
1755 libs.unlockBuffer();
1756 }
Xavier Ducrohet63459ad2009-11-30 18:05:10 -08001757 } else {
1758 const String8 customPkg(bundle->getCustomPackage());
1759 err = writeResourceSymbols(bundle, assets, customPkg, true);
1760 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001761 if (err < 0) {
1762 goto bail;
1763 }
1764 } else {
1765 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
1766 if (err < 0) {
1767 goto bail;
1768 }
1769 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
1770 if (err < 0) {
1771 goto bail;
1772 }
1773 }
1774
Joe Onorato1553c822009-08-30 13:36:22 -07001775 // Write out the ProGuard file
1776 err = writeProguardFile(bundle, assets);
1777 if (err < 0) {
1778 goto bail;
1779 }
1780
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001781 // Write the apk
1782 if (outputAPKFile) {
1783 err = writeAPK(bundle, assets, String8(outputAPKFile));
1784 if (err != NO_ERROR) {
1785 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
1786 goto bail;
1787 }
1788 }
1789
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001790 // If we've been asked to generate a dependency file, we need to finish up here.
1791 // the writeResourceSymbols and writeAPK functions have already written the target
1792 // half of the dependency file, now we need to write the prerequisites. (files that
1793 // the R.java file or .ap_ file depend on)
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001794 if (bundle->getGenDependencies()) {
1795 // Now that writeResourceSymbols or writeAPK has taken care of writing
1796 // the targets to our dependency file, we'll write the prereqs
1797 fp = fopen(dependencyFile, "a+");
1798 fprintf(fp, " : ");
1799 bool includeRaw = (outputAPKFile != NULL);
1800 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001801 // Also manually add the AndroidManifeset since it's not under res/ or assets/
1802 // and therefore was not added to our pathstores during slurping
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001803 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
1804 fclose(fp);
1805 }
1806
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001807 retVal = 0;
1808bail:
1809 if (SourcePos::hasErrors()) {
1810 SourcePos::printErrors(stderr);
1811 }
1812 return retVal;
1813}
Josiah Gaskin8a39da82011-06-06 17:00:35 -07001814
1815/*
1816 * Do PNG Crunching
1817 * PRECONDITIONS
1818 * -S flag points to a source directory containing drawable* folders
1819 * -C flag points to destination directory. The folder structure in the
1820 * source directory will be mirrored to the destination (cache) directory
1821 *
1822 * POSTCONDITIONS
1823 * Destination directory will be updated to match the PNG files in
1824 * the source directory.
1825 */
1826int doCrunch(Bundle* bundle)
1827{
1828 fprintf(stdout, "Crunching PNG Files in ");
1829 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
1830 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
1831
1832 updatePreProcessedCache(bundle);
1833
1834 return NO_ERROR;
1835}
Dan Morrille74763e2012-01-06 10:47:10 -08001836
1837char CONSOLE_DATA[2925] = {
1838 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1839 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1840 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1841 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1842 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
1843 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
1844 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1845 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1846 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
1847 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
1848 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1849 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1850 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
1851 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1852 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1853 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
1854 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
1855 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1856 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1857 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
1858 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
1859 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
1860 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
1861 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
1862 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1863 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1864 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
1865 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
1866 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
1867 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1868 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
1869 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1870 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1871 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
1872 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
1873 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1874 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1875 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
1876 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
1877 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
1878 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1879 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
1880 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1881 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1882 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
1883 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
1884 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
1885 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
1886 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
1887 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1888 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
1889 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
1890 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
1891 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1892 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
1893 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
1894 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
1895 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
1896 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
1897 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
1898 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
1899 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
1900 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
1901 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
1902 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
1903 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
1904 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
1905 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
1906 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
1907 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
1908 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
1909 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
1910 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1911 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
1912 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
1913 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
1914 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1915 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
1916 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
1917 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1918 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
1919 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
1920 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
1921 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1922 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
1923 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
1924 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1925 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
1926 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
1927 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1928 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1929 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
1930 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
1931 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1932 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1933 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
1934 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1935 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
1936 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
1937 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
1938 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1939 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1940 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
1941 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
1942 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
1943 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1944 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
1945 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1946 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1947 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
1948 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
1949 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1950 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1951 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
1952 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1953 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
1954 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
1955 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
1956 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1957 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1958 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
1959 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
1960 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
1961 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
1962 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
1963 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
1964 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
1965 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
1966 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
1967 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1968 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1969 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
1970 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
1971 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
1972 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
1973 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
1974 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1975 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1976 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
1977 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
1978 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
1979 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1980 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
1981 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1982 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1983 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
1984 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
1985 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1986 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1987 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
1988 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
1989 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1990 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
1991 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
1992 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1993 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1994 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
1995 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
1996 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1997 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1998 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1999 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2000 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2001 };