blob: d9e2dc5d3903cef1155a672f0ca1df6a63d4abce [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 Ducrohetb1f6ad82012-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,
Adam Lesinskib1249b82013-09-25 17:03:50 -0700349 PERMISSION_ATTR = 0x01010006,
Adam Lesinskid831e802013-09-27 11:14:57 -0700350 RESOURCE_ATTR = 0x01010025,
Dan Morrillb6ec11e2012-04-03 12:44:40 -0700351 DEBUGGABLE_ATTR = 0x0101000f,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800352 VERSION_CODE_ATTR = 0x0101021b,
353 VERSION_NAME_ATTR = 0x0101021c,
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -0700354 SCREEN_ORIENTATION_ATTR = 0x0101001e,
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700355 MIN_SDK_VERSION_ATTR = 0x0101020c,
Suchi Amalapurapu75c49842009-08-14 15:13:09 -0700356 MAX_SDK_VERSION_ATTR = 0x01010271,
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700357 REQ_TOUCH_SCREEN_ATTR = 0x01010227,
358 REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
359 REQ_HARD_KEYBOARD_ATTR = 0x01010229,
360 REQ_NAVIGATION_ATTR = 0x0101022a,
361 REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
362 TARGET_SDK_VERSION_ATTR = 0x01010270,
363 TEST_ONLY_ATTR = 0x01010272,
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700364 ANY_DENSITY_ATTR = 0x0101026c,
Dianne Hackborne5276a72009-08-27 16:28:44 -0700365 GL_ES_VERSION_ATTR = 0x01010281,
Dianne Hackborn723738c2009-06-25 19:48:04 -0700366 SMALL_SCREEN_ATTR = 0x01010284,
367 NORMAL_SCREEN_ATTR = 0x01010285,
368 LARGE_SCREEN_ATTR = 0x01010286,
Dianne Hackbornf43489d2010-08-20 12:44:33 -0700369 XLARGE_SCREEN_ATTR = 0x010102bf,
Dianne Hackborne5276a72009-08-27 16:28:44 -0700370 REQUIRED_ATTR = 0x0101028e,
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700371 SCREEN_SIZE_ATTR = 0x010102ca,
372 SCREEN_DENSITY_ATTR = 0x010102cb,
Dianne Hackborne289bff2011-06-13 19:33:22 -0700373 REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
374 COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
375 LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
Kenny Root56088a52011-09-29 13:49:45 -0700376 PUBLIC_KEY_ATTR = 0x010103a6,
Adam Lesinskid831e802013-09-27 11:14:57 -0700377 CATEGORY_ATTR = 0x010103e8,
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800378 BANNER_ATTR = 0x10103f2,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800379};
380
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700381const char *getComponentName(String8 &pkgName, String8 &componentName) {
382 ssize_t idx = componentName.find(".");
383 String8 retStr(pkgName);
384 if (idx == 0) {
385 retStr += componentName;
386 } else if (idx < 0) {
387 retStr += ".";
388 retStr += componentName;
389 } else {
390 return componentName.string();
391 }
392 return retStr.string();
393}
394
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700395static void printCompatibleScreens(ResXMLTree& tree) {
396 size_t len;
397 ResXMLTree::event_code_t code;
398 int depth = 0;
399 bool first = true;
400 printf("compatible-screens:");
401 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
402 if (code == ResXMLTree::END_TAG) {
403 depth--;
404 if (depth < 0) {
405 break;
406 }
407 continue;
408 }
409 if (code != ResXMLTree::START_TAG) {
410 continue;
411 }
412 depth++;
413 String8 tag(tree.getElementName(&len));
414 if (tag == "screen") {
415 int32_t screenSize = getIntegerAttribute(tree,
416 SCREEN_SIZE_ATTR, NULL, -1);
417 int32_t screenDensity = getIntegerAttribute(tree,
418 SCREEN_DENSITY_ATTR, NULL, -1);
419 if (screenSize > 0 && screenDensity > 0) {
420 if (!first) {
421 printf(",");
422 }
423 first = false;
424 printf("'%d/%d'", screenSize, screenDensity);
425 }
426 }
427 }
428 printf("\n");
429}
430
Adam Lesinskid831e802013-09-27 11:14:57 -0700431Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool offHost,
432 String8 *outError = NULL)
433{
434 Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER);
435 if (aidAsset == NULL) {
436 if (outError != NULL) *outError = "xml resource does not exist";
437 return Vector<String8>();
438 }
439
440 const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service");
441
442 bool withinApduService = false;
443 Vector<String8> categories;
444
445 String8 error;
446 ResXMLTree tree;
447 tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength());
448
449 size_t len;
450 int depth = 0;
451 ResXMLTree::event_code_t code;
452 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
453 if (code == ResXMLTree::END_TAG) {
454 depth--;
455 String8 tag(tree.getElementName(&len));
456
457 if (depth == 0 && tag == serviceTagName) {
458 withinApduService = false;
459 }
460
461 } else if (code == ResXMLTree::START_TAG) {
462 depth++;
463 String8 tag(tree.getElementName(&len));
464
465 if (depth == 1) {
466 if (tag == serviceTagName) {
467 withinApduService = true;
468 }
469 } else if (depth == 2 && withinApduService) {
470 if (tag == "aid-group") {
471 String8 category = getAttribute(tree, CATEGORY_ATTR, &error);
472 if (error != "") {
473 if (outError != NULL) *outError = error;
474 return Vector<String8>();
475 }
476
477 categories.add(category);
478 }
479 }
480 }
481 }
482 aidAsset->close();
483 return categories;
484}
485
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800486/*
487 * Handle the "dump" command, to extract select data from an archive.
488 */
Dan Morrille74763e2012-01-06 10:47:10 -0800489extern char CONSOLE_DATA[2925]; // see EOF
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800490int doDump(Bundle* bundle)
491{
492 status_t result = UNKNOWN_ERROR;
493 Asset* asset = NULL;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700494
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800495 if (bundle->getFileSpecCount() < 1) {
496 fprintf(stderr, "ERROR: no dump option specified\n");
497 return 1;
498 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700499
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800500 if (bundle->getFileSpecCount() < 2) {
501 fprintf(stderr, "ERROR: no dump file specified\n");
502 return 1;
503 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700504
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800505 const char* option = bundle->getFileSpecEntry(0);
506 const char* filename = bundle->getFileSpecEntry(1);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700507
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800508 AssetManager assets;
Narayan Kamath745d4ef2014-01-27 11:17:22 +0000509 int32_t assetsCookie;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700510 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800511 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
512 return 1;
513 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700514
Dianne Hackborne289bff2011-06-13 19:33:22 -0700515 // Make a dummy config for retrieving resources... we need to supply
516 // non-default values for some configs so that we can retrieve resources
517 // in the app that don't have a default. The most important of these is
518 // the API version because key resources like icons will have an implicit
519 // version if they are using newer config types like density.
520 ResTable_config config;
521 config.language[0] = 'e';
522 config.language[1] = 'n';
523 config.country[0] = 'U';
524 config.country[1] = 'S';
525 config.orientation = ResTable_config::ORIENTATION_PORT;
526 config.density = ResTable_config::DENSITY_MEDIUM;
527 config.sdkVersion = 10000; // Very high.
528 config.screenWidthDp = 320;
529 config.screenHeightDp = 480;
530 config.smallestScreenWidthDp = 320;
531 assets.setConfiguration(config);
532
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800533 const ResTable& res = assets.getResources(false);
534 if (&res == NULL) {
535 fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
536 goto bail;
537 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700538
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800539 if (strcmp("resources", option) == 0) {
Steve Blockf1ff21a2010-06-14 17:34:04 +0100540#ifndef HAVE_ANDROID_OS
Dianne Hackborne17086b2009-06-19 15:13:28 -0700541 res.print(bundle->getValues());
Steve Blockf1ff21a2010-06-14 17:34:04 +0100542#endif
Dianne Hackborn6c997a92012-01-31 11:27:43 -0800543
544 } else if (strcmp("strings", option) == 0) {
545 const ResStringPool* pool = res.getTableStringBlock(0);
546 printStringPool(pool);
547
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800548 } else if (strcmp("xmltree", option) == 0) {
549 if (bundle->getFileSpecCount() < 3) {
550 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
551 goto bail;
552 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700553
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800554 for (int i=2; i<bundle->getFileSpecCount(); i++) {
555 const char* resname = bundle->getFileSpecEntry(i);
556 ResXMLTree tree;
557 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
558 if (asset == NULL) {
Kenny Root44b283d2009-09-01 19:03:11 -0500559 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800560 goto bail;
561 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700562
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800563 if (tree.setTo(asset->getBuffer(true),
564 asset->getLength()) != NO_ERROR) {
565 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
566 goto bail;
567 }
568 tree.restart();
569 printXMLBlock(&tree);
Kenny Root19138462009-12-04 09:38:48 -0800570 tree.uninit();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800571 delete asset;
572 asset = NULL;
573 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700574
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800575 } else if (strcmp("xmlstrings", option) == 0) {
576 if (bundle->getFileSpecCount() < 3) {
577 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
578 goto bail;
579 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700580
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800581 for (int i=2; i<bundle->getFileSpecCount(); i++) {
582 const char* resname = bundle->getFileSpecEntry(i);
583 ResXMLTree tree;
584 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
585 if (asset == NULL) {
Kenny Root44b283d2009-09-01 19:03:11 -0500586 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800587 goto bail;
588 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700589
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800590 if (tree.setTo(asset->getBuffer(true),
591 asset->getLength()) != NO_ERROR) {
592 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
593 goto bail;
594 }
595 printStringPool(&tree.getStrings());
596 delete asset;
597 asset = NULL;
598 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700599
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800600 } else {
601 ResXMLTree tree;
602 asset = assets.openNonAsset("AndroidManifest.xml",
603 Asset::ACCESS_BUFFER);
604 if (asset == NULL) {
605 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
606 goto bail;
607 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700608
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800609 if (tree.setTo(asset->getBuffer(true),
610 asset->getLength()) != NO_ERROR) {
611 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
612 goto bail;
613 }
614 tree.restart();
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700615
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800616 if (strcmp("permissions", option) == 0) {
617 size_t len;
618 ResXMLTree::event_code_t code;
619 int depth = 0;
620 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
621 if (code == ResXMLTree::END_TAG) {
622 depth--;
623 continue;
624 }
625 if (code != ResXMLTree::START_TAG) {
626 continue;
627 }
628 depth++;
629 String8 tag(tree.getElementName(&len));
630 //printf("Depth %d tag %s\n", depth, tag.string());
631 if (depth == 1) {
632 if (tag != "manifest") {
633 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
634 goto bail;
635 }
636 String8 pkg = getAttribute(tree, NULL, "package", NULL);
637 printf("package: %s\n", pkg.string());
638 } else if (depth == 2 && tag == "permission") {
639 String8 error;
640 String8 name = getAttribute(tree, NAME_ATTR, &error);
641 if (error != "") {
642 fprintf(stderr, "ERROR: %s\n", error.string());
643 goto bail;
644 }
645 printf("permission: %s\n", name.string());
646 } else if (depth == 2 && tag == "uses-permission") {
647 String8 error;
648 String8 name = getAttribute(tree, NAME_ATTR, &error);
649 if (error != "") {
650 fprintf(stderr, "ERROR: %s\n", error.string());
651 goto bail;
652 }
653 printf("uses-permission: %s\n", name.string());
Nick Kralevich1bcc3d62013-04-03 09:00:02 -0700654 int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
655 if (!req) {
656 printf("optional-permission: %s\n", name.string());
657 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800658 }
659 }
660 } else if (strcmp("badging", option) == 0) {
Dianne Hackborne289bff2011-06-13 19:33:22 -0700661 Vector<String8> locales;
662 res.getLocales(&locales);
663
664 Vector<ResTable_config> configs;
665 res.getConfigurations(&configs);
666 SortedVector<int> densities;
667 const size_t NC = configs.size();
668 for (size_t i=0; i<NC; i++) {
669 int dens = configs[i].density;
670 if (dens == 0) dens = 160;
671 densities.add(dens);
672 }
673
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800674 size_t len;
675 ResXMLTree::event_code_t code;
676 int depth = 0;
677 String8 error;
678 bool withinActivity = false;
679 bool isMainActivity = false;
680 bool isLauncherActivity = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800681 bool isLeanbackLauncherActivity = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700682 bool isSearchable = false;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700683 bool withinApplication = false;
Michael Wrighteaeb1902013-09-05 18:15:57 -0700684 bool withinSupportsInput = false;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700685 bool withinReceiver = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700686 bool withinService = false;
687 bool withinIntentFilter = false;
688 bool hasMainActivity = false;
689 bool hasOtherActivities = false;
690 bool hasOtherReceivers = false;
691 bool hasOtherServices = false;
692 bool hasWallpaperService = false;
693 bool hasImeService = false;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700694 bool hasAccessibilityService = false;
695 bool hasPrintService = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700696 bool hasWidgetReceivers = false;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700697 bool hasDeviceAdminReceiver = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700698 bool hasIntentFilter = false;
Adam Lesinskid831e802013-09-27 11:14:57 -0700699 bool hasPaymentService = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700700 bool actMainActivity = false;
701 bool actWidgetReceivers = false;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700702 bool actDeviceAdminEnabled = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700703 bool actImeService = false;
704 bool actWallpaperService = false;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700705 bool actAccessibilityService = false;
706 bool actPrintService = false;
Adam Lesinskid831e802013-09-27 11:14:57 -0700707 bool actHostApduService = false;
708 bool actOffHostApduService = false;
709 bool hasMetaHostPaymentCategory = false;
710 bool hasMetaOffHostPaymentCategory = false;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700711
712 // These permissions are required by services implementing services
713 // the system binds to (IME, Accessibility, PrintServices, etc.)
714 bool hasBindDeviceAdminPermission = false;
715 bool hasBindInputMethodPermission = false;
716 bool hasBindAccessibilityServicePermission = false;
717 bool hasBindPrintServicePermission = false;
Adam Lesinskid831e802013-09-27 11:14:57 -0700718 bool hasBindNfcServicePermission = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700719
Kenny Root063a44e2011-12-08 08:46:03 -0800720 // These two implement the implicit permissions that are granted
721 // to pre-1.6 applications.
722 bool hasWriteExternalStoragePermission = false;
723 bool hasReadPhoneStatePermission = false;
724
Dianne Hackborn79245122012-03-12 10:51:26 -0700725 // If an app requests write storage, they will also get read storage.
726 bool hasReadExternalStoragePermission = false;
727
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -0700728 // Implement transition to read and write call log.
729 bool hasReadContactsPermission = false;
730 bool hasWriteContactsPermission = false;
731 bool hasReadCallLogPermission = false;
732 bool hasWriteCallLogPermission = false;
733
Dan Morrill89d97c12010-05-03 16:13:14 -0700734 // This next group of variables is used to implement a group of
735 // backward-compatibility heuristics necessitated by the addition of
736 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
737 // heuristic is "if an app requests a permission but doesn't explicitly
738 // request the corresponding <uses-feature>, presume it's there anyway".
739 bool specCameraFeature = false; // camera-related
740 bool specCameraAutofocusFeature = false;
741 bool reqCameraAutofocusFeature = false;
742 bool reqCameraFlashFeature = false;
Dianne Hackborne5276a72009-08-27 16:28:44 -0700743 bool hasCameraPermission = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700744 bool specLocationFeature = false; // location-related
745 bool specNetworkLocFeature = false;
746 bool reqNetworkLocFeature = false;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800747 bool specGpsFeature = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700748 bool reqGpsFeature = false;
749 bool hasMockLocPermission = false;
750 bool hasCoarseLocPermission = false;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800751 bool hasGpsPermission = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700752 bool hasGeneralLocPermission = false;
753 bool specBluetoothFeature = false; // Bluetooth API-related
754 bool hasBluetoothPermission = false;
755 bool specMicrophoneFeature = false; // microphone-related
756 bool hasRecordAudioPermission = false;
757 bool specWiFiFeature = false;
758 bool hasWiFiPermission = false;
759 bool specTelephonyFeature = false; // telephony-related
760 bool reqTelephonySubFeature = false;
761 bool hasTelephonyPermission = false;
762 bool specTouchscreenFeature = false; // touchscreen-related
763 bool specMultitouchFeature = false;
764 bool reqDistinctMultitouchFeature = false;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700765 bool specScreenPortraitFeature = false;
766 bool specScreenLandscapeFeature = false;
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -0700767 bool reqScreenPortraitFeature = false;
768 bool reqScreenLandscapeFeature = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700769 // 2.2 also added some other features that apps can request, but that
770 // have no corresponding permission, so we cannot implement any
771 // back-compatibility heuristic for them. The below are thus unnecessary
772 // (but are retained here for documentary purposes.)
773 //bool specCompassFeature = false;
774 //bool specAccelerometerFeature = false;
775 //bool specProximityFeature = false;
776 //bool specAmbientLightFeature = false;
777 //bool specLiveWallpaperFeature = false;
778
Dianne Hackborn723738c2009-06-25 19:48:04 -0700779 int targetSdk = 0;
780 int smallScreen = 1;
781 int normalScreen = 1;
782 int largeScreen = 1;
Dianne Hackbornf43489d2010-08-20 12:44:33 -0700783 int xlargeScreen = 1;
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700784 int anyDensity = 1;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700785 int requiresSmallestWidthDp = 0;
786 int compatibleWidthLimitDp = 0;
787 int largestWidthLimitDp = 0;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700788 String8 pkg;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800789 String8 activityName;
790 String8 activityLabel;
791 String8 activityIcon;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800792 String8 activityBanner;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700793 String8 receiverName;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700794 String8 serviceName;
Michael Wrighteaeb1902013-09-05 18:15:57 -0700795 Vector<String8> supportedInput;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800796 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
797 if (code == ResXMLTree::END_TAG) {
798 depth--;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700799 if (depth < 2) {
Michael Wrighteaeb1902013-09-05 18:15:57 -0700800 if (withinSupportsInput && !supportedInput.isEmpty()) {
801 printf("supports-input: '");
802 const size_t N = supportedInput.size();
803 for (size_t i=0; i<N; i++) {
804 printf("%s", supportedInput[i].string());
805 if (i != N - 1) {
806 printf("' '");
807 } else {
808 printf("'\n");
809 }
810 }
811 supportedInput.clear();
812 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700813 withinApplication = false;
Michael Wrighteaeb1902013-09-05 18:15:57 -0700814 withinSupportsInput = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700815 } else if (depth < 3) {
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800816 if (withinActivity && isMainActivity) {
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700817 const char *aName = getComponentName(pkg, activityName);
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800818 if (isLauncherActivity) {
819 printf("launchable-activity:");
820 if (aName != NULL) {
821 printf(" name='%s' ", aName);
822 }
823 printf(" label='%s' icon='%s'\n",
824 activityLabel.string(),
825 activityIcon.string());
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700826 }
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800827 if (isLeanbackLauncherActivity) {
828 printf("leanback-launchable-activity:");
829 if (aName != NULL) {
830 printf(" name='%s' ", aName);
831 }
832 printf(" label='%s' icon='%s' banner='%s'\n",
833 activityLabel.string(),
834 activityIcon.string(),
835 activityBanner.string());
836 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700837 }
838 if (!hasIntentFilter) {
839 hasOtherActivities |= withinActivity;
840 hasOtherReceivers |= withinReceiver;
841 hasOtherServices |= withinService;
Adam Lesinskid831e802013-09-27 11:14:57 -0700842 } else {
843 if (withinService) {
844 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
845 hasBindNfcServicePermission);
846 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
847 hasBindNfcServicePermission);
848 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700849 }
850 withinActivity = false;
851 withinService = false;
852 withinReceiver = false;
853 hasIntentFilter = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800854 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700855 } else if (depth < 4) {
856 if (withinIntentFilter) {
857 if (withinActivity) {
858 hasMainActivity |= actMainActivity;
859 hasOtherActivities |= !actMainActivity;
860 } else if (withinReceiver) {
861 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700862 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
863 hasBindDeviceAdminPermission);
864 hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled);
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700865 } else if (withinService) {
866 hasImeService |= actImeService;
867 hasWallpaperService |= actWallpaperService;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700868 hasAccessibilityService |= (actAccessibilityService &&
869 hasBindAccessibilityServicePermission);
870 hasPrintService |= (actPrintService && hasBindPrintServicePermission);
871 hasOtherServices |= (!actImeService && !actWallpaperService &&
Adam Lesinskid831e802013-09-27 11:14:57 -0700872 !actAccessibilityService && !actPrintService &&
873 !actHostApduService && !actOffHostApduService);
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700874 }
875 }
876 withinIntentFilter = false;
877 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800878 continue;
879 }
880 if (code != ResXMLTree::START_TAG) {
881 continue;
882 }
883 depth++;
884 String8 tag(tree.getElementName(&len));
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700885 //printf("Depth %d, %s\n", depth, tag.string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800886 if (depth == 1) {
887 if (tag != "manifest") {
888 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
889 goto bail;
890 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700891 pkg = getAttribute(tree, NULL, "package", NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800892 printf("package: name='%s' ", pkg.string());
893 int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
894 if (error != "") {
895 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
896 goto bail;
897 }
898 if (versionCode > 0) {
899 printf("versionCode='%d' ", versionCode);
900 } else {
901 printf("versionCode='' ");
902 }
Dianne Hackborncf244ad2010-03-09 15:00:30 -0800903 String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800904 if (error != "") {
905 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
906 goto bail;
907 }
908 printf("versionName='%s'\n", versionName.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700909 } else if (depth == 2) {
910 withinApplication = false;
911 if (tag == "application") {
912 withinApplication = true;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700913
914 String8 label;
915 const size_t NL = locales.size();
916 for (size_t i=0; i<NL; i++) {
917 const char* localeStr = locales[i].string();
918 assets.setLocale(localeStr != NULL ? localeStr : "");
919 String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
920 if (llabel != "") {
921 if (localeStr == NULL || strlen(localeStr) == 0) {
922 label = llabel;
923 printf("application-label:'%s'\n", llabel.string());
924 } else {
925 if (label == "") {
926 label = llabel;
927 }
928 printf("application-label-%s:'%s'\n", localeStr,
929 llabel.string());
930 }
931 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700932 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700933
934 ResTable_config tmpConfig = config;
935 const size_t ND = densities.size();
936 for (size_t i=0; i<ND; i++) {
937 tmpConfig.density = densities[i];
938 assets.setConfiguration(tmpConfig);
939 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
940 if (icon != "") {
941 printf("application-icon-%d:'%s'\n", densities[i], icon.string());
942 }
943 }
944 assets.setConfiguration(config);
945
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700946 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
947 if (error != "") {
948 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
949 goto bail;
950 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700951 int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700952 if (error != "") {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700953 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700954 goto bail;
955 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700956 printf("application: label='%s' ", label.string());
957 printf("icon='%s'\n", icon.string());
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700958 if (testOnly != 0) {
959 printf("testOnly='%d'\n", testOnly);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700960 }
Dan Morrillb6ec11e2012-04-03 12:44:40 -0700961
962 int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0);
963 if (error != "") {
964 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string());
965 goto bail;
966 }
967 if (debuggable != 0) {
968 printf("application-debuggable\n");
969 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700970 } else if (tag == "uses-sdk") {
971 int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
972 if (error != "") {
973 error = "";
974 String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
975 if (error != "") {
976 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
977 error.string());
978 goto bail;
979 }
Dianne Hackborn723738c2009-06-25 19:48:04 -0700980 if (name == "Donut") targetSdk = 4;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700981 printf("sdkVersion:'%s'\n", name.string());
982 } else if (code != -1) {
Dianne Hackborn723738c2009-06-25 19:48:04 -0700983 targetSdk = code;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700984 printf("sdkVersion:'%d'\n", code);
985 }
Suchi Amalapurapu75c49842009-08-14 15:13:09 -0700986 code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
987 if (code != -1) {
988 printf("maxSdkVersion:'%d'\n", code);
989 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700990 code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
991 if (error != "") {
992 error = "";
993 String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
994 if (error != "") {
995 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
996 error.string());
997 goto bail;
998 }
Dianne Hackborn723738c2009-06-25 19:48:04 -0700999 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001000 printf("targetSdkVersion:'%s'\n", name.string());
1001 } else if (code != -1) {
Dianne Hackborn723738c2009-06-25 19:48:04 -07001002 if (targetSdk < code) {
1003 targetSdk = code;
1004 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001005 printf("targetSdkVersion:'%d'\n", code);
1006 }
1007 } else if (tag == "uses-configuration") {
1008 int32_t reqTouchScreen = getIntegerAttribute(tree,
1009 REQ_TOUCH_SCREEN_ATTR, NULL, 0);
1010 int32_t reqKeyboardType = getIntegerAttribute(tree,
1011 REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
1012 int32_t reqHardKeyboard = getIntegerAttribute(tree,
1013 REQ_HARD_KEYBOARD_ATTR, NULL, 0);
1014 int32_t reqNavigation = getIntegerAttribute(tree,
1015 REQ_NAVIGATION_ATTR, NULL, 0);
1016 int32_t reqFiveWayNav = getIntegerAttribute(tree,
1017 REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
Dianne Hackborncb2d50d2010-01-06 11:29:54 -08001018 printf("uses-configuration:");
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001019 if (reqTouchScreen != 0) {
1020 printf(" reqTouchScreen='%d'", reqTouchScreen);
1021 }
1022 if (reqKeyboardType != 0) {
1023 printf(" reqKeyboardType='%d'", reqKeyboardType);
1024 }
1025 if (reqHardKeyboard != 0) {
1026 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1027 }
1028 if (reqNavigation != 0) {
1029 printf(" reqNavigation='%d'", reqNavigation);
1030 }
1031 if (reqFiveWayNav != 0) {
1032 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1033 }
1034 printf("\n");
Michael Wrighteaeb1902013-09-05 18:15:57 -07001035 } else if (tag == "supports-input") {
1036 withinSupportsInput = true;
Dianne Hackborn723738c2009-06-25 19:48:04 -07001037 } else if (tag == "supports-screens") {
1038 smallScreen = getIntegerAttribute(tree,
1039 SMALL_SCREEN_ATTR, NULL, 1);
1040 normalScreen = getIntegerAttribute(tree,
1041 NORMAL_SCREEN_ATTR, NULL, 1);
1042 largeScreen = getIntegerAttribute(tree,
1043 LARGE_SCREEN_ATTR, NULL, 1);
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001044 xlargeScreen = getIntegerAttribute(tree,
1045 XLARGE_SCREEN_ATTR, NULL, 1);
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001046 anyDensity = getIntegerAttribute(tree,
1047 ANY_DENSITY_ATTR, NULL, 1);
Dianne Hackborne289bff2011-06-13 19:33:22 -07001048 requiresSmallestWidthDp = getIntegerAttribute(tree,
1049 REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
1050 compatibleWidthLimitDp = getIntegerAttribute(tree,
1051 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
1052 largestWidthLimitDp = getIntegerAttribute(tree,
1053 LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
Dianne Hackborne5276a72009-08-27 16:28:44 -07001054 } else if (tag == "uses-feature") {
1055 String8 name = getAttribute(tree, NAME_ATTR, &error);
Suchi Amalapurapu40b94722009-09-20 13:39:37 -07001056
1057 if (name != "" && error == "") {
Dianne Hackborne5276a72009-08-27 16:28:44 -07001058 int req = getIntegerAttribute(tree,
1059 REQUIRED_ATTR, NULL, 1);
Dan Morrill89d97c12010-05-03 16:13:14 -07001060
Dianne Hackborne5276a72009-08-27 16:28:44 -07001061 if (name == "android.hardware.camera") {
1062 specCameraFeature = true;
Dan Morrill89d97c12010-05-03 16:13:14 -07001063 } else if (name == "android.hardware.camera.autofocus") {
1064 // these have no corresponding permission to check for,
1065 // but should imply the foundational camera permission
1066 reqCameraAutofocusFeature = reqCameraAutofocusFeature || req;
1067 specCameraAutofocusFeature = true;
1068 } else if (req && (name == "android.hardware.camera.flash")) {
1069 // these have no corresponding permission to check for,
1070 // but should imply the foundational camera permission
1071 reqCameraFlashFeature = true;
1072 } else if (name == "android.hardware.location") {
1073 specLocationFeature = true;
1074 } else if (name == "android.hardware.location.network") {
1075 specNetworkLocFeature = true;
1076 reqNetworkLocFeature = reqNetworkLocFeature || req;
Dianne Hackbornef05e072010-03-01 17:43:39 -08001077 } else if (name == "android.hardware.location.gps") {
1078 specGpsFeature = true;
Dan Morrill89d97c12010-05-03 16:13:14 -07001079 reqGpsFeature = reqGpsFeature || req;
1080 } else if (name == "android.hardware.bluetooth") {
1081 specBluetoothFeature = true;
1082 } else if (name == "android.hardware.touchscreen") {
1083 specTouchscreenFeature = true;
1084 } else if (name == "android.hardware.touchscreen.multitouch") {
1085 specMultitouchFeature = true;
1086 } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
1087 reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req;
1088 } else if (name == "android.hardware.microphone") {
1089 specMicrophoneFeature = true;
1090 } else if (name == "android.hardware.wifi") {
1091 specWiFiFeature = true;
1092 } else if (name == "android.hardware.telephony") {
1093 specTelephonyFeature = true;
1094 } else if (req && (name == "android.hardware.telephony.gsm" ||
1095 name == "android.hardware.telephony.cdma")) {
1096 // these have no corresponding permission to check for,
1097 // but should imply the foundational telephony permission
1098 reqTelephonySubFeature = true;
Dianne Hackborne289bff2011-06-13 19:33:22 -07001099 } else if (name == "android.hardware.screen.portrait") {
1100 specScreenPortraitFeature = true;
1101 } else if (name == "android.hardware.screen.landscape") {
1102 specScreenLandscapeFeature = true;
Dianne Hackborne5276a72009-08-27 16:28:44 -07001103 }
1104 printf("uses-feature%s:'%s'\n",
1105 req ? "" : "-not-required", name.string());
1106 } else {
1107 int vers = getIntegerAttribute(tree,
1108 GL_ES_VERSION_ATTR, &error);
1109 if (error == "") {
1110 printf("uses-gl-es:'0x%x'\n", vers);
1111 }
1112 }
1113 } else if (tag == "uses-permission") {
1114 String8 name = getAttribute(tree, NAME_ATTR, &error);
Suchi Amalapurapu40b94722009-09-20 13:39:37 -07001115 if (name != "" && error == "") {
Dianne Hackborne5276a72009-08-27 16:28:44 -07001116 if (name == "android.permission.CAMERA") {
1117 hasCameraPermission = true;
Dianne Hackbornef05e072010-03-01 17:43:39 -08001118 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
1119 hasGpsPermission = true;
Dan Morrill89d97c12010-05-03 16:13:14 -07001120 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
1121 hasMockLocPermission = true;
1122 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
1123 hasCoarseLocPermission = true;
1124 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
1125 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
1126 hasGeneralLocPermission = true;
1127 } else if (name == "android.permission.BLUETOOTH" ||
1128 name == "android.permission.BLUETOOTH_ADMIN") {
1129 hasBluetoothPermission = true;
1130 } else if (name == "android.permission.RECORD_AUDIO") {
1131 hasRecordAudioPermission = true;
1132 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1133 name == "android.permission.CHANGE_WIFI_STATE" ||
1134 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
1135 hasWiFiPermission = true;
1136 } else if (name == "android.permission.CALL_PHONE" ||
1137 name == "android.permission.CALL_PRIVILEGED" ||
1138 name == "android.permission.MODIFY_PHONE_STATE" ||
1139 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1140 name == "android.permission.READ_SMS" ||
1141 name == "android.permission.RECEIVE_SMS" ||
1142 name == "android.permission.RECEIVE_MMS" ||
1143 name == "android.permission.RECEIVE_WAP_PUSH" ||
1144 name == "android.permission.SEND_SMS" ||
1145 name == "android.permission.WRITE_APN_SETTINGS" ||
1146 name == "android.permission.WRITE_SMS") {
1147 hasTelephonyPermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001148 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1149 hasWriteExternalStoragePermission = true;
Dianne Hackborn79245122012-03-12 10:51:26 -07001150 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1151 hasReadExternalStoragePermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001152 } else if (name == "android.permission.READ_PHONE_STATE") {
1153 hasReadPhoneStatePermission = true;
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001154 } else if (name == "android.permission.READ_CONTACTS") {
1155 hasReadContactsPermission = true;
1156 } else if (name == "android.permission.WRITE_CONTACTS") {
1157 hasWriteContactsPermission = true;
1158 } else if (name == "android.permission.READ_CALL_LOG") {
1159 hasReadCallLogPermission = true;
1160 } else if (name == "android.permission.WRITE_CALL_LOG") {
1161 hasWriteCallLogPermission = true;
Dianne Hackborne5276a72009-08-27 16:28:44 -07001162 }
1163 printf("uses-permission:'%s'\n", name.string());
Nick Kralevich1bcc3d62013-04-03 09:00:02 -07001164 int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
1165 if (!req) {
1166 printf("optional-permission:'%s'\n", name.string());
1167 }
Dianne Hackborne5276a72009-08-27 16:28:44 -07001168 } else {
1169 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1170 error.string());
1171 goto bail;
1172 }
Dianne Hackborn43b68032010-09-02 17:14:41 -07001173 } else if (tag == "uses-package") {
1174 String8 name = getAttribute(tree, NAME_ATTR, &error);
1175 if (name != "" && error == "") {
1176 printf("uses-package:'%s'\n", name.string());
1177 } else {
1178 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1179 error.string());
1180 goto bail;
1181 }
Jeff Hamiltone2c17f92010-02-12 13:45:16 -06001182 } else if (tag == "original-package") {
1183 String8 name = getAttribute(tree, NAME_ATTR, &error);
1184 if (name != "" && error == "") {
1185 printf("original-package:'%s'\n", name.string());
1186 } else {
1187 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1188 error.string());
1189 goto bail;
1190 }
Dan Morrill096b67f2010-12-13 16:25:54 -08001191 } else if (tag == "supports-gl-texture") {
Dan Morrill6f51fc12010-10-13 14:33:43 -07001192 String8 name = getAttribute(tree, NAME_ATTR, &error);
1193 if (name != "" && error == "") {
Dan Morrill096b67f2010-12-13 16:25:54 -08001194 printf("supports-gl-texture:'%s'\n", name.string());
Dan Morrill6f51fc12010-10-13 14:33:43 -07001195 } else {
1196 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1197 error.string());
1198 goto bail;
1199 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001200 } else if (tag == "compatible-screens") {
1201 printCompatibleScreens(tree);
1202 depth--;
Kenny Root56088a52011-09-29 13:49:45 -07001203 } else if (tag == "package-verifier") {
1204 String8 name = getAttribute(tree, NAME_ATTR, &error);
1205 if (name != "" && error == "") {
1206 String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
1207 if (publicKey != "" && error == "") {
1208 printf("package-verifier: name='%s' publicKey='%s'\n",
1209 name.string(), publicKey.string());
1210 }
1211 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001212 }
Michael Wrighteaeb1902013-09-05 18:15:57 -07001213 } else if (depth == 3) {
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001214 withinActivity = false;
1215 withinReceiver = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001216 withinService = false;
1217 hasIntentFilter = false;
Adam Lesinskid831e802013-09-27 11:14:57 -07001218 hasMetaHostPaymentCategory = false;
1219 hasMetaOffHostPaymentCategory = false;
1220 hasBindDeviceAdminPermission = false;
1221 hasBindInputMethodPermission = false;
1222 hasBindAccessibilityServicePermission = false;
1223 hasBindPrintServicePermission = false;
1224 hasBindNfcServicePermission = false;
Michael Wrighteaeb1902013-09-05 18:15:57 -07001225 if (withinApplication) {
1226 if(tag == "activity") {
1227 withinActivity = true;
1228 activityName = getAttribute(tree, NAME_ATTR, &error);
1229 if (error != "") {
1230 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1231 error.string());
1232 goto bail;
1233 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001234
Michael Wrighteaeb1902013-09-05 18:15:57 -07001235 activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1236 if (error != "") {
1237 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
1238 error.string());
1239 goto bail;
1240 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001241
Michael Wrighteaeb1902013-09-05 18:15:57 -07001242 activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1243 if (error != "") {
1244 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1245 error.string());
1246 goto bail;
1247 }
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001248
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001249 activityBanner = getResolvedAttribute(&res, tree, BANNER_ATTR, &error);
1250 if (error != "") {
1251 fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
1252 error.string());
1253 goto bail;
1254 }
1255
Michael Wrighteaeb1902013-09-05 18:15:57 -07001256 int32_t orien = getResolvedIntegerAttribute(&res, tree,
1257 SCREEN_ORIENTATION_ATTR, &error);
1258 if (error == "") {
1259 if (orien == 0 || orien == 6 || orien == 8) {
1260 // Requests landscape, sensorLandscape, or reverseLandscape.
1261 reqScreenLandscapeFeature = true;
1262 } else if (orien == 1 || orien == 7 || orien == 9) {
1263 // Requests portrait, sensorPortrait, or reversePortrait.
1264 reqScreenPortraitFeature = true;
1265 }
1266 }
1267 } else if (tag == "uses-library") {
1268 String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1269 if (error != "") {
1270 fprintf(stderr,
1271 "ERROR getting 'android:name' attribute for uses-library"
1272 " %s\n", error.string());
1273 goto bail;
1274 }
1275 int req = getIntegerAttribute(tree,
1276 REQUIRED_ATTR, NULL, 1);
1277 printf("uses-library%s:'%s'\n",
1278 req ? "" : "-not-required", libraryName.string());
1279 } else if (tag == "receiver") {
1280 withinReceiver = true;
1281 receiverName = getAttribute(tree, NAME_ATTR, &error);
1282
1283 if (error != "") {
1284 fprintf(stderr,
1285 "ERROR getting 'android:name' attribute for receiver:"
1286 " %s\n", error.string());
1287 goto bail;
1288 }
Adam Lesinskib1249b82013-09-25 17:03:50 -07001289
1290 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1291 if (error == "") {
1292 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1293 hasBindDeviceAdminPermission = true;
1294 }
1295 } else {
1296 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1297 " receiver '%s': %s\n", receiverName.string(), error.string());
1298 }
Michael Wrighteaeb1902013-09-05 18:15:57 -07001299 } else if (tag == "service") {
1300 withinService = true;
1301 serviceName = getAttribute(tree, NAME_ATTR, &error);
1302
1303 if (error != "") {
1304 fprintf(stderr, "ERROR getting 'android:name' attribute for"
1305 " service: %s\n", error.string());
1306 goto bail;
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001307 }
Adam Lesinskib1249b82013-09-25 17:03:50 -07001308
1309 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1310 if (error == "") {
1311 if (permission == "android.permission.BIND_INPUT_METHOD") {
1312 hasBindInputMethodPermission = true;
1313 } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") {
1314 hasBindAccessibilityServicePermission = true;
1315 } else if (permission == "android.permission.BIND_PRINT_SERVICE") {
1316 hasBindPrintServicePermission = true;
Adam Lesinskid831e802013-09-27 11:14:57 -07001317 } else if (permission == "android.permission.BIND_NFC_SERVICE") {
1318 hasBindNfcServicePermission = true;
Adam Lesinskib1249b82013-09-25 17:03:50 -07001319 }
1320 } else {
1321 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1322 " service '%s': %s\n", serviceName.string(), error.string());
1323 }
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001324 }
Michael Wrighteaeb1902013-09-05 18:15:57 -07001325 } else if (withinSupportsInput && tag == "input-type") {
1326 String8 name = getAttribute(tree, NAME_ATTR, &error);
1327 if (name != "" && error == "") {
1328 supportedInput.add(name);
1329 } else {
1330 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1331 error.string());
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001332 goto bail;
1333 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001334 }
Adam Lesinskid831e802013-09-27 11:14:57 -07001335 } else if (depth == 4) {
1336 if (tag == "intent-filter") {
1337 hasIntentFilter = true;
1338 withinIntentFilter = true;
1339 actMainActivity = false;
1340 actWidgetReceivers = false;
1341 actImeService = false;
1342 actWallpaperService = false;
1343 actAccessibilityService = false;
1344 actPrintService = false;
1345 actDeviceAdminEnabled = false;
1346 actHostApduService = false;
1347 actOffHostApduService = false;
1348 } else if (withinService && tag == "meta-data") {
1349 String8 name = getAttribute(tree, NAME_ATTR, &error);
1350 if (error != "") {
1351 fprintf(stderr, "ERROR getting 'android:name' attribute for"
1352 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1353 goto bail;
1354 }
1355
1356 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1357 name == "android.nfc.cardemulation.off_host_apdu_service") {
1358 bool offHost = true;
1359 if (name == "android.nfc.cardemulation.host_apdu_service") {
1360 offHost = false;
1361 }
1362
1363 String8 xmlPath = getResolvedAttribute(&res, tree, RESOURCE_ATTR, &error);
1364 if (error != "") {
1365 fprintf(stderr, "ERROR getting 'android:resource' attribute for"
1366 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1367 goto bail;
1368 }
1369
1370 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1371 offHost, &error);
1372 if (error != "") {
1373 fprintf(stderr, "ERROR getting AID category for service '%s'\n",
1374 serviceName.string());
1375 goto bail;
1376 }
1377
1378 const size_t catLen = categories.size();
1379 for (size_t i = 0; i < catLen; i++) {
1380 bool paymentCategory = (categories[i] == "payment");
1381 if (offHost) {
1382 hasMetaOffHostPaymentCategory |= paymentCategory;
1383 } else {
1384 hasMetaHostPaymentCategory |= paymentCategory;
1385 }
1386 }
1387 }
1388 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001389 } else if ((depth == 5) && withinIntentFilter){
1390 String8 action;
1391 if (tag == "action") {
1392 action = getAttribute(tree, NAME_ATTR, &error);
1393 if (error != "") {
1394 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1395 goto bail;
1396 }
1397 if (withinActivity) {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001398 if (action == "android.intent.action.MAIN") {
1399 isMainActivity = true;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001400 actMainActivity = true;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001401 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001402 } else if (withinReceiver) {
1403 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1404 actWidgetReceivers = true;
Adam Lesinskib1249b82013-09-25 17:03:50 -07001405 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1406 actDeviceAdminEnabled = true;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001407 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001408 } else if (withinService) {
1409 if (action == "android.view.InputMethod") {
1410 actImeService = true;
1411 } else if (action == "android.service.wallpaper.WallpaperService") {
1412 actWallpaperService = true;
Adam Lesinskib1249b82013-09-25 17:03:50 -07001413 } else if (action == "android.accessibilityservice.AccessibilityService") {
1414 actAccessibilityService = true;
1415 } else if (action == "android.printservice.PrintService") {
1416 actPrintService = true;
Adam Lesinskid831e802013-09-27 11:14:57 -07001417 } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
1418 actHostApduService = true;
1419 } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
1420 actOffHostApduService = true;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001421 }
1422 }
1423 if (action == "android.intent.action.SEARCH") {
1424 isSearchable = true;
1425 }
1426 }
1427
1428 if (tag == "category") {
1429 String8 category = getAttribute(tree, NAME_ATTR, &error);
1430 if (error != "") {
1431 fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string());
1432 goto bail;
1433 }
1434 if (withinActivity) {
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001435 if (category == "android.intent.category.LAUNCHER") {
1436 isLauncherActivity = true;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001437 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
1438 isLeanbackLauncherActivity = true;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001439 }
1440 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001441 }
1442 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001443 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001444
Kenny Root063a44e2011-12-08 08:46:03 -08001445 // Pre-1.6 implicitly granted permission compatibility logic
1446 if (targetSdk < 4) {
1447 if (!hasWriteExternalStoragePermission) {
1448 printf("uses-permission:'android.permission.WRITE_EXTERNAL_STORAGE'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001449 printf("uses-implied-permission:'android.permission.WRITE_EXTERNAL_STORAGE'," \
1450 "'targetSdkVersion < 4'\n");
Dianne Hackborn79245122012-03-12 10:51:26 -07001451 hasWriteExternalStoragePermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001452 }
1453 if (!hasReadPhoneStatePermission) {
1454 printf("uses-permission:'android.permission.READ_PHONE_STATE'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001455 printf("uses-implied-permission:'android.permission.READ_PHONE_STATE'," \
1456 "'targetSdkVersion < 4'\n");
Kenny Root063a44e2011-12-08 08:46:03 -08001457 }
1458 }
1459
Dianne Hackborn79245122012-03-12 10:51:26 -07001460 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
Dianne Hackborn2bd8d042012-06-11 12:27:05 -07001461 // force them to always take READ_EXTERNAL_STORAGE as well. We always
1462 // do this (regardless of target API version) because we can't have
1463 // an app with write permission but not read permission.
Dianne Hackborn79245122012-03-12 10:51:26 -07001464 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
1465 printf("uses-permission:'android.permission.READ_EXTERNAL_STORAGE'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001466 printf("uses-implied-permission:'android.permission.READ_EXTERNAL_STORAGE'," \
1467 "'requested WRITE_EXTERNAL_STORAGE'\n");
Dianne Hackborn79245122012-03-12 10:51:26 -07001468 }
1469
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001470 // Pre-JellyBean call log permission compatibility.
1471 if (targetSdk < 16) {
1472 if (!hasReadCallLogPermission && hasReadContactsPermission) {
1473 printf("uses-permission:'android.permission.READ_CALL_LOG'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001474 printf("uses-implied-permission:'android.permission.READ_CALL_LOG'," \
1475 "'targetSdkVersion < 16 and requested READ_CONTACTS'\n");
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001476 }
1477 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
1478 printf("uses-permission:'android.permission.WRITE_CALL_LOG'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001479 printf("uses-implied-permission:'android.permission.WRITE_CALL_LOG'," \
1480 "'targetSdkVersion < 16 and requested WRITE_CONTACTS'\n");
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001481 }
1482 }
1483
Dan Morrill89d97c12010-05-03 16:13:14 -07001484 /* The following blocks handle printing "inferred" uses-features, based
1485 * on whether related features or permissions are used by the app.
1486 * Note that the various spec*Feature variables denote whether the
1487 * relevant tag was *present* in the AndroidManfest, not that it was
1488 * present and set to true.
1489 */
1490 // Camera-related back-compatibility logic
1491 if (!specCameraFeature) {
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001492 if (reqCameraFlashFeature) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001493 // if app requested a sub-feature (autofocus or flash) and didn't
1494 // request the base camera feature, we infer that it meant to
1495 printf("uses-feature:'android.hardware.camera'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001496 printf("uses-implied-feature:'android.hardware.camera'," \
1497 "'requested android.hardware.camera.flash feature'\n");
1498 } else if (reqCameraAutofocusFeature) {
1499 // if app requested a sub-feature (autofocus or flash) and didn't
1500 // request the base camera feature, we infer that it meant to
1501 printf("uses-feature:'android.hardware.camera'\n");
1502 printf("uses-implied-feature:'android.hardware.camera'," \
1503 "'requested android.hardware.camera.autofocus feature'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001504 } else if (hasCameraPermission) {
1505 // if app wants to use camera but didn't request the feature, we infer
1506 // that it meant to, and further that it wants autofocus
1507 // (which was the 1.0 - 1.5 behavior)
1508 printf("uses-feature:'android.hardware.camera'\n");
1509 if (!specCameraAutofocusFeature) {
1510 printf("uses-feature:'android.hardware.camera.autofocus'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001511 printf("uses-implied-feature:'android.hardware.camera.autofocus'," \
1512 "'requested android.permission.CAMERA permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001513 }
1514 }
Dianne Hackborne5276a72009-08-27 16:28:44 -07001515 }
Doug Zongkerdbe7a682009-10-09 11:24:51 -07001516
Dan Morrill89d97c12010-05-03 16:13:14 -07001517 // Location-related back-compatibility logic
1518 if (!specLocationFeature &&
1519 (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission ||
1520 hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) {
1521 // if app either takes a location-related permission or requests one of the
1522 // sub-features, we infer that it also meant to request the base location feature
1523 printf("uses-feature:'android.hardware.location'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001524 printf("uses-implied-feature:'android.hardware.location'," \
1525 "'requested a location access permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001526 }
Dianne Hackbornef05e072010-03-01 17:43:39 -08001527 if (!specGpsFeature && hasGpsPermission) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001528 // if app takes GPS (FINE location) perm but does not request the GPS
1529 // feature, we infer that it meant to
Dianne Hackbornef05e072010-03-01 17:43:39 -08001530 printf("uses-feature:'android.hardware.location.gps'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001531 printf("uses-implied-feature:'android.hardware.location.gps'," \
1532 "'requested android.permission.ACCESS_FINE_LOCATION permission'\n");
Dianne Hackbornef05e072010-03-01 17:43:39 -08001533 }
Dan Morrill89d97c12010-05-03 16:13:14 -07001534 if (!specNetworkLocFeature && hasCoarseLocPermission) {
1535 // if app takes Network location (COARSE location) perm but does not request the
1536 // network location feature, we infer that it meant to
1537 printf("uses-feature:'android.hardware.location.network'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001538 printf("uses-implied-feature:'android.hardware.location.network'," \
Dianne Hackbornec559ff2013-02-25 15:42:07 -08001539 "'requested android.permission.ACCESS_COARSE_LOCATION permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001540 }
1541
1542 // Bluetooth-related compatibility logic
Dan Morrill6b22d812010-06-15 21:41:42 -07001543 if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001544 // if app takes a Bluetooth permission but does not request the Bluetooth
1545 // feature, we infer that it meant to
1546 printf("uses-feature:'android.hardware.bluetooth'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001547 printf("uses-implied-feature:'android.hardware.bluetooth'," \
1548 "'requested android.permission.BLUETOOTH or android.permission.BLUETOOTH_ADMIN " \
1549 "permission and targetSdkVersion > 4'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001550 }
1551
1552 // Microphone-related compatibility logic
1553 if (!specMicrophoneFeature && hasRecordAudioPermission) {
1554 // if app takes the record-audio permission but does not request the microphone
1555 // feature, we infer that it meant to
1556 printf("uses-feature:'android.hardware.microphone'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001557 printf("uses-implied-feature:'android.hardware.microphone'," \
1558 "'requested android.permission.RECORD_AUDIO permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001559 }
1560
1561 // WiFi-related compatibility logic
1562 if (!specWiFiFeature && hasWiFiPermission) {
1563 // if app takes one of the WiFi permissions but does not request the WiFi
1564 // feature, we infer that it meant to
1565 printf("uses-feature:'android.hardware.wifi'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001566 printf("uses-implied-feature:'android.hardware.wifi'," \
1567 "'requested android.permission.ACCESS_WIFI_STATE, " \
1568 "android.permission.CHANGE_WIFI_STATE, or " \
1569 "android.permission.CHANGE_WIFI_MULTICAST_STATE permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001570 }
1571
1572 // Telephony-related compatibility logic
1573 if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) {
1574 // if app takes one of the telephony permissions or requests a sub-feature but
1575 // does not request the base telephony feature, we infer that it meant to
1576 printf("uses-feature:'android.hardware.telephony'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001577 printf("uses-implied-feature:'android.hardware.telephony'," \
1578 "'requested a telephony-related permission or feature'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001579 }
1580
1581 // Touchscreen-related back-compatibility logic
1582 if (!specTouchscreenFeature) { // not a typo!
1583 // all apps are presumed to require a touchscreen, unless they explicitly say
1584 // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
1585 // Note that specTouchscreenFeature is true if the tag is present, regardless
1586 // of whether its value is true or false, so this is safe
1587 printf("uses-feature:'android.hardware.touchscreen'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001588 printf("uses-implied-feature:'android.hardware.touchscreen'," \
1589 "'assumed you require a touch screen unless explicitly made optional'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001590 }
1591 if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
1592 // if app takes one of the telephony permissions or requests a sub-feature but
1593 // does not request the base telephony feature, we infer that it meant to
1594 printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001595 printf("uses-implied-feature:'android.hardware.touchscreen.multitouch'," \
1596 "'requested android.hardware.touchscreen.multitouch.distinct feature'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001597 }
Dianne Hackbornef05e072010-03-01 17:43:39 -08001598
Dianne Hackborne289bff2011-06-13 19:33:22 -07001599 // Landscape/portrait-related compatibility logic
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001600 if (!specScreenLandscapeFeature && !specScreenPortraitFeature) {
1601 // If the app has specified any activities in its manifest
1602 // that request a specific orientation, then assume that
1603 // orientation is required.
1604 if (reqScreenLandscapeFeature) {
1605 printf("uses-feature:'android.hardware.screen.landscape'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001606 printf("uses-implied-feature:'android.hardware.screen.landscape'," \
1607 "'one or more activities have specified a landscape orientation'\n");
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001608 }
1609 if (reqScreenPortraitFeature) {
1610 printf("uses-feature:'android.hardware.screen.portrait'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001611 printf("uses-implied-feature:'android.hardware.screen.portrait'," \
1612 "'one or more activities have specified a portrait orientation'\n");
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001613 }
Dianne Hackborne289bff2011-06-13 19:33:22 -07001614 }
1615
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001616 if (hasMainActivity) {
1617 printf("main\n");
1618 }
1619 if (hasWidgetReceivers) {
1620 printf("app-widget\n");
1621 }
Adam Lesinskib1249b82013-09-25 17:03:50 -07001622 if (hasDeviceAdminReceiver) {
1623 printf("device-admin\n");
1624 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001625 if (hasImeService) {
1626 printf("ime\n");
1627 }
1628 if (hasWallpaperService) {
1629 printf("wallpaper\n");
1630 }
Adam Lesinskib1249b82013-09-25 17:03:50 -07001631 if (hasAccessibilityService) {
1632 printf("accessibility\n");
1633 }
1634 if (hasPrintService) {
1635 printf("print\n");
1636 }
Adam Lesinskid831e802013-09-27 11:14:57 -07001637 if (hasPaymentService) {
1638 printf("payment\n");
1639 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001640 if (hasOtherActivities) {
1641 printf("other-activities\n");
1642 }
1643 if (isSearchable) {
1644 printf("search\n");
1645 }
1646 if (hasOtherReceivers) {
1647 printf("other-receivers\n");
1648 }
1649 if (hasOtherServices) {
1650 printf("other-services\n");
1651 }
1652
Dianne Hackborne289bff2011-06-13 19:33:22 -07001653 // For modern apps, if screen size buckets haven't been specified
1654 // but the new width ranges have, then infer the buckets from them.
1655 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1656 && requiresSmallestWidthDp > 0) {
1657 int compatWidth = compatibleWidthLimitDp;
1658 if (compatWidth <= 0) compatWidth = requiresSmallestWidthDp;
1659 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1660 smallScreen = -1;
1661 } else {
1662 smallScreen = 0;
1663 }
1664 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1665 normalScreen = -1;
1666 } else {
1667 normalScreen = 0;
1668 }
1669 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1670 largeScreen = -1;
1671 } else {
1672 largeScreen = 0;
1673 }
1674 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1675 xlargeScreen = -1;
1676 } else {
1677 xlargeScreen = 0;
1678 }
1679 }
1680
Dianne Hackborn723738c2009-06-25 19:48:04 -07001681 // Determine default values for any unspecified screen sizes,
1682 // based on the target SDK of the package. As of 4 (donut)
1683 // the screen size support was introduced, so all default to
1684 // enabled.
1685 if (smallScreen > 0) {
1686 smallScreen = targetSdk >= 4 ? -1 : 0;
1687 }
1688 if (normalScreen > 0) {
1689 normalScreen = -1;
1690 }
1691 if (largeScreen > 0) {
1692 largeScreen = targetSdk >= 4 ? -1 : 0;
1693 }
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001694 if (xlargeScreen > 0) {
Scott Maind58fb972010-11-04 18:32:00 -07001695 // Introduced in Gingerbread.
1696 xlargeScreen = targetSdk >= 9 ? -1 : 0;
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001697 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001698 if (anyDensity > 0) {
Dianne Hackborne289bff2011-06-13 19:33:22 -07001699 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1700 || compatibleWidthLimitDp > 0) ? -1 : 0;
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001701 }
Dianne Hackborn723738c2009-06-25 19:48:04 -07001702 printf("supports-screens:");
1703 if (smallScreen != 0) printf(" 'small'");
1704 if (normalScreen != 0) printf(" 'normal'");
1705 if (largeScreen != 0) printf(" 'large'");
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001706 if (xlargeScreen != 0) printf(" 'xlarge'");
Dianne Hackborn723738c2009-06-25 19:48:04 -07001707 printf("\n");
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001708 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
Dianne Hackborne289bff2011-06-13 19:33:22 -07001709 if (requiresSmallestWidthDp > 0) {
1710 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1711 }
1712 if (compatibleWidthLimitDp > 0) {
1713 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1714 }
1715 if (largestWidthLimitDp > 0) {
1716 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1717 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001718
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001719 printf("locales:");
Dianne Hackborne17086b2009-06-19 15:13:28 -07001720 const size_t NL = locales.size();
1721 for (size_t i=0; i<NL; i++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001722 const char* localeStr = locales[i].string();
1723 if (localeStr == NULL || strlen(localeStr) == 0) {
1724 localeStr = "--_--";
1725 }
1726 printf(" '%s'", localeStr);
1727 }
1728 printf("\n");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001729
Dianne Hackborne17086b2009-06-19 15:13:28 -07001730 printf("densities:");
1731 const size_t ND = densities.size();
1732 for (size_t i=0; i<ND; i++) {
1733 printf(" '%d'", densities[i]);
1734 }
1735 printf("\n");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001736
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001737 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1738 if (dir != NULL) {
1739 if (dir->getFileCount() > 0) {
1740 printf("native-code:");
1741 for (size_t i=0; i<dir->getFileCount(); i++) {
1742 printf(" '%s'", dir->getFileName(i).string());
1743 }
1744 printf("\n");
1745 }
1746 delete dir;
1747 }
Dan Morrille74763e2012-01-06 10:47:10 -08001748 } else if (strcmp("badger", option) == 0) {
Dianne Hackborn6c997a92012-01-31 11:27:43 -08001749 printf("%s", CONSOLE_DATA);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001750 } else if (strcmp("configurations", option) == 0) {
1751 Vector<ResTable_config> configs;
1752 res.getConfigurations(&configs);
1753 const size_t N = configs.size();
1754 for (size_t i=0; i<N; i++) {
1755 printf("%s\n", configs[i].toString().string());
1756 }
1757 } else {
1758 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
1759 goto bail;
1760 }
1761 }
1762
1763 result = NO_ERROR;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001764
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001765bail:
1766 if (asset) {
1767 delete asset;
1768 }
1769 return (result != NO_ERROR);
1770}
1771
1772
1773/*
1774 * Handle the "add" command, which wants to add files to a new or
1775 * pre-existing archive.
1776 */
1777int doAdd(Bundle* bundle)
1778{
1779 ZipFile* zip = NULL;
1780 status_t result = UNKNOWN_ERROR;
1781 const char* zipFileName;
1782
1783 if (bundle->getUpdate()) {
1784 /* avoid confusion */
1785 fprintf(stderr, "ERROR: can't use '-u' with add\n");
1786 goto bail;
1787 }
1788
1789 if (bundle->getFileSpecCount() < 1) {
1790 fprintf(stderr, "ERROR: must specify zip file name\n");
1791 goto bail;
1792 }
1793 zipFileName = bundle->getFileSpecEntry(0);
1794
1795 if (bundle->getFileSpecCount() < 2) {
1796 fprintf(stderr, "NOTE: nothing to do\n");
1797 goto bail;
1798 }
1799
1800 zip = openReadWrite(zipFileName, true);
1801 if (zip == NULL) {
1802 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
1803 goto bail;
1804 }
1805
1806 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1807 const char* fileName = bundle->getFileSpecEntry(i);
1808
1809 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
1810 printf(" '%s'... (from gzip)\n", fileName);
1811 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
1812 } else {
Doug Zongkerdbe7a682009-10-09 11:24:51 -07001813 if (bundle->getJunkPath()) {
1814 String8 storageName = String8(fileName).getPathLeaf();
1815 printf(" '%s' as '%s'...\n", fileName, storageName.string());
1816 result = zip->add(fileName, storageName.string(),
1817 bundle->getCompressionMethod(), NULL);
1818 } else {
1819 printf(" '%s'...\n", fileName);
1820 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
1821 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001822 }
1823 if (result != NO_ERROR) {
1824 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
1825 if (result == NAME_NOT_FOUND)
1826 fprintf(stderr, ": file not found\n");
1827 else if (result == ALREADY_EXISTS)
1828 fprintf(stderr, ": already exists in archive\n");
1829 else
1830 fprintf(stderr, "\n");
1831 goto bail;
1832 }
1833 }
1834
1835 result = NO_ERROR;
1836
1837bail:
1838 delete zip;
1839 return (result != NO_ERROR);
1840}
1841
1842
1843/*
1844 * Delete files from an existing archive.
1845 */
1846int doRemove(Bundle* bundle)
1847{
1848 ZipFile* zip = NULL;
1849 status_t result = UNKNOWN_ERROR;
1850 const char* zipFileName;
1851
1852 if (bundle->getFileSpecCount() < 1) {
1853 fprintf(stderr, "ERROR: must specify zip file name\n");
1854 goto bail;
1855 }
1856 zipFileName = bundle->getFileSpecEntry(0);
1857
1858 if (bundle->getFileSpecCount() < 2) {
1859 fprintf(stderr, "NOTE: nothing to do\n");
1860 goto bail;
1861 }
1862
1863 zip = openReadWrite(zipFileName, false);
1864 if (zip == NULL) {
1865 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
1866 zipFileName);
1867 goto bail;
1868 }
1869
1870 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1871 const char* fileName = bundle->getFileSpecEntry(i);
1872 ZipEntry* entry;
1873
1874 entry = zip->getEntryByName(fileName);
1875 if (entry == NULL) {
1876 printf(" '%s' NOT FOUND\n", fileName);
1877 continue;
1878 }
1879
1880 result = zip->remove(entry);
1881
1882 if (result != NO_ERROR) {
1883 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
1884 bundle->getFileSpecEntry(i), zipFileName);
1885 goto bail;
1886 }
1887 }
1888
1889 /* update the archive */
1890 zip->flush();
1891
1892bail:
1893 delete zip;
1894 return (result != NO_ERROR);
1895}
1896
1897
1898/*
1899 * Package up an asset directory and associated application files.
1900 */
1901int doPackage(Bundle* bundle)
1902{
1903 const char* outputAPKFile;
1904 int retVal = 1;
1905 status_t err;
1906 sp<AaptAssets> assets;
1907 int N;
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001908 FILE* fp;
1909 String8 dependencyFile;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001910
1911 // -c zz_ZZ means do pseudolocalization
1912 ResourceFilter filter;
1913 err = filter.parse(bundle->getConfigurations());
1914 if (err != NO_ERROR) {
1915 goto bail;
1916 }
1917 if (filter.containsPseudo()) {
1918 bundle->setPseudolocalize(true);
1919 }
1920
1921 N = bundle->getFileSpecCount();
1922 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
Adam Lesinski09384302014-01-22 16:07:42 -08001923 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001924 fprintf(stderr, "ERROR: no input files\n");
1925 goto bail;
1926 }
1927
1928 outputAPKFile = bundle->getOutputAPKFile();
1929
1930 // Make sure the filenames provided exist and are of the appropriate type.
1931 if (outputAPKFile) {
1932 FileType type;
1933 type = getFileType(outputAPKFile);
1934 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
1935 fprintf(stderr,
1936 "ERROR: output file '%s' exists but is not regular file\n",
1937 outputAPKFile);
1938 goto bail;
1939 }
1940 }
1941
1942 // Load the assets.
1943 assets = new AaptAssets();
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001944
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001945 // Set up the resource gathering in assets if we're going to generate
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001946 // dependency files. Every time we encounter a resource while slurping
1947 // the tree, we'll add it to these stores so we have full resource paths
1948 // to write to a dependency file.
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001949 if (bundle->getGenDependencies()) {
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001950 sp<FilePathStore> resPathStore = new FilePathStore;
1951 assets->setFullResPaths(resPathStore);
1952 sp<FilePathStore> assetPathStore = new FilePathStore;
1953 assets->setFullAssetPaths(assetPathStore);
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001954 }
1955
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001956 err = assets->slurpFromArgs(bundle);
1957 if (err < 0) {
1958 goto bail;
1959 }
1960
1961 if (bundle->getVerbose()) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001962 assets->print(String8());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001963 }
1964
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001965 // If they asked for any fileAs that need to be compiled, do so.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001966 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
1967 err = buildResources(bundle, assets);
1968 if (err != 0) {
1969 goto bail;
1970 }
1971 }
1972
1973 // At this point we've read everything and processed everything. From here
1974 // on out it's just writing output files.
1975 if (SourcePos::hasErrors()) {
1976 goto bail;
1977 }
1978
Dianne Hackborn1644c6d72012-02-06 15:33:21 -08001979 // Update symbols with information about which ones are needed as Java symbols.
1980 assets->applyJavaSymbols();
1981 if (SourcePos::hasErrors()) {
1982 goto bail;
1983 }
1984
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001985 // If we've been asked to generate a dependency file, do that here
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001986 if (bundle->getGenDependencies()) {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001987 // If this is the packaging step, generate the dependency file next to
1988 // the output apk (e.g. bin/resources.ap_.d)
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001989 if (outputAPKFile) {
1990 dependencyFile = String8(outputAPKFile);
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001991 // Add the .d extension to the dependency file.
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001992 dependencyFile.append(".d");
1993 } else {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001994 // Else if this is the R.java dependency generation step,
1995 // generate the dependency file in the R.java package subdirectory
1996 // e.g. gen/com/foo/app/R.java.d
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001997 dependencyFile = String8(bundle->getRClassDir());
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001998 dependencyFile.appendPath("R.java.d");
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001999 }
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07002000 // Make sure we have a clean dependency file to start with
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07002001 fp = fopen(dependencyFile, "w");
2002 fclose(fp);
2003 }
2004
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002005 // Write out R.java constants
Dianne Hackborn1644c6d72012-02-06 15:33:21 -08002006 if (!assets->havePrivateSymbols()) {
Xavier Ducrohet63459ad2009-11-30 18:05:10 -08002007 if (bundle->getCustomPackage() == NULL) {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07002008 // Write the R.java file into the appropriate class directory
2009 // e.g. gen/com/foo/app/R.java
Xavier Ducrohet63459ad2009-11-30 18:05:10 -08002010 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
2011 } else {
2012 const String8 customPkg(bundle->getCustomPackage());
2013 err = writeResourceSymbols(bundle, assets, customPkg, true);
2014 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002015 if (err < 0) {
2016 goto bail;
2017 }
Ying Wang002f5372012-04-25 18:53:49 -07002018 // If we have library files, we're going to write our R.java file into
2019 // the appropriate class directory for those libraries as well.
2020 // e.g. gen/com/foo/app/lib/R.java
2021 if (bundle->getExtraPackages() != NULL) {
2022 // Split on colon
2023 String8 libs(bundle->getExtraPackages());
2024 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2025 while (packageString != NULL) {
2026 // Write the R.java file out with the correct package name
2027 err = writeResourceSymbols(bundle, assets, String8(packageString), true);
2028 if (err < 0) {
2029 goto bail;
2030 }
2031 packageString = strtok(NULL, ":");
2032 }
2033 libs.unlockBuffer();
2034 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002035 } else {
2036 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
2037 if (err < 0) {
2038 goto bail;
2039 }
2040 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
2041 if (err < 0) {
2042 goto bail;
2043 }
2044 }
2045
Joe Onorato1553c822009-08-30 13:36:22 -07002046 // Write out the ProGuard file
2047 err = writeProguardFile(bundle, assets);
2048 if (err < 0) {
2049 goto bail;
2050 }
2051
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002052 // Write the apk
2053 if (outputAPKFile) {
2054 err = writeAPK(bundle, assets, String8(outputAPKFile));
2055 if (err != NO_ERROR) {
2056 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
2057 goto bail;
2058 }
2059 }
2060
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07002061 // If we've been asked to generate a dependency file, we need to finish up here.
2062 // the writeResourceSymbols and writeAPK functions have already written the target
2063 // half of the dependency file, now we need to write the prerequisites. (files that
2064 // the R.java file or .ap_ file depend on)
Josiah Gaskin03589cc2011-06-27 16:26:02 -07002065 if (bundle->getGenDependencies()) {
2066 // Now that writeResourceSymbols or writeAPK has taken care of writing
2067 // the targets to our dependency file, we'll write the prereqs
2068 fp = fopen(dependencyFile, "a+");
2069 fprintf(fp, " : ");
2070 bool includeRaw = (outputAPKFile != NULL);
2071 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07002072 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2073 // and therefore was not added to our pathstores during slurping
Josiah Gaskin03589cc2011-06-27 16:26:02 -07002074 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2075 fclose(fp);
2076 }
2077
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002078 retVal = 0;
2079bail:
2080 if (SourcePos::hasErrors()) {
2081 SourcePos::printErrors(stderr);
2082 }
2083 return retVal;
2084}
Josiah Gaskin8a39da82011-06-06 17:00:35 -07002085
2086/*
2087 * Do PNG Crunching
2088 * PRECONDITIONS
2089 * -S flag points to a source directory containing drawable* folders
2090 * -C flag points to destination directory. The folder structure in the
2091 * source directory will be mirrored to the destination (cache) directory
2092 *
2093 * POSTCONDITIONS
2094 * Destination directory will be updated to match the PNG files in
2095 * the source directory.
2096 */
2097int doCrunch(Bundle* bundle)
2098{
2099 fprintf(stdout, "Crunching PNG Files in ");
2100 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2101 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2102
2103 updatePreProcessedCache(bundle);
2104
2105 return NO_ERROR;
2106}
Dan Morrille74763e2012-01-06 10:47:10 -08002107
Xavier Ducrohetb1f6ad82012-12-21 09:54:02 -08002108/*
2109 * Do PNG Crunching on a single flag
2110 * -i points to a single png file
2111 * -o points to a single png output file
2112 */
2113int doSingleCrunch(Bundle* bundle)
2114{
2115 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2116 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2117
2118 String8 input(bundle->getSingleCrunchInputFile());
2119 String8 output(bundle->getSingleCrunchOutputFile());
Xavier Ducrohetb7de2192013-01-15 16:41:29 -08002120
Xavier Ducrohetc75d3f52013-01-14 18:02:40 -08002121 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2122 // we can't return the status_t as it gets truncate to the lower 8 bits.
2123 return 42;
2124 }
Xavier Ducrohetb7de2192013-01-15 16:41:29 -08002125
Xavier Ducrohetc75d3f52013-01-14 18:02:40 -08002126 return NO_ERROR;
Xavier Ducrohetb1f6ad82012-12-21 09:54:02 -08002127}
2128
Dan Morrille74763e2012-01-06 10:47:10 -08002129char CONSOLE_DATA[2925] = {
2130 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2131 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2132 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2133 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2134 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2135 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2136 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2137 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2138 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2139 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2140 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2141 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2142 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2143 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2144 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2145 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2146 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2147 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2148 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2149 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2150 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2151 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2152 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2153 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2154 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2155 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2156 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2157 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2158 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2159 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2160 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2161 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2162 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2163 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2164 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2165 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2166 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2167 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2168 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2169 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2170 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2171 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2172 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2173 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2174 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2175 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2176 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2177 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2178 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2179 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2180 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2181 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2182 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2183 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2184 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2185 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2186 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2187 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2188 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2189 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2190 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2191 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2192 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2193 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2194 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2195 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2196 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2197 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2198 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2199 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2200 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2201 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2202 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2203 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2204 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2205 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2206 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2207 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2208 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2209 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2210 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2211 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2212 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2213 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2214 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2215 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2216 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2217 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2218 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2219 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2220 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2221 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2222 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2223 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2224 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2225 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2226 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2227 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2228 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2229 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2230 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2231 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2232 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2233 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2234 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2235 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2236 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2237 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2238 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2239 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2240 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2241 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2242 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2243 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2244 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2245 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2246 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2247 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2248 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2249 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2250 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2251 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2252 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2253 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2254 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2255 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2256 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2257 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2258 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2259 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2260 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2261 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2262 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2263 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2264 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2265 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2266 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2267 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2268 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2269 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2270 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2271 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2272 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2273 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2274 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2275 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2276 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2277 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2278 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2279 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2280 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2281 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2282 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2283 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2284 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2285 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2286 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2287 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2288 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2289 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2290 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2291 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2292 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2293 };