blob: b98925bd184cc0415735af4be1c27385a4dc0869 [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"
Xavier Ducrohet22d7cf22012-12-21 09:54:02 -080010#include "Images.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080011#include "XMLNode.h"
12
Mathias Agopian3b4062e2009-05-31 19:13:00 -070013#include <utils/Log.h>
14#include <utils/threads.h>
15#include <utils/List.h>
16#include <utils/Errors.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080017
18#include <fcntl.h>
19#include <errno.h>
20
21using namespace android;
22
23/*
24 * Show version info. All the cool kids do it.
25 */
26int doVersion(Bundle* bundle)
27{
28 if (bundle->getFileSpecCount() != 0)
29 printf("(ignoring extra arguments)\n");
30 printf("Android Asset Packaging Tool, v0.2\n");
31
32 return 0;
33}
34
35
36/*
37 * Open the file read only. The call fails if the file doesn't exist.
38 *
39 * Returns NULL on failure.
40 */
41ZipFile* openReadOnly(const char* fileName)
42{
43 ZipFile* zip;
44 status_t result;
45
46 zip = new ZipFile;
47 result = zip->open(fileName, ZipFile::kOpenReadOnly);
48 if (result != NO_ERROR) {
49 if (result == NAME_NOT_FOUND)
50 fprintf(stderr, "ERROR: '%s' not found\n", fileName);
51 else if (result == PERMISSION_DENIED)
52 fprintf(stderr, "ERROR: '%s' access denied\n", fileName);
53 else
54 fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n",
55 fileName);
56 delete zip;
57 return NULL;
58 }
59
60 return zip;
61}
62
63/*
64 * Open the file read-write. The file will be created if it doesn't
65 * already exist and "okayToCreate" is set.
66 *
67 * Returns NULL on failure.
68 */
69ZipFile* openReadWrite(const char* fileName, bool okayToCreate)
70{
71 ZipFile* zip = NULL;
72 status_t result;
73 int flags;
74
75 flags = ZipFile::kOpenReadWrite;
76 if (okayToCreate)
77 flags |= ZipFile::kOpenCreate;
78
79 zip = new ZipFile;
80 result = zip->open(fileName, flags);
81 if (result != NO_ERROR) {
82 delete zip;
83 zip = NULL;
84 goto bail;
85 }
86
87bail:
88 return zip;
89}
90
91
92/*
93 * Return a short string describing the compression method.
94 */
95const char* compressionName(int method)
96{
97 if (method == ZipEntry::kCompressStored)
98 return "Stored";
99 else if (method == ZipEntry::kCompressDeflated)
100 return "Deflated";
101 else
102 return "Unknown";
103}
104
105/*
106 * Return the percent reduction in size (0% == no compression).
107 */
108int calcPercent(long uncompressedLen, long compressedLen)
109{
110 if (!uncompressedLen)
111 return 0;
112 else
113 return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5);
114}
115
116/*
117 * Handle the "list" command, which can be a simple file dump or
118 * a verbose listing.
119 *
120 * The verbose listing closely matches the output of the Info-ZIP "unzip"
121 * command.
122 */
123int doList(Bundle* bundle)
124{
125 int result = 1;
126 ZipFile* zip = NULL;
127 const ZipEntry* entry;
128 long totalUncLen, totalCompLen;
129 const char* zipFileName;
130
131 if (bundle->getFileSpecCount() != 1) {
132 fprintf(stderr, "ERROR: specify zip file name (only)\n");
133 goto bail;
134 }
135 zipFileName = bundle->getFileSpecEntry(0);
136
137 zip = openReadOnly(zipFileName);
138 if (zip == NULL)
139 goto bail;
140
141 int count, i;
142
143 if (bundle->getVerbose()) {
144 printf("Archive: %s\n", zipFileName);
145 printf(
Kenny Rootfb2a9462010-08-25 07:36:31 -0700146 " Length Method Size Ratio Offset Date Time CRC-32 Name\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800147 printf(
Kenny Rootfb2a9462010-08-25 07:36:31 -0700148 "-------- ------ ------- ----- ------- ---- ---- ------ ----\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800149 }
150
151 totalUncLen = totalCompLen = 0;
152
153 count = zip->getNumEntries();
154 for (i = 0; i < count; i++) {
155 entry = zip->getEntryByIndex(i);
156 if (bundle->getVerbose()) {
157 char dateBuf[32];
158 time_t when;
159
160 when = entry->getModWhen();
161 strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M",
162 localtime(&when));
163
Kenny Rootfb2a9462010-08-25 07:36:31 -0700164 printf("%8ld %-7.7s %7ld %3d%% %8zd %s %08lx %s\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800165 (long) entry->getUncompressedLen(),
166 compressionName(entry->getCompressionMethod()),
167 (long) entry->getCompressedLen(),
168 calcPercent(entry->getUncompressedLen(),
169 entry->getCompressedLen()),
Kenny Rootfb2a9462010-08-25 07:36:31 -0700170 (size_t) entry->getLFHOffset(),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800171 dateBuf,
172 entry->getCRC32(),
173 entry->getFileName());
174 } else {
175 printf("%s\n", entry->getFileName());
176 }
177
178 totalUncLen += entry->getUncompressedLen();
179 totalCompLen += entry->getCompressedLen();
180 }
181
182 if (bundle->getVerbose()) {
183 printf(
184 "-------- ------- --- -------\n");
185 printf("%8ld %7ld %2d%% %d files\n",
186 totalUncLen,
187 totalCompLen,
188 calcPercent(totalUncLen, totalCompLen),
189 zip->getNumEntries());
190 }
191
192 if (bundle->getAndroidList()) {
193 AssetManager assets;
194 if (!assets.addAssetPath(String8(zipFileName), NULL)) {
195 fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n");
196 goto bail;
197 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700198
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800199 const ResTable& res = assets.getResources(false);
200 if (&res == NULL) {
201 printf("\nNo resource table found.\n");
202 } else {
Steve Blockf1ff21a2010-06-14 17:34:04 +0100203#ifndef HAVE_ANDROID_OS
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204 printf("\nResource table:\n");
Dianne Hackborne17086b2009-06-19 15:13:28 -0700205 res.print(false);
Steve Blockf1ff21a2010-06-14 17:34:04 +0100206#endif
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800207 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700208
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800209 Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
210 Asset::ACCESS_BUFFER);
211 if (manifestAsset == NULL) {
212 printf("\nNo AndroidManifest.xml found.\n");
213 } else {
214 printf("\nAndroid manifest:\n");
215 ResXMLTree tree;
216 tree.setTo(manifestAsset->getBuffer(true),
217 manifestAsset->getLength());
218 printXMLBlock(&tree);
219 }
220 delete manifestAsset;
221 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700222
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800223 result = 0;
224
225bail:
226 delete zip;
227 return result;
228}
229
230static ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes)
231{
232 size_t N = tree.getAttributeCount();
233 for (size_t i=0; i<N; i++) {
234 if (tree.getAttributeNameResID(i) == attrRes) {
235 return (ssize_t)i;
236 }
237 }
238 return -1;
239}
240
Joe Onorato1553c822009-08-30 13:36:22 -0700241String8 getAttribute(const ResXMLTree& tree, const char* ns,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800242 const char* attr, String8* outError)
243{
244 ssize_t idx = tree.indexOfAttribute(ns, attr);
245 if (idx < 0) {
246 return String8();
247 }
248 Res_value value;
249 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
250 if (value.dataType != Res_value::TYPE_STRING) {
251 if (outError != NULL) *outError = "attribute is not a string value";
252 return String8();
253 }
254 }
255 size_t len;
256 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
257 return str ? String8(str, len) : String8();
258}
259
260static String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError)
261{
262 ssize_t idx = indexOfAttribute(tree, attrRes);
263 if (idx < 0) {
264 return String8();
265 }
266 Res_value value;
267 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
268 if (value.dataType != Res_value::TYPE_STRING) {
269 if (outError != NULL) *outError = "attribute is not a string value";
270 return String8();
271 }
272 }
273 size_t len;
274 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
275 return str ? String8(str, len) : String8();
276}
277
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700278static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes,
279 String8* outError, int32_t defValue = -1)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800280{
281 ssize_t idx = indexOfAttribute(tree, attrRes);
282 if (idx < 0) {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700283 return defValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800284 }
285 Res_value value;
286 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700287 if (value.dataType < Res_value::TYPE_FIRST_INT
288 || value.dataType > Res_value::TYPE_LAST_INT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800289 if (outError != NULL) *outError = "attribute is not an integer value";
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700290 return defValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800291 }
292 }
293 return value.data;
294}
295
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -0700296static int32_t getResolvedIntegerAttribute(const ResTable* resTable, const ResXMLTree& tree,
297 uint32_t attrRes, String8* outError, int32_t defValue = -1)
298{
299 ssize_t idx = indexOfAttribute(tree, attrRes);
300 if (idx < 0) {
301 return defValue;
302 }
303 Res_value value;
304 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
305 if (value.dataType == Res_value::TYPE_REFERENCE) {
306 resTable->resolveReference(&value, 0);
307 }
308 if (value.dataType < Res_value::TYPE_FIRST_INT
309 || value.dataType > Res_value::TYPE_LAST_INT) {
310 if (outError != NULL) *outError = "attribute is not an integer value";
311 return defValue;
312 }
313 }
314 return value.data;
315}
316
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800317static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree,
318 uint32_t attrRes, String8* outError)
319{
320 ssize_t idx = indexOfAttribute(tree, attrRes);
321 if (idx < 0) {
322 return String8();
323 }
324 Res_value value;
325 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
326 if (value.dataType == Res_value::TYPE_STRING) {
327 size_t len;
328 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
329 return str ? String8(str, len) : String8();
330 }
331 resTable->resolveReference(&value, 0);
332 if (value.dataType != Res_value::TYPE_STRING) {
333 if (outError != NULL) *outError = "attribute is not a string value";
334 return String8();
335 }
336 }
337 size_t len;
338 const Res_value* value2 = &value;
339 const char16_t* str = const_cast<ResTable*>(resTable)->valueToString(value2, 0, NULL, &len);
340 return str ? String8(str, len) : String8();
341}
342
343// These are attribute resource constants for the platform, as found
344// in android.R.attr
345enum {
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -0700346 LABEL_ATTR = 0x01010001,
347 ICON_ATTR = 0x01010002,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800348 NAME_ATTR = 0x01010003,
Dan Morrillb6ec11e2012-04-03 12:44:40 -0700349 DEBUGGABLE_ATTR = 0x0101000f,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800350 VERSION_CODE_ATTR = 0x0101021b,
351 VERSION_NAME_ATTR = 0x0101021c,
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -0700352 SCREEN_ORIENTATION_ATTR = 0x0101001e,
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700353 MIN_SDK_VERSION_ATTR = 0x0101020c,
Suchi Amalapurapu75c49842009-08-14 15:13:09 -0700354 MAX_SDK_VERSION_ATTR = 0x01010271,
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700355 REQ_TOUCH_SCREEN_ATTR = 0x01010227,
356 REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
357 REQ_HARD_KEYBOARD_ATTR = 0x01010229,
358 REQ_NAVIGATION_ATTR = 0x0101022a,
359 REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
360 TARGET_SDK_VERSION_ATTR = 0x01010270,
361 TEST_ONLY_ATTR = 0x01010272,
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700362 ANY_DENSITY_ATTR = 0x0101026c,
Dianne Hackborne5276a72009-08-27 16:28:44 -0700363 GL_ES_VERSION_ATTR = 0x01010281,
Dianne Hackborn723738c2009-06-25 19:48:04 -0700364 SMALL_SCREEN_ATTR = 0x01010284,
365 NORMAL_SCREEN_ATTR = 0x01010285,
366 LARGE_SCREEN_ATTR = 0x01010286,
Dianne Hackbornf43489d2010-08-20 12:44:33 -0700367 XLARGE_SCREEN_ATTR = 0x010102bf,
Dianne Hackborne5276a72009-08-27 16:28:44 -0700368 REQUIRED_ATTR = 0x0101028e,
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700369 SCREEN_SIZE_ATTR = 0x010102ca,
370 SCREEN_DENSITY_ATTR = 0x010102cb,
Dianne Hackborne289bff2011-06-13 19:33:22 -0700371 REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
372 COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
373 LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
Kenny Root56088a52011-09-29 13:49:45 -0700374 PUBLIC_KEY_ATTR = 0x010103a6,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800375};
376
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700377const char *getComponentName(String8 &pkgName, String8 &componentName) {
378 ssize_t idx = componentName.find(".");
379 String8 retStr(pkgName);
380 if (idx == 0) {
381 retStr += componentName;
382 } else if (idx < 0) {
383 retStr += ".";
384 retStr += componentName;
385 } else {
386 return componentName.string();
387 }
388 return retStr.string();
389}
390
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700391static void printCompatibleScreens(ResXMLTree& tree) {
392 size_t len;
393 ResXMLTree::event_code_t code;
394 int depth = 0;
395 bool first = true;
396 printf("compatible-screens:");
397 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
398 if (code == ResXMLTree::END_TAG) {
399 depth--;
400 if (depth < 0) {
401 break;
402 }
403 continue;
404 }
405 if (code != ResXMLTree::START_TAG) {
406 continue;
407 }
408 depth++;
409 String8 tag(tree.getElementName(&len));
410 if (tag == "screen") {
411 int32_t screenSize = getIntegerAttribute(tree,
412 SCREEN_SIZE_ATTR, NULL, -1);
413 int32_t screenDensity = getIntegerAttribute(tree,
414 SCREEN_DENSITY_ATTR, NULL, -1);
415 if (screenSize > 0 && screenDensity > 0) {
416 if (!first) {
417 printf(",");
418 }
419 first = false;
420 printf("'%d/%d'", screenSize, screenDensity);
421 }
422 }
423 }
424 printf("\n");
425}
426
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800427/*
428 * Handle the "dump" command, to extract select data from an archive.
429 */
Dan Morrille74763e2012-01-06 10:47:10 -0800430extern char CONSOLE_DATA[2925]; // see EOF
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800431int doDump(Bundle* bundle)
432{
433 status_t result = UNKNOWN_ERROR;
434 Asset* asset = NULL;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700435
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800436 if (bundle->getFileSpecCount() < 1) {
437 fprintf(stderr, "ERROR: no dump option specified\n");
438 return 1;
439 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700440
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800441 if (bundle->getFileSpecCount() < 2) {
442 fprintf(stderr, "ERROR: no dump file specified\n");
443 return 1;
444 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700445
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800446 const char* option = bundle->getFileSpecEntry(0);
447 const char* filename = bundle->getFileSpecEntry(1);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700448
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800449 AssetManager assets;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700450 void* assetsCookie;
451 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800452 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
453 return 1;
454 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700455
Dianne Hackborne289bff2011-06-13 19:33:22 -0700456 // Make a dummy config for retrieving resources... we need to supply
457 // non-default values for some configs so that we can retrieve resources
458 // in the app that don't have a default. The most important of these is
459 // the API version because key resources like icons will have an implicit
460 // version if they are using newer config types like density.
461 ResTable_config config;
462 config.language[0] = 'e';
463 config.language[1] = 'n';
464 config.country[0] = 'U';
465 config.country[1] = 'S';
466 config.orientation = ResTable_config::ORIENTATION_PORT;
467 config.density = ResTable_config::DENSITY_MEDIUM;
468 config.sdkVersion = 10000; // Very high.
469 config.screenWidthDp = 320;
470 config.screenHeightDp = 480;
471 config.smallestScreenWidthDp = 320;
472 assets.setConfiguration(config);
473
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800474 const ResTable& res = assets.getResources(false);
475 if (&res == NULL) {
476 fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
477 goto bail;
478 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700479
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800480 if (strcmp("resources", option) == 0) {
Steve Blockf1ff21a2010-06-14 17:34:04 +0100481#ifndef HAVE_ANDROID_OS
Dianne Hackborne17086b2009-06-19 15:13:28 -0700482 res.print(bundle->getValues());
Steve Blockf1ff21a2010-06-14 17:34:04 +0100483#endif
Dianne Hackborn6c997a92012-01-31 11:27:43 -0800484
485 } else if (strcmp("strings", option) == 0) {
486 const ResStringPool* pool = res.getTableStringBlock(0);
487 printStringPool(pool);
488
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800489 } else if (strcmp("xmltree", option) == 0) {
490 if (bundle->getFileSpecCount() < 3) {
491 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
492 goto bail;
493 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700494
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800495 for (int i=2; i<bundle->getFileSpecCount(); i++) {
496 const char* resname = bundle->getFileSpecEntry(i);
497 ResXMLTree tree;
498 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
499 if (asset == NULL) {
Kenny Root44b283d2009-09-01 19:03:11 -0500500 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800501 goto bail;
502 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700503
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800504 if (tree.setTo(asset->getBuffer(true),
505 asset->getLength()) != NO_ERROR) {
506 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
507 goto bail;
508 }
509 tree.restart();
510 printXMLBlock(&tree);
Kenny Root19138462009-12-04 09:38:48 -0800511 tree.uninit();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800512 delete asset;
513 asset = NULL;
514 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700515
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800516 } else if (strcmp("xmlstrings", option) == 0) {
517 if (bundle->getFileSpecCount() < 3) {
518 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
519 goto bail;
520 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700521
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800522 for (int i=2; i<bundle->getFileSpecCount(); i++) {
523 const char* resname = bundle->getFileSpecEntry(i);
524 ResXMLTree tree;
525 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
526 if (asset == NULL) {
Kenny Root44b283d2009-09-01 19:03:11 -0500527 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800528 goto bail;
529 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700530
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800531 if (tree.setTo(asset->getBuffer(true),
532 asset->getLength()) != NO_ERROR) {
533 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
534 goto bail;
535 }
536 printStringPool(&tree.getStrings());
537 delete asset;
538 asset = NULL;
539 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700540
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800541 } else {
542 ResXMLTree tree;
543 asset = assets.openNonAsset("AndroidManifest.xml",
544 Asset::ACCESS_BUFFER);
545 if (asset == NULL) {
546 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
547 goto bail;
548 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700549
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800550 if (tree.setTo(asset->getBuffer(true),
551 asset->getLength()) != NO_ERROR) {
552 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
553 goto bail;
554 }
555 tree.restart();
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700556
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800557 if (strcmp("permissions", option) == 0) {
558 size_t len;
559 ResXMLTree::event_code_t code;
560 int depth = 0;
561 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
562 if (code == ResXMLTree::END_TAG) {
563 depth--;
564 continue;
565 }
566 if (code != ResXMLTree::START_TAG) {
567 continue;
568 }
569 depth++;
570 String8 tag(tree.getElementName(&len));
571 //printf("Depth %d tag %s\n", depth, tag.string());
572 if (depth == 1) {
573 if (tag != "manifest") {
574 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
575 goto bail;
576 }
577 String8 pkg = getAttribute(tree, NULL, "package", NULL);
578 printf("package: %s\n", pkg.string());
579 } else if (depth == 2 && tag == "permission") {
580 String8 error;
581 String8 name = getAttribute(tree, NAME_ATTR, &error);
582 if (error != "") {
583 fprintf(stderr, "ERROR: %s\n", error.string());
584 goto bail;
585 }
586 printf("permission: %s\n", name.string());
587 } else if (depth == 2 && tag == "uses-permission") {
588 String8 error;
589 String8 name = getAttribute(tree, NAME_ATTR, &error);
590 if (error != "") {
591 fprintf(stderr, "ERROR: %s\n", error.string());
592 goto bail;
593 }
594 printf("uses-permission: %s\n", name.string());
595 }
596 }
597 } else if (strcmp("badging", option) == 0) {
Dianne Hackborne289bff2011-06-13 19:33:22 -0700598 Vector<String8> locales;
599 res.getLocales(&locales);
600
601 Vector<ResTable_config> configs;
602 res.getConfigurations(&configs);
603 SortedVector<int> densities;
604 const size_t NC = configs.size();
605 for (size_t i=0; i<NC; i++) {
606 int dens = configs[i].density;
607 if (dens == 0) dens = 160;
608 densities.add(dens);
609 }
610
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800611 size_t len;
612 ResXMLTree::event_code_t code;
613 int depth = 0;
614 String8 error;
615 bool withinActivity = false;
616 bool isMainActivity = false;
617 bool isLauncherActivity = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700618 bool isSearchable = false;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700619 bool withinApplication = false;
620 bool withinReceiver = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700621 bool withinService = false;
622 bool withinIntentFilter = false;
623 bool hasMainActivity = false;
624 bool hasOtherActivities = false;
625 bool hasOtherReceivers = false;
626 bool hasOtherServices = false;
627 bool hasWallpaperService = false;
628 bool hasImeService = false;
629 bool hasWidgetReceivers = false;
630 bool hasIntentFilter = false;
631 bool actMainActivity = false;
632 bool actWidgetReceivers = false;
633 bool actImeService = false;
634 bool actWallpaperService = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700635
Kenny Root063a44e2011-12-08 08:46:03 -0800636 // These two implement the implicit permissions that are granted
637 // to pre-1.6 applications.
638 bool hasWriteExternalStoragePermission = false;
639 bool hasReadPhoneStatePermission = false;
640
Dianne Hackborn79245122012-03-12 10:51:26 -0700641 // If an app requests write storage, they will also get read storage.
642 bool hasReadExternalStoragePermission = false;
643
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -0700644 // Implement transition to read and write call log.
645 bool hasReadContactsPermission = false;
646 bool hasWriteContactsPermission = false;
647 bool hasReadCallLogPermission = false;
648 bool hasWriteCallLogPermission = false;
649
Dan Morrill89d97c12010-05-03 16:13:14 -0700650 // This next group of variables is used to implement a group of
651 // backward-compatibility heuristics necessitated by the addition of
652 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
653 // heuristic is "if an app requests a permission but doesn't explicitly
654 // request the corresponding <uses-feature>, presume it's there anyway".
655 bool specCameraFeature = false; // camera-related
656 bool specCameraAutofocusFeature = false;
657 bool reqCameraAutofocusFeature = false;
658 bool reqCameraFlashFeature = false;
Dianne Hackborne5276a72009-08-27 16:28:44 -0700659 bool hasCameraPermission = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700660 bool specLocationFeature = false; // location-related
661 bool specNetworkLocFeature = false;
662 bool reqNetworkLocFeature = false;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800663 bool specGpsFeature = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700664 bool reqGpsFeature = false;
665 bool hasMockLocPermission = false;
666 bool hasCoarseLocPermission = false;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800667 bool hasGpsPermission = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700668 bool hasGeneralLocPermission = false;
669 bool specBluetoothFeature = false; // Bluetooth API-related
670 bool hasBluetoothPermission = false;
671 bool specMicrophoneFeature = false; // microphone-related
672 bool hasRecordAudioPermission = false;
673 bool specWiFiFeature = false;
674 bool hasWiFiPermission = false;
675 bool specTelephonyFeature = false; // telephony-related
676 bool reqTelephonySubFeature = false;
677 bool hasTelephonyPermission = false;
678 bool specTouchscreenFeature = false; // touchscreen-related
679 bool specMultitouchFeature = false;
680 bool reqDistinctMultitouchFeature = false;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700681 bool specScreenPortraitFeature = false;
682 bool specScreenLandscapeFeature = false;
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -0700683 bool reqScreenPortraitFeature = false;
684 bool reqScreenLandscapeFeature = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700685 // 2.2 also added some other features that apps can request, but that
686 // have no corresponding permission, so we cannot implement any
687 // back-compatibility heuristic for them. The below are thus unnecessary
688 // (but are retained here for documentary purposes.)
689 //bool specCompassFeature = false;
690 //bool specAccelerometerFeature = false;
691 //bool specProximityFeature = false;
692 //bool specAmbientLightFeature = false;
693 //bool specLiveWallpaperFeature = false;
694
Dianne Hackborn723738c2009-06-25 19:48:04 -0700695 int targetSdk = 0;
696 int smallScreen = 1;
697 int normalScreen = 1;
698 int largeScreen = 1;
Dianne Hackbornf43489d2010-08-20 12:44:33 -0700699 int xlargeScreen = 1;
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700700 int anyDensity = 1;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700701 int requiresSmallestWidthDp = 0;
702 int compatibleWidthLimitDp = 0;
703 int largestWidthLimitDp = 0;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700704 String8 pkg;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800705 String8 activityName;
706 String8 activityLabel;
707 String8 activityIcon;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700708 String8 receiverName;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700709 String8 serviceName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800710 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
711 if (code == ResXMLTree::END_TAG) {
712 depth--;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700713 if (depth < 2) {
714 withinApplication = false;
715 } else if (depth < 3) {
716 if (withinActivity && isMainActivity && isLauncherActivity) {
717 const char *aName = getComponentName(pkg, activityName);
Dianne Hackborne289bff2011-06-13 19:33:22 -0700718 printf("launchable-activity:");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700719 if (aName != NULL) {
Dianne Hackborne289bff2011-06-13 19:33:22 -0700720 printf(" name='%s' ", aName);
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700721 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700722 printf(" label='%s' icon='%s'\n",
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700723 activityLabel.string(),
724 activityIcon.string());
725 }
726 if (!hasIntentFilter) {
727 hasOtherActivities |= withinActivity;
728 hasOtherReceivers |= withinReceiver;
729 hasOtherServices |= withinService;
730 }
731 withinActivity = false;
732 withinService = false;
733 withinReceiver = false;
734 hasIntentFilter = false;
735 isMainActivity = isLauncherActivity = false;
736 } else if (depth < 4) {
737 if (withinIntentFilter) {
738 if (withinActivity) {
739 hasMainActivity |= actMainActivity;
740 hasOtherActivities |= !actMainActivity;
741 } else if (withinReceiver) {
742 hasWidgetReceivers |= actWidgetReceivers;
743 hasOtherReceivers |= !actWidgetReceivers;
744 } else if (withinService) {
745 hasImeService |= actImeService;
746 hasWallpaperService |= actWallpaperService;
747 hasOtherServices |= (!actImeService && !actWallpaperService);
748 }
749 }
750 withinIntentFilter = false;
751 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800752 continue;
753 }
754 if (code != ResXMLTree::START_TAG) {
755 continue;
756 }
757 depth++;
758 String8 tag(tree.getElementName(&len));
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700759 //printf("Depth %d, %s\n", depth, tag.string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800760 if (depth == 1) {
761 if (tag != "manifest") {
762 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
763 goto bail;
764 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700765 pkg = getAttribute(tree, NULL, "package", NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800766 printf("package: name='%s' ", pkg.string());
767 int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
768 if (error != "") {
769 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
770 goto bail;
771 }
772 if (versionCode > 0) {
773 printf("versionCode='%d' ", versionCode);
774 } else {
775 printf("versionCode='' ");
776 }
Dianne Hackborncf244ad2010-03-09 15:00:30 -0800777 String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800778 if (error != "") {
779 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
780 goto bail;
781 }
782 printf("versionName='%s'\n", versionName.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700783 } else if (depth == 2) {
784 withinApplication = false;
785 if (tag == "application") {
786 withinApplication = true;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700787
788 String8 label;
789 const size_t NL = locales.size();
790 for (size_t i=0; i<NL; i++) {
791 const char* localeStr = locales[i].string();
792 assets.setLocale(localeStr != NULL ? localeStr : "");
793 String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
794 if (llabel != "") {
795 if (localeStr == NULL || strlen(localeStr) == 0) {
796 label = llabel;
797 printf("application-label:'%s'\n", llabel.string());
798 } else {
799 if (label == "") {
800 label = llabel;
801 }
802 printf("application-label-%s:'%s'\n", localeStr,
803 llabel.string());
804 }
805 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700806 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700807
808 ResTable_config tmpConfig = config;
809 const size_t ND = densities.size();
810 for (size_t i=0; i<ND; i++) {
811 tmpConfig.density = densities[i];
812 assets.setConfiguration(tmpConfig);
813 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
814 if (icon != "") {
815 printf("application-icon-%d:'%s'\n", densities[i], icon.string());
816 }
817 }
818 assets.setConfiguration(config);
819
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700820 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
821 if (error != "") {
822 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
823 goto bail;
824 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700825 int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700826 if (error != "") {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700827 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700828 goto bail;
829 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700830 printf("application: label='%s' ", label.string());
831 printf("icon='%s'\n", icon.string());
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700832 if (testOnly != 0) {
833 printf("testOnly='%d'\n", testOnly);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700834 }
Dan Morrillb6ec11e2012-04-03 12:44:40 -0700835
836 int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0);
837 if (error != "") {
838 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string());
839 goto bail;
840 }
841 if (debuggable != 0) {
842 printf("application-debuggable\n");
843 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700844 } else if (tag == "uses-sdk") {
845 int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
846 if (error != "") {
847 error = "";
848 String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
849 if (error != "") {
850 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
851 error.string());
852 goto bail;
853 }
Dianne Hackborn723738c2009-06-25 19:48:04 -0700854 if (name == "Donut") targetSdk = 4;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700855 printf("sdkVersion:'%s'\n", name.string());
856 } else if (code != -1) {
Dianne Hackborn723738c2009-06-25 19:48:04 -0700857 targetSdk = code;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700858 printf("sdkVersion:'%d'\n", code);
859 }
Suchi Amalapurapu75c49842009-08-14 15:13:09 -0700860 code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
861 if (code != -1) {
862 printf("maxSdkVersion:'%d'\n", code);
863 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700864 code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
865 if (error != "") {
866 error = "";
867 String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
868 if (error != "") {
869 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
870 error.string());
871 goto bail;
872 }
Dianne Hackborn723738c2009-06-25 19:48:04 -0700873 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700874 printf("targetSdkVersion:'%s'\n", name.string());
875 } else if (code != -1) {
Dianne Hackborn723738c2009-06-25 19:48:04 -0700876 if (targetSdk < code) {
877 targetSdk = code;
878 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700879 printf("targetSdkVersion:'%d'\n", code);
880 }
881 } else if (tag == "uses-configuration") {
882 int32_t reqTouchScreen = getIntegerAttribute(tree,
883 REQ_TOUCH_SCREEN_ATTR, NULL, 0);
884 int32_t reqKeyboardType = getIntegerAttribute(tree,
885 REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
886 int32_t reqHardKeyboard = getIntegerAttribute(tree,
887 REQ_HARD_KEYBOARD_ATTR, NULL, 0);
888 int32_t reqNavigation = getIntegerAttribute(tree,
889 REQ_NAVIGATION_ATTR, NULL, 0);
890 int32_t reqFiveWayNav = getIntegerAttribute(tree,
891 REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
Dianne Hackborncb2d50d2010-01-06 11:29:54 -0800892 printf("uses-configuration:");
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700893 if (reqTouchScreen != 0) {
894 printf(" reqTouchScreen='%d'", reqTouchScreen);
895 }
896 if (reqKeyboardType != 0) {
897 printf(" reqKeyboardType='%d'", reqKeyboardType);
898 }
899 if (reqHardKeyboard != 0) {
900 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
901 }
902 if (reqNavigation != 0) {
903 printf(" reqNavigation='%d'", reqNavigation);
904 }
905 if (reqFiveWayNav != 0) {
906 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
907 }
908 printf("\n");
Dianne Hackborn723738c2009-06-25 19:48:04 -0700909 } else if (tag == "supports-screens") {
910 smallScreen = getIntegerAttribute(tree,
911 SMALL_SCREEN_ATTR, NULL, 1);
912 normalScreen = getIntegerAttribute(tree,
913 NORMAL_SCREEN_ATTR, NULL, 1);
914 largeScreen = getIntegerAttribute(tree,
915 LARGE_SCREEN_ATTR, NULL, 1);
Dianne Hackbornf43489d2010-08-20 12:44:33 -0700916 xlargeScreen = getIntegerAttribute(tree,
917 XLARGE_SCREEN_ATTR, NULL, 1);
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700918 anyDensity = getIntegerAttribute(tree,
919 ANY_DENSITY_ATTR, NULL, 1);
Dianne Hackborne289bff2011-06-13 19:33:22 -0700920 requiresSmallestWidthDp = getIntegerAttribute(tree,
921 REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
922 compatibleWidthLimitDp = getIntegerAttribute(tree,
923 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
924 largestWidthLimitDp = getIntegerAttribute(tree,
925 LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
Dianne Hackborne5276a72009-08-27 16:28:44 -0700926 } else if (tag == "uses-feature") {
927 String8 name = getAttribute(tree, NAME_ATTR, &error);
Suchi Amalapurapu40b94722009-09-20 13:39:37 -0700928
929 if (name != "" && error == "") {
Dianne Hackborne5276a72009-08-27 16:28:44 -0700930 int req = getIntegerAttribute(tree,
931 REQUIRED_ATTR, NULL, 1);
Dan Morrill89d97c12010-05-03 16:13:14 -0700932
Dianne Hackborne5276a72009-08-27 16:28:44 -0700933 if (name == "android.hardware.camera") {
934 specCameraFeature = true;
Dan Morrill89d97c12010-05-03 16:13:14 -0700935 } else if (name == "android.hardware.camera.autofocus") {
936 // these have no corresponding permission to check for,
937 // but should imply the foundational camera permission
938 reqCameraAutofocusFeature = reqCameraAutofocusFeature || req;
939 specCameraAutofocusFeature = true;
940 } else if (req && (name == "android.hardware.camera.flash")) {
941 // these have no corresponding permission to check for,
942 // but should imply the foundational camera permission
943 reqCameraFlashFeature = true;
944 } else if (name == "android.hardware.location") {
945 specLocationFeature = true;
946 } else if (name == "android.hardware.location.network") {
947 specNetworkLocFeature = true;
948 reqNetworkLocFeature = reqNetworkLocFeature || req;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800949 } else if (name == "android.hardware.location.gps") {
950 specGpsFeature = true;
Dan Morrill89d97c12010-05-03 16:13:14 -0700951 reqGpsFeature = reqGpsFeature || req;
952 } else if (name == "android.hardware.bluetooth") {
953 specBluetoothFeature = true;
954 } else if (name == "android.hardware.touchscreen") {
955 specTouchscreenFeature = true;
956 } else if (name == "android.hardware.touchscreen.multitouch") {
957 specMultitouchFeature = true;
958 } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
959 reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req;
960 } else if (name == "android.hardware.microphone") {
961 specMicrophoneFeature = true;
962 } else if (name == "android.hardware.wifi") {
963 specWiFiFeature = true;
964 } else if (name == "android.hardware.telephony") {
965 specTelephonyFeature = true;
966 } else if (req && (name == "android.hardware.telephony.gsm" ||
967 name == "android.hardware.telephony.cdma")) {
968 // these have no corresponding permission to check for,
969 // but should imply the foundational telephony permission
970 reqTelephonySubFeature = true;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700971 } else if (name == "android.hardware.screen.portrait") {
972 specScreenPortraitFeature = true;
973 } else if (name == "android.hardware.screen.landscape") {
974 specScreenLandscapeFeature = true;
Dianne Hackborne5276a72009-08-27 16:28:44 -0700975 }
976 printf("uses-feature%s:'%s'\n",
977 req ? "" : "-not-required", name.string());
978 } else {
979 int vers = getIntegerAttribute(tree,
980 GL_ES_VERSION_ATTR, &error);
981 if (error == "") {
982 printf("uses-gl-es:'0x%x'\n", vers);
983 }
984 }
985 } else if (tag == "uses-permission") {
986 String8 name = getAttribute(tree, NAME_ATTR, &error);
Suchi Amalapurapu40b94722009-09-20 13:39:37 -0700987 if (name != "" && error == "") {
Dianne Hackborne5276a72009-08-27 16:28:44 -0700988 if (name == "android.permission.CAMERA") {
989 hasCameraPermission = true;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800990 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
991 hasGpsPermission = true;
Dan Morrill89d97c12010-05-03 16:13:14 -0700992 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
993 hasMockLocPermission = true;
994 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
995 hasCoarseLocPermission = true;
996 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
997 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
998 hasGeneralLocPermission = true;
999 } else if (name == "android.permission.BLUETOOTH" ||
1000 name == "android.permission.BLUETOOTH_ADMIN") {
1001 hasBluetoothPermission = true;
1002 } else if (name == "android.permission.RECORD_AUDIO") {
1003 hasRecordAudioPermission = true;
1004 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1005 name == "android.permission.CHANGE_WIFI_STATE" ||
1006 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
1007 hasWiFiPermission = true;
1008 } else if (name == "android.permission.CALL_PHONE" ||
1009 name == "android.permission.CALL_PRIVILEGED" ||
1010 name == "android.permission.MODIFY_PHONE_STATE" ||
1011 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1012 name == "android.permission.READ_SMS" ||
1013 name == "android.permission.RECEIVE_SMS" ||
1014 name == "android.permission.RECEIVE_MMS" ||
1015 name == "android.permission.RECEIVE_WAP_PUSH" ||
1016 name == "android.permission.SEND_SMS" ||
1017 name == "android.permission.WRITE_APN_SETTINGS" ||
1018 name == "android.permission.WRITE_SMS") {
1019 hasTelephonyPermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001020 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1021 hasWriteExternalStoragePermission = true;
Dianne Hackborn79245122012-03-12 10:51:26 -07001022 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1023 hasReadExternalStoragePermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001024 } else if (name == "android.permission.READ_PHONE_STATE") {
1025 hasReadPhoneStatePermission = true;
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001026 } else if (name == "android.permission.READ_CONTACTS") {
1027 hasReadContactsPermission = true;
1028 } else if (name == "android.permission.WRITE_CONTACTS") {
1029 hasWriteContactsPermission = true;
1030 } else if (name == "android.permission.READ_CALL_LOG") {
1031 hasReadCallLogPermission = true;
1032 } else if (name == "android.permission.WRITE_CALL_LOG") {
1033 hasWriteCallLogPermission = true;
Dianne Hackborne5276a72009-08-27 16:28:44 -07001034 }
1035 printf("uses-permission:'%s'\n", name.string());
1036 } else {
1037 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1038 error.string());
1039 goto bail;
1040 }
Dianne Hackborn43b68032010-09-02 17:14:41 -07001041 } else if (tag == "uses-package") {
1042 String8 name = getAttribute(tree, NAME_ATTR, &error);
1043 if (name != "" && error == "") {
1044 printf("uses-package:'%s'\n", name.string());
1045 } else {
1046 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1047 error.string());
1048 goto bail;
1049 }
Jeff Hamiltone2c17f92010-02-12 13:45:16 -06001050 } else if (tag == "original-package") {
1051 String8 name = getAttribute(tree, NAME_ATTR, &error);
1052 if (name != "" && error == "") {
1053 printf("original-package:'%s'\n", name.string());
1054 } else {
1055 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1056 error.string());
1057 goto bail;
1058 }
Dan Morrill096b67f2010-12-13 16:25:54 -08001059 } else if (tag == "supports-gl-texture") {
Dan Morrill6f51fc12010-10-13 14:33:43 -07001060 String8 name = getAttribute(tree, NAME_ATTR, &error);
1061 if (name != "" && error == "") {
Dan Morrill096b67f2010-12-13 16:25:54 -08001062 printf("supports-gl-texture:'%s'\n", name.string());
Dan Morrill6f51fc12010-10-13 14:33:43 -07001063 } else {
1064 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1065 error.string());
1066 goto bail;
1067 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001068 } else if (tag == "compatible-screens") {
1069 printCompatibleScreens(tree);
1070 depth--;
Kenny Root56088a52011-09-29 13:49:45 -07001071 } else if (tag == "package-verifier") {
1072 String8 name = getAttribute(tree, NAME_ATTR, &error);
1073 if (name != "" && error == "") {
1074 String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
1075 if (publicKey != "" && error == "") {
1076 printf("package-verifier: name='%s' publicKey='%s'\n",
1077 name.string(), publicKey.string());
1078 }
1079 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001080 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001081 } else if (depth == 3 && withinApplication) {
1082 withinActivity = false;
1083 withinReceiver = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001084 withinService = false;
1085 hasIntentFilter = false;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001086 if(tag == "activity") {
1087 withinActivity = true;
1088 activityName = getAttribute(tree, NAME_ATTR, &error);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001089 if (error != "") {
1090 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1091 goto bail;
1092 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001093
1094 activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001095 if (error != "") {
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001096 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001097 goto bail;
1098 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001099
1100 activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1101 if (error != "") {
1102 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
1103 goto bail;
1104 }
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001105
1106 int32_t orien = getResolvedIntegerAttribute(&res, tree,
1107 SCREEN_ORIENTATION_ATTR, &error);
1108 if (error == "") {
1109 if (orien == 0 || orien == 6 || orien == 8) {
1110 // Requests landscape, sensorLandscape, or reverseLandscape.
1111 reqScreenLandscapeFeature = true;
1112 } else if (orien == 1 || orien == 7 || orien == 9) {
1113 // Requests portrait, sensorPortrait, or reversePortrait.
1114 reqScreenPortraitFeature = true;
1115 }
1116 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001117 } else if (tag == "uses-library") {
1118 String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1119 if (error != "") {
1120 fprintf(stderr, "ERROR getting 'android:name' attribute for uses-library: %s\n", error.string());
1121 goto bail;
1122 }
Dianne Hackborn49237342009-08-27 20:08:01 -07001123 int req = getIntegerAttribute(tree,
1124 REQUIRED_ATTR, NULL, 1);
1125 printf("uses-library%s:'%s'\n",
1126 req ? "" : "-not-required", libraryName.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001127 } else if (tag == "receiver") {
1128 withinReceiver = true;
1129 receiverName = getAttribute(tree, NAME_ATTR, &error);
1130
1131 if (error != "") {
1132 fprintf(stderr, "ERROR getting 'android:name' attribute for receiver: %s\n", error.string());
1133 goto bail;
1134 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001135 } else if (tag == "service") {
1136 withinService = true;
1137 serviceName = getAttribute(tree, NAME_ATTR, &error);
1138
1139 if (error != "") {
1140 fprintf(stderr, "ERROR getting 'android:name' attribute for service: %s\n", error.string());
1141 goto bail;
1142 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001143 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001144 } else if ((depth == 4) && (tag == "intent-filter")) {
1145 hasIntentFilter = true;
1146 withinIntentFilter = true;
1147 actMainActivity = actWidgetReceivers = actImeService = actWallpaperService = false;
1148 } else if ((depth == 5) && withinIntentFilter){
1149 String8 action;
1150 if (tag == "action") {
1151 action = getAttribute(tree, NAME_ATTR, &error);
1152 if (error != "") {
1153 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1154 goto bail;
1155 }
1156 if (withinActivity) {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001157 if (action == "android.intent.action.MAIN") {
1158 isMainActivity = true;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001159 actMainActivity = true;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001160 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001161 } else if (withinReceiver) {
1162 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1163 actWidgetReceivers = true;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001164 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001165 } else if (withinService) {
1166 if (action == "android.view.InputMethod") {
1167 actImeService = true;
1168 } else if (action == "android.service.wallpaper.WallpaperService") {
1169 actWallpaperService = true;
1170 }
1171 }
1172 if (action == "android.intent.action.SEARCH") {
1173 isSearchable = true;
1174 }
1175 }
1176
1177 if (tag == "category") {
1178 String8 category = getAttribute(tree, NAME_ATTR, &error);
1179 if (error != "") {
1180 fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string());
1181 goto bail;
1182 }
1183 if (withinActivity) {
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001184 if (category == "android.intent.category.LAUNCHER") {
1185 isLauncherActivity = true;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001186 }
1187 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001188 }
1189 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001190 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001191
Kenny Root063a44e2011-12-08 08:46:03 -08001192 // Pre-1.6 implicitly granted permission compatibility logic
1193 if (targetSdk < 4) {
1194 if (!hasWriteExternalStoragePermission) {
1195 printf("uses-permission:'android.permission.WRITE_EXTERNAL_STORAGE'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001196 printf("uses-implied-permission:'android.permission.WRITE_EXTERNAL_STORAGE'," \
1197 "'targetSdkVersion < 4'\n");
Dianne Hackborn79245122012-03-12 10:51:26 -07001198 hasWriteExternalStoragePermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001199 }
1200 if (!hasReadPhoneStatePermission) {
1201 printf("uses-permission:'android.permission.READ_PHONE_STATE'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001202 printf("uses-implied-permission:'android.permission.READ_PHONE_STATE'," \
1203 "'targetSdkVersion < 4'\n");
Kenny Root063a44e2011-12-08 08:46:03 -08001204 }
1205 }
1206
Dianne Hackborn79245122012-03-12 10:51:26 -07001207 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
Dianne Hackborn2bd8d042012-06-11 12:27:05 -07001208 // force them to always take READ_EXTERNAL_STORAGE as well. We always
1209 // do this (regardless of target API version) because we can't have
1210 // an app with write permission but not read permission.
Dianne Hackborn79245122012-03-12 10:51:26 -07001211 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
1212 printf("uses-permission:'android.permission.READ_EXTERNAL_STORAGE'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001213 printf("uses-implied-permission:'android.permission.READ_EXTERNAL_STORAGE'," \
1214 "'requested WRITE_EXTERNAL_STORAGE'\n");
Dianne Hackborn79245122012-03-12 10:51:26 -07001215 }
1216
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001217 // Pre-JellyBean call log permission compatibility.
1218 if (targetSdk < 16) {
1219 if (!hasReadCallLogPermission && hasReadContactsPermission) {
1220 printf("uses-permission:'android.permission.READ_CALL_LOG'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001221 printf("uses-implied-permission:'android.permission.READ_CALL_LOG'," \
1222 "'targetSdkVersion < 16 and requested READ_CONTACTS'\n");
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001223 }
1224 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
1225 printf("uses-permission:'android.permission.WRITE_CALL_LOG'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001226 printf("uses-implied-permission:'android.permission.WRITE_CALL_LOG'," \
1227 "'targetSdkVersion < 16 and requested WRITE_CONTACTS'\n");
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001228 }
1229 }
1230
Dan Morrill89d97c12010-05-03 16:13:14 -07001231 /* The following blocks handle printing "inferred" uses-features, based
1232 * on whether related features or permissions are used by the app.
1233 * Note that the various spec*Feature variables denote whether the
1234 * relevant tag was *present* in the AndroidManfest, not that it was
1235 * present and set to true.
1236 */
1237 // Camera-related back-compatibility logic
1238 if (!specCameraFeature) {
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001239 if (reqCameraFlashFeature) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001240 // if app requested a sub-feature (autofocus or flash) and didn't
1241 // request the base camera feature, we infer that it meant to
1242 printf("uses-feature:'android.hardware.camera'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001243 printf("uses-implied-feature:'android.hardware.camera'," \
1244 "'requested android.hardware.camera.flash feature'\n");
1245 } else if (reqCameraAutofocusFeature) {
1246 // if app requested a sub-feature (autofocus or flash) and didn't
1247 // request the base camera feature, we infer that it meant to
1248 printf("uses-feature:'android.hardware.camera'\n");
1249 printf("uses-implied-feature:'android.hardware.camera'," \
1250 "'requested android.hardware.camera.autofocus feature'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001251 } else if (hasCameraPermission) {
1252 // if app wants to use camera but didn't request the feature, we infer
1253 // that it meant to, and further that it wants autofocus
1254 // (which was the 1.0 - 1.5 behavior)
1255 printf("uses-feature:'android.hardware.camera'\n");
1256 if (!specCameraAutofocusFeature) {
1257 printf("uses-feature:'android.hardware.camera.autofocus'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001258 printf("uses-implied-feature:'android.hardware.camera.autofocus'," \
1259 "'requested android.permission.CAMERA permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001260 }
1261 }
Dianne Hackborne5276a72009-08-27 16:28:44 -07001262 }
Doug Zongkerdbe7a682009-10-09 11:24:51 -07001263
Dan Morrill89d97c12010-05-03 16:13:14 -07001264 // Location-related back-compatibility logic
1265 if (!specLocationFeature &&
1266 (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission ||
1267 hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) {
1268 // if app either takes a location-related permission or requests one of the
1269 // sub-features, we infer that it also meant to request the base location feature
1270 printf("uses-feature:'android.hardware.location'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001271 printf("uses-implied-feature:'android.hardware.location'," \
1272 "'requested a location access permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001273 }
Dianne Hackbornef05e072010-03-01 17:43:39 -08001274 if (!specGpsFeature && hasGpsPermission) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001275 // if app takes GPS (FINE location) perm but does not request the GPS
1276 // feature, we infer that it meant to
Dianne Hackbornef05e072010-03-01 17:43:39 -08001277 printf("uses-feature:'android.hardware.location.gps'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001278 printf("uses-implied-feature:'android.hardware.location.gps'," \
1279 "'requested android.permission.ACCESS_FINE_LOCATION permission'\n");
Dianne Hackbornef05e072010-03-01 17:43:39 -08001280 }
Dan Morrill89d97c12010-05-03 16:13:14 -07001281 if (!specNetworkLocFeature && hasCoarseLocPermission) {
1282 // if app takes Network location (COARSE location) perm but does not request the
1283 // network location feature, we infer that it meant to
1284 printf("uses-feature:'android.hardware.location.network'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001285 printf("uses-implied-feature:'android.hardware.location.network'," \
1286 "'requested android.permission.ACCESS_COURSE_LOCATION permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001287 }
1288
1289 // Bluetooth-related compatibility logic
Dan Morrill6b22d812010-06-15 21:41:42 -07001290 if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001291 // if app takes a Bluetooth permission but does not request the Bluetooth
1292 // feature, we infer that it meant to
1293 printf("uses-feature:'android.hardware.bluetooth'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001294 printf("uses-implied-feature:'android.hardware.bluetooth'," \
1295 "'requested android.permission.BLUETOOTH or android.permission.BLUETOOTH_ADMIN " \
1296 "permission and targetSdkVersion > 4'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001297 }
1298
1299 // Microphone-related compatibility logic
1300 if (!specMicrophoneFeature && hasRecordAudioPermission) {
1301 // if app takes the record-audio permission but does not request the microphone
1302 // feature, we infer that it meant to
1303 printf("uses-feature:'android.hardware.microphone'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001304 printf("uses-implied-feature:'android.hardware.microphone'," \
1305 "'requested android.permission.RECORD_AUDIO permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001306 }
1307
1308 // WiFi-related compatibility logic
1309 if (!specWiFiFeature && hasWiFiPermission) {
1310 // if app takes one of the WiFi permissions but does not request the WiFi
1311 // feature, we infer that it meant to
1312 printf("uses-feature:'android.hardware.wifi'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001313 printf("uses-implied-feature:'android.hardware.wifi'," \
1314 "'requested android.permission.ACCESS_WIFI_STATE, " \
1315 "android.permission.CHANGE_WIFI_STATE, or " \
1316 "android.permission.CHANGE_WIFI_MULTICAST_STATE permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001317 }
1318
1319 // Telephony-related compatibility logic
1320 if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) {
1321 // if app takes one of the telephony permissions or requests a sub-feature but
1322 // does not request the base telephony feature, we infer that it meant to
1323 printf("uses-feature:'android.hardware.telephony'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001324 printf("uses-implied-feature:'android.hardware.telephony'," \
1325 "'requested a telephony-related permission or feature'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001326 }
1327
1328 // Touchscreen-related back-compatibility logic
1329 if (!specTouchscreenFeature) { // not a typo!
1330 // all apps are presumed to require a touchscreen, unless they explicitly say
1331 // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
1332 // Note that specTouchscreenFeature is true if the tag is present, regardless
1333 // of whether its value is true or false, so this is safe
1334 printf("uses-feature:'android.hardware.touchscreen'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001335 printf("uses-implied-feature:'android.hardware.touchscreen'," \
1336 "'assumed you require a touch screen unless explicitly made optional'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001337 }
1338 if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
1339 // if app takes one of the telephony permissions or requests a sub-feature but
1340 // does not request the base telephony feature, we infer that it meant to
1341 printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001342 printf("uses-implied-feature:'android.hardware.touchscreen.multitouch'," \
1343 "'requested android.hardware.touchscreen.multitouch.distinct feature'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001344 }
Dianne Hackbornef05e072010-03-01 17:43:39 -08001345
Dianne Hackborne289bff2011-06-13 19:33:22 -07001346 // Landscape/portrait-related compatibility logic
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001347 if (!specScreenLandscapeFeature && !specScreenPortraitFeature) {
1348 // If the app has specified any activities in its manifest
1349 // that request a specific orientation, then assume that
1350 // orientation is required.
1351 if (reqScreenLandscapeFeature) {
1352 printf("uses-feature:'android.hardware.screen.landscape'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001353 printf("uses-implied-feature:'android.hardware.screen.landscape'," \
1354 "'one or more activities have specified a landscape orientation'\n");
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001355 }
1356 if (reqScreenPortraitFeature) {
1357 printf("uses-feature:'android.hardware.screen.portrait'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001358 printf("uses-implied-feature:'android.hardware.screen.portrait'," \
1359 "'one or more activities have specified a portrait orientation'\n");
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001360 }
Dianne Hackborne289bff2011-06-13 19:33:22 -07001361 }
1362
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001363 if (hasMainActivity) {
1364 printf("main\n");
1365 }
1366 if (hasWidgetReceivers) {
1367 printf("app-widget\n");
1368 }
1369 if (hasImeService) {
1370 printf("ime\n");
1371 }
1372 if (hasWallpaperService) {
1373 printf("wallpaper\n");
1374 }
1375 if (hasOtherActivities) {
1376 printf("other-activities\n");
1377 }
1378 if (isSearchable) {
1379 printf("search\n");
1380 }
1381 if (hasOtherReceivers) {
1382 printf("other-receivers\n");
1383 }
1384 if (hasOtherServices) {
1385 printf("other-services\n");
1386 }
1387
Dianne Hackborne289bff2011-06-13 19:33:22 -07001388 // For modern apps, if screen size buckets haven't been specified
1389 // but the new width ranges have, then infer the buckets from them.
1390 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1391 && requiresSmallestWidthDp > 0) {
1392 int compatWidth = compatibleWidthLimitDp;
1393 if (compatWidth <= 0) compatWidth = requiresSmallestWidthDp;
1394 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1395 smallScreen = -1;
1396 } else {
1397 smallScreen = 0;
1398 }
1399 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1400 normalScreen = -1;
1401 } else {
1402 normalScreen = 0;
1403 }
1404 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1405 largeScreen = -1;
1406 } else {
1407 largeScreen = 0;
1408 }
1409 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1410 xlargeScreen = -1;
1411 } else {
1412 xlargeScreen = 0;
1413 }
1414 }
1415
Dianne Hackborn723738c2009-06-25 19:48:04 -07001416 // Determine default values for any unspecified screen sizes,
1417 // based on the target SDK of the package. As of 4 (donut)
1418 // the screen size support was introduced, so all default to
1419 // enabled.
1420 if (smallScreen > 0) {
1421 smallScreen = targetSdk >= 4 ? -1 : 0;
1422 }
1423 if (normalScreen > 0) {
1424 normalScreen = -1;
1425 }
1426 if (largeScreen > 0) {
1427 largeScreen = targetSdk >= 4 ? -1 : 0;
1428 }
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001429 if (xlargeScreen > 0) {
Scott Maind58fb972010-11-04 18:32:00 -07001430 // Introduced in Gingerbread.
1431 xlargeScreen = targetSdk >= 9 ? -1 : 0;
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001432 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001433 if (anyDensity > 0) {
Dianne Hackborne289bff2011-06-13 19:33:22 -07001434 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1435 || compatibleWidthLimitDp > 0) ? -1 : 0;
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001436 }
Dianne Hackborn723738c2009-06-25 19:48:04 -07001437 printf("supports-screens:");
1438 if (smallScreen != 0) printf(" 'small'");
1439 if (normalScreen != 0) printf(" 'normal'");
1440 if (largeScreen != 0) printf(" 'large'");
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001441 if (xlargeScreen != 0) printf(" 'xlarge'");
Dianne Hackborn723738c2009-06-25 19:48:04 -07001442 printf("\n");
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001443 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
Dianne Hackborne289bff2011-06-13 19:33:22 -07001444 if (requiresSmallestWidthDp > 0) {
1445 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1446 }
1447 if (compatibleWidthLimitDp > 0) {
1448 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1449 }
1450 if (largestWidthLimitDp > 0) {
1451 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1452 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001453
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001454 printf("locales:");
Dianne Hackborne17086b2009-06-19 15:13:28 -07001455 const size_t NL = locales.size();
1456 for (size_t i=0; i<NL; i++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001457 const char* localeStr = locales[i].string();
1458 if (localeStr == NULL || strlen(localeStr) == 0) {
1459 localeStr = "--_--";
1460 }
1461 printf(" '%s'", localeStr);
1462 }
1463 printf("\n");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001464
Dianne Hackborne17086b2009-06-19 15:13:28 -07001465 printf("densities:");
1466 const size_t ND = densities.size();
1467 for (size_t i=0; i<ND; i++) {
1468 printf(" '%d'", densities[i]);
1469 }
1470 printf("\n");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001471
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001472 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1473 if (dir != NULL) {
1474 if (dir->getFileCount() > 0) {
1475 printf("native-code:");
1476 for (size_t i=0; i<dir->getFileCount(); i++) {
1477 printf(" '%s'", dir->getFileName(i).string());
1478 }
1479 printf("\n");
1480 }
1481 delete dir;
1482 }
Dan Morrille74763e2012-01-06 10:47:10 -08001483 } else if (strcmp("badger", option) == 0) {
Dianne Hackborn6c997a92012-01-31 11:27:43 -08001484 printf("%s", CONSOLE_DATA);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001485 } else if (strcmp("configurations", option) == 0) {
1486 Vector<ResTable_config> configs;
1487 res.getConfigurations(&configs);
1488 const size_t N = configs.size();
1489 for (size_t i=0; i<N; i++) {
1490 printf("%s\n", configs[i].toString().string());
1491 }
1492 } else {
1493 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
1494 goto bail;
1495 }
1496 }
1497
1498 result = NO_ERROR;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001499
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001500bail:
1501 if (asset) {
1502 delete asset;
1503 }
1504 return (result != NO_ERROR);
1505}
1506
1507
1508/*
1509 * Handle the "add" command, which wants to add files to a new or
1510 * pre-existing archive.
1511 */
1512int doAdd(Bundle* bundle)
1513{
1514 ZipFile* zip = NULL;
1515 status_t result = UNKNOWN_ERROR;
1516 const char* zipFileName;
1517
1518 if (bundle->getUpdate()) {
1519 /* avoid confusion */
1520 fprintf(stderr, "ERROR: can't use '-u' with add\n");
1521 goto bail;
1522 }
1523
1524 if (bundle->getFileSpecCount() < 1) {
1525 fprintf(stderr, "ERROR: must specify zip file name\n");
1526 goto bail;
1527 }
1528 zipFileName = bundle->getFileSpecEntry(0);
1529
1530 if (bundle->getFileSpecCount() < 2) {
1531 fprintf(stderr, "NOTE: nothing to do\n");
1532 goto bail;
1533 }
1534
1535 zip = openReadWrite(zipFileName, true);
1536 if (zip == NULL) {
1537 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
1538 goto bail;
1539 }
1540
1541 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1542 const char* fileName = bundle->getFileSpecEntry(i);
1543
1544 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
1545 printf(" '%s'... (from gzip)\n", fileName);
1546 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
1547 } else {
Doug Zongkerdbe7a682009-10-09 11:24:51 -07001548 if (bundle->getJunkPath()) {
1549 String8 storageName = String8(fileName).getPathLeaf();
1550 printf(" '%s' as '%s'...\n", fileName, storageName.string());
1551 result = zip->add(fileName, storageName.string(),
1552 bundle->getCompressionMethod(), NULL);
1553 } else {
1554 printf(" '%s'...\n", fileName);
1555 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
1556 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001557 }
1558 if (result != NO_ERROR) {
1559 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
1560 if (result == NAME_NOT_FOUND)
1561 fprintf(stderr, ": file not found\n");
1562 else if (result == ALREADY_EXISTS)
1563 fprintf(stderr, ": already exists in archive\n");
1564 else
1565 fprintf(stderr, "\n");
1566 goto bail;
1567 }
1568 }
1569
1570 result = NO_ERROR;
1571
1572bail:
1573 delete zip;
1574 return (result != NO_ERROR);
1575}
1576
1577
1578/*
1579 * Delete files from an existing archive.
1580 */
1581int doRemove(Bundle* bundle)
1582{
1583 ZipFile* zip = NULL;
1584 status_t result = UNKNOWN_ERROR;
1585 const char* zipFileName;
1586
1587 if (bundle->getFileSpecCount() < 1) {
1588 fprintf(stderr, "ERROR: must specify zip file name\n");
1589 goto bail;
1590 }
1591 zipFileName = bundle->getFileSpecEntry(0);
1592
1593 if (bundle->getFileSpecCount() < 2) {
1594 fprintf(stderr, "NOTE: nothing to do\n");
1595 goto bail;
1596 }
1597
1598 zip = openReadWrite(zipFileName, false);
1599 if (zip == NULL) {
1600 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
1601 zipFileName);
1602 goto bail;
1603 }
1604
1605 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1606 const char* fileName = bundle->getFileSpecEntry(i);
1607 ZipEntry* entry;
1608
1609 entry = zip->getEntryByName(fileName);
1610 if (entry == NULL) {
1611 printf(" '%s' NOT FOUND\n", fileName);
1612 continue;
1613 }
1614
1615 result = zip->remove(entry);
1616
1617 if (result != NO_ERROR) {
1618 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
1619 bundle->getFileSpecEntry(i), zipFileName);
1620 goto bail;
1621 }
1622 }
1623
1624 /* update the archive */
1625 zip->flush();
1626
1627bail:
1628 delete zip;
1629 return (result != NO_ERROR);
1630}
1631
1632
1633/*
1634 * Package up an asset directory and associated application files.
1635 */
1636int doPackage(Bundle* bundle)
1637{
1638 const char* outputAPKFile;
1639 int retVal = 1;
1640 status_t err;
1641 sp<AaptAssets> assets;
1642 int N;
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001643 FILE* fp;
1644 String8 dependencyFile;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001645
1646 // -c zz_ZZ means do pseudolocalization
1647 ResourceFilter filter;
1648 err = filter.parse(bundle->getConfigurations());
1649 if (err != NO_ERROR) {
1650 goto bail;
1651 }
1652 if (filter.containsPseudo()) {
1653 bundle->setPseudolocalize(true);
1654 }
1655
1656 N = bundle->getFileSpecCount();
1657 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
1658 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDir() == NULL) {
1659 fprintf(stderr, "ERROR: no input files\n");
1660 goto bail;
1661 }
1662
1663 outputAPKFile = bundle->getOutputAPKFile();
1664
1665 // Make sure the filenames provided exist and are of the appropriate type.
1666 if (outputAPKFile) {
1667 FileType type;
1668 type = getFileType(outputAPKFile);
1669 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
1670 fprintf(stderr,
1671 "ERROR: output file '%s' exists but is not regular file\n",
1672 outputAPKFile);
1673 goto bail;
1674 }
1675 }
1676
1677 // Load the assets.
1678 assets = new AaptAssets();
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001679
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001680 // Set up the resource gathering in assets if we're going to generate
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001681 // dependency files. Every time we encounter a resource while slurping
1682 // the tree, we'll add it to these stores so we have full resource paths
1683 // to write to a dependency file.
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001684 if (bundle->getGenDependencies()) {
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001685 sp<FilePathStore> resPathStore = new FilePathStore;
1686 assets->setFullResPaths(resPathStore);
1687 sp<FilePathStore> assetPathStore = new FilePathStore;
1688 assets->setFullAssetPaths(assetPathStore);
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001689 }
1690
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001691 err = assets->slurpFromArgs(bundle);
1692 if (err < 0) {
1693 goto bail;
1694 }
1695
1696 if (bundle->getVerbose()) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001697 assets->print(String8());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001698 }
1699
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001700 // If they asked for any fileAs that need to be compiled, do so.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001701 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
1702 err = buildResources(bundle, assets);
1703 if (err != 0) {
1704 goto bail;
1705 }
1706 }
1707
1708 // At this point we've read everything and processed everything. From here
1709 // on out it's just writing output files.
1710 if (SourcePos::hasErrors()) {
1711 goto bail;
1712 }
1713
Dianne Hackborn1644c6d72012-02-06 15:33:21 -08001714 // Update symbols with information about which ones are needed as Java symbols.
1715 assets->applyJavaSymbols();
1716 if (SourcePos::hasErrors()) {
1717 goto bail;
1718 }
1719
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001720 // If we've been asked to generate a dependency file, do that here
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001721 if (bundle->getGenDependencies()) {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001722 // If this is the packaging step, generate the dependency file next to
1723 // the output apk (e.g. bin/resources.ap_.d)
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001724 if (outputAPKFile) {
1725 dependencyFile = String8(outputAPKFile);
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001726 // Add the .d extension to the dependency file.
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001727 dependencyFile.append(".d");
1728 } else {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001729 // Else if this is the R.java dependency generation step,
1730 // generate the dependency file in the R.java package subdirectory
1731 // e.g. gen/com/foo/app/R.java.d
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001732 dependencyFile = String8(bundle->getRClassDir());
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001733 dependencyFile.appendPath("R.java.d");
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001734 }
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001735 // Make sure we have a clean dependency file to start with
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001736 fp = fopen(dependencyFile, "w");
1737 fclose(fp);
1738 }
1739
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001740 // Write out R.java constants
Dianne Hackborn1644c6d72012-02-06 15:33:21 -08001741 if (!assets->havePrivateSymbols()) {
Xavier Ducrohet63459ad2009-11-30 18:05:10 -08001742 if (bundle->getCustomPackage() == NULL) {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001743 // Write the R.java file into the appropriate class directory
1744 // e.g. gen/com/foo/app/R.java
Xavier Ducrohet63459ad2009-11-30 18:05:10 -08001745 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
1746 } else {
1747 const String8 customPkg(bundle->getCustomPackage());
1748 err = writeResourceSymbols(bundle, assets, customPkg, true);
1749 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001750 if (err < 0) {
1751 goto bail;
1752 }
Ying Wang002f5372012-04-25 18:53:49 -07001753 // If we have library files, we're going to write our R.java file into
1754 // the appropriate class directory for those libraries as well.
1755 // e.g. gen/com/foo/app/lib/R.java
1756 if (bundle->getExtraPackages() != NULL) {
1757 // Split on colon
1758 String8 libs(bundle->getExtraPackages());
1759 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
1760 while (packageString != NULL) {
1761 // Write the R.java file out with the correct package name
1762 err = writeResourceSymbols(bundle, assets, String8(packageString), true);
1763 if (err < 0) {
1764 goto bail;
1765 }
1766 packageString = strtok(NULL, ":");
1767 }
1768 libs.unlockBuffer();
1769 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001770 } else {
1771 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
1772 if (err < 0) {
1773 goto bail;
1774 }
1775 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
1776 if (err < 0) {
1777 goto bail;
1778 }
1779 }
1780
Joe Onorato1553c822009-08-30 13:36:22 -07001781 // Write out the ProGuard file
1782 err = writeProguardFile(bundle, assets);
1783 if (err < 0) {
1784 goto bail;
1785 }
1786
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001787 // Write the apk
1788 if (outputAPKFile) {
1789 err = writeAPK(bundle, assets, String8(outputAPKFile));
1790 if (err != NO_ERROR) {
1791 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
1792 goto bail;
1793 }
1794 }
1795
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001796 // If we've been asked to generate a dependency file, we need to finish up here.
1797 // the writeResourceSymbols and writeAPK functions have already written the target
1798 // half of the dependency file, now we need to write the prerequisites. (files that
1799 // the R.java file or .ap_ file depend on)
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001800 if (bundle->getGenDependencies()) {
1801 // Now that writeResourceSymbols or writeAPK has taken care of writing
1802 // the targets to our dependency file, we'll write the prereqs
1803 fp = fopen(dependencyFile, "a+");
1804 fprintf(fp, " : ");
1805 bool includeRaw = (outputAPKFile != NULL);
1806 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001807 // Also manually add the AndroidManifeset since it's not under res/ or assets/
1808 // and therefore was not added to our pathstores during slurping
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001809 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
1810 fclose(fp);
1811 }
1812
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001813 retVal = 0;
1814bail:
1815 if (SourcePos::hasErrors()) {
1816 SourcePos::printErrors(stderr);
1817 }
1818 return retVal;
1819}
Josiah Gaskin8a39da82011-06-06 17:00:35 -07001820
1821/*
1822 * Do PNG Crunching
1823 * PRECONDITIONS
1824 * -S flag points to a source directory containing drawable* folders
1825 * -C flag points to destination directory. The folder structure in the
1826 * source directory will be mirrored to the destination (cache) directory
1827 *
1828 * POSTCONDITIONS
1829 * Destination directory will be updated to match the PNG files in
1830 * the source directory.
1831 */
1832int doCrunch(Bundle* bundle)
1833{
1834 fprintf(stdout, "Crunching PNG Files in ");
1835 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
1836 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
1837
1838 updatePreProcessedCache(bundle);
1839
1840 return NO_ERROR;
1841}
Dan Morrille74763e2012-01-06 10:47:10 -08001842
Xavier Ducrohet22d7cf22012-12-21 09:54:02 -08001843/*
1844 * Do PNG Crunching on a single flag
1845 * -i points to a single png file
1846 * -o points to a single png output file
1847 */
1848int doSingleCrunch(Bundle* bundle)
1849{
1850 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
1851 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
1852
1853 String8 input(bundle->getSingleCrunchInputFile());
1854 String8 output(bundle->getSingleCrunchOutputFile());
1855 return preProcessImageToCache(bundle, input, output);
1856}
1857
Dan Morrille74763e2012-01-06 10:47:10 -08001858char CONSOLE_DATA[2925] = {
1859 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1860 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1861 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1862 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1863 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
1864 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
1865 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1866 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1867 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
1868 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
1869 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1870 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1871 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
1872 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1873 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1874 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
1875 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
1876 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1877 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1878 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
1879 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
1880 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
1881 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
1882 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
1883 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1884 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1885 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
1886 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
1887 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
1888 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1889 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
1890 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1891 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1892 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
1893 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
1894 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1895 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1896 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
1897 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
1898 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
1899 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1900 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
1901 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1902 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1903 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
1904 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
1905 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
1906 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
1907 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
1908 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1909 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
1910 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
1911 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
1912 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1913 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
1914 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
1915 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
1916 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
1917 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
1918 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
1919 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
1920 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
1921 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
1922 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
1923 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
1924 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
1925 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
1926 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
1927 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
1928 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
1929 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
1930 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
1931 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1932 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
1933 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
1934 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
1935 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1936 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
1937 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
1938 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1939 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
1940 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
1941 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
1942 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1943 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
1944 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
1945 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1946 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
1947 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
1948 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1949 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1950 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
1951 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
1952 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1953 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1954 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
1955 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1956 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
1957 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
1958 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
1959 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1960 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1961 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
1962 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
1963 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
1964 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1965 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
1966 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1967 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1968 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
1969 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
1970 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1971 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1972 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
1973 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1974 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
1975 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
1976 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
1977 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1978 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1979 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
1980 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
1981 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
1982 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
1983 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
1984 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
1985 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
1986 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
1987 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
1988 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1989 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1990 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
1991 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
1992 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
1993 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
1994 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
1995 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1996 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1997 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
1998 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
1999 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2000 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2001 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2002 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2003 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2004 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2005 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2006 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2007 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2008 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2009 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2010 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2011 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2012 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2013 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2014 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2015 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2016 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2017 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2018 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2019 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2020 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2021 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2022 };