blob: 34292fe4c5b33d1034d5c2dca6194895bbb5eb54 [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;
Narayan Kamath788fa412014-01-21 15:32:36 +0000521 memset(&config, 0, sizeof(ResTable_config));
Dianne Hackborne289bff2011-06-13 19:33:22 -0700522 config.language[0] = 'e';
523 config.language[1] = 'n';
524 config.country[0] = 'U';
525 config.country[1] = 'S';
526 config.orientation = ResTable_config::ORIENTATION_PORT;
527 config.density = ResTable_config::DENSITY_MEDIUM;
528 config.sdkVersion = 10000; // Very high.
529 config.screenWidthDp = 320;
530 config.screenHeightDp = 480;
531 config.smallestScreenWidthDp = 320;
532 assets.setConfiguration(config);
533
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800534 const ResTable& res = assets.getResources(false);
535 if (&res == NULL) {
536 fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
537 goto bail;
538 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700539
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800540 if (strcmp("resources", option) == 0) {
Steve Blockf1ff21a2010-06-14 17:34:04 +0100541#ifndef HAVE_ANDROID_OS
Dianne Hackborne17086b2009-06-19 15:13:28 -0700542 res.print(bundle->getValues());
Steve Blockf1ff21a2010-06-14 17:34:04 +0100543#endif
Dianne Hackborn6c997a92012-01-31 11:27:43 -0800544
545 } else if (strcmp("strings", option) == 0) {
546 const ResStringPool* pool = res.getTableStringBlock(0);
547 printStringPool(pool);
548
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800549 } else if (strcmp("xmltree", option) == 0) {
550 if (bundle->getFileSpecCount() < 3) {
551 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
552 goto bail;
553 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700554
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800555 for (int i=2; i<bundle->getFileSpecCount(); i++) {
556 const char* resname = bundle->getFileSpecEntry(i);
557 ResXMLTree tree;
558 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
559 if (asset == NULL) {
Kenny Root44b283d2009-09-01 19:03:11 -0500560 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800561 goto bail;
562 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700563
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800564 if (tree.setTo(asset->getBuffer(true),
565 asset->getLength()) != NO_ERROR) {
566 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
567 goto bail;
568 }
569 tree.restart();
570 printXMLBlock(&tree);
Kenny Root19138462009-12-04 09:38:48 -0800571 tree.uninit();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800572 delete asset;
573 asset = NULL;
574 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700575
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800576 } else if (strcmp("xmlstrings", option) == 0) {
577 if (bundle->getFileSpecCount() < 3) {
578 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
579 goto bail;
580 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700581
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800582 for (int i=2; i<bundle->getFileSpecCount(); i++) {
583 const char* resname = bundle->getFileSpecEntry(i);
584 ResXMLTree tree;
585 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
586 if (asset == NULL) {
Kenny Root44b283d2009-09-01 19:03:11 -0500587 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800588 goto bail;
589 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700590
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800591 if (tree.setTo(asset->getBuffer(true),
592 asset->getLength()) != NO_ERROR) {
593 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
594 goto bail;
595 }
596 printStringPool(&tree.getStrings());
597 delete asset;
598 asset = NULL;
599 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700600
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800601 } else {
602 ResXMLTree tree;
603 asset = assets.openNonAsset("AndroidManifest.xml",
604 Asset::ACCESS_BUFFER);
605 if (asset == NULL) {
606 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
607 goto bail;
608 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700609
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800610 if (tree.setTo(asset->getBuffer(true),
611 asset->getLength()) != NO_ERROR) {
612 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
613 goto bail;
614 }
615 tree.restart();
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700616
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800617 if (strcmp("permissions", option) == 0) {
618 size_t len;
619 ResXMLTree::event_code_t code;
620 int depth = 0;
621 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
622 if (code == ResXMLTree::END_TAG) {
623 depth--;
624 continue;
625 }
626 if (code != ResXMLTree::START_TAG) {
627 continue;
628 }
629 depth++;
630 String8 tag(tree.getElementName(&len));
631 //printf("Depth %d tag %s\n", depth, tag.string());
632 if (depth == 1) {
633 if (tag != "manifest") {
634 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
635 goto bail;
636 }
637 String8 pkg = getAttribute(tree, NULL, "package", NULL);
638 printf("package: %s\n", pkg.string());
639 } else if (depth == 2 && tag == "permission") {
640 String8 error;
641 String8 name = getAttribute(tree, NAME_ATTR, &error);
642 if (error != "") {
643 fprintf(stderr, "ERROR: %s\n", error.string());
644 goto bail;
645 }
646 printf("permission: %s\n", name.string());
647 } else if (depth == 2 && tag == "uses-permission") {
648 String8 error;
649 String8 name = getAttribute(tree, NAME_ATTR, &error);
650 if (error != "") {
651 fprintf(stderr, "ERROR: %s\n", error.string());
652 goto bail;
653 }
654 printf("uses-permission: %s\n", name.string());
Nick Kralevich1bcc3d62013-04-03 09:00:02 -0700655 int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
656 if (!req) {
657 printf("optional-permission: %s\n", name.string());
658 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800659 }
660 }
661 } else if (strcmp("badging", option) == 0) {
Dianne Hackborne289bff2011-06-13 19:33:22 -0700662 Vector<String8> locales;
663 res.getLocales(&locales);
664
665 Vector<ResTable_config> configs;
666 res.getConfigurations(&configs);
667 SortedVector<int> densities;
668 const size_t NC = configs.size();
669 for (size_t i=0; i<NC; i++) {
670 int dens = configs[i].density;
671 if (dens == 0) dens = 160;
672 densities.add(dens);
673 }
674
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800675 size_t len;
676 ResXMLTree::event_code_t code;
677 int depth = 0;
678 String8 error;
679 bool withinActivity = false;
680 bool isMainActivity = false;
681 bool isLauncherActivity = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800682 bool isLeanbackLauncherActivity = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700683 bool isSearchable = false;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700684 bool withinApplication = false;
Michael Wrighteaeb1902013-09-05 18:15:57 -0700685 bool withinSupportsInput = false;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700686 bool withinReceiver = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700687 bool withinService = false;
688 bool withinIntentFilter = false;
689 bool hasMainActivity = false;
690 bool hasOtherActivities = false;
691 bool hasOtherReceivers = false;
692 bool hasOtherServices = false;
693 bool hasWallpaperService = false;
694 bool hasImeService = false;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700695 bool hasAccessibilityService = false;
696 bool hasPrintService = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700697 bool hasWidgetReceivers = false;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700698 bool hasDeviceAdminReceiver = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700699 bool hasIntentFilter = false;
Adam Lesinskid831e802013-09-27 11:14:57 -0700700 bool hasPaymentService = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700701 bool actMainActivity = false;
702 bool actWidgetReceivers = false;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700703 bool actDeviceAdminEnabled = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700704 bool actImeService = false;
705 bool actWallpaperService = false;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700706 bool actAccessibilityService = false;
707 bool actPrintService = false;
Adam Lesinskid831e802013-09-27 11:14:57 -0700708 bool actHostApduService = false;
709 bool actOffHostApduService = false;
710 bool hasMetaHostPaymentCategory = false;
711 bool hasMetaOffHostPaymentCategory = false;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700712
713 // These permissions are required by services implementing services
714 // the system binds to (IME, Accessibility, PrintServices, etc.)
715 bool hasBindDeviceAdminPermission = false;
716 bool hasBindInputMethodPermission = false;
717 bool hasBindAccessibilityServicePermission = false;
718 bool hasBindPrintServicePermission = false;
Adam Lesinskid831e802013-09-27 11:14:57 -0700719 bool hasBindNfcServicePermission = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700720
Kenny Root063a44e2011-12-08 08:46:03 -0800721 // These two implement the implicit permissions that are granted
722 // to pre-1.6 applications.
723 bool hasWriteExternalStoragePermission = false;
724 bool hasReadPhoneStatePermission = false;
725
Dianne Hackborn79245122012-03-12 10:51:26 -0700726 // If an app requests write storage, they will also get read storage.
727 bool hasReadExternalStoragePermission = false;
728
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -0700729 // Implement transition to read and write call log.
730 bool hasReadContactsPermission = false;
731 bool hasWriteContactsPermission = false;
732 bool hasReadCallLogPermission = false;
733 bool hasWriteCallLogPermission = false;
734
Dan Morrill89d97c12010-05-03 16:13:14 -0700735 // This next group of variables is used to implement a group of
736 // backward-compatibility heuristics necessitated by the addition of
737 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
738 // heuristic is "if an app requests a permission but doesn't explicitly
739 // request the corresponding <uses-feature>, presume it's there anyway".
740 bool specCameraFeature = false; // camera-related
741 bool specCameraAutofocusFeature = false;
742 bool reqCameraAutofocusFeature = false;
743 bool reqCameraFlashFeature = false;
Dianne Hackborne5276a72009-08-27 16:28:44 -0700744 bool hasCameraPermission = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700745 bool specLocationFeature = false; // location-related
746 bool specNetworkLocFeature = false;
747 bool reqNetworkLocFeature = false;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800748 bool specGpsFeature = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700749 bool reqGpsFeature = false;
750 bool hasMockLocPermission = false;
751 bool hasCoarseLocPermission = false;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800752 bool hasGpsPermission = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700753 bool hasGeneralLocPermission = false;
754 bool specBluetoothFeature = false; // Bluetooth API-related
755 bool hasBluetoothPermission = false;
756 bool specMicrophoneFeature = false; // microphone-related
757 bool hasRecordAudioPermission = false;
758 bool specWiFiFeature = false;
759 bool hasWiFiPermission = false;
760 bool specTelephonyFeature = false; // telephony-related
761 bool reqTelephonySubFeature = false;
762 bool hasTelephonyPermission = false;
763 bool specTouchscreenFeature = false; // touchscreen-related
764 bool specMultitouchFeature = false;
765 bool reqDistinctMultitouchFeature = false;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700766 bool specScreenPortraitFeature = false;
767 bool specScreenLandscapeFeature = false;
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -0700768 bool reqScreenPortraitFeature = false;
769 bool reqScreenLandscapeFeature = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700770 // 2.2 also added some other features that apps can request, but that
771 // have no corresponding permission, so we cannot implement any
772 // back-compatibility heuristic for them. The below are thus unnecessary
773 // (but are retained here for documentary purposes.)
774 //bool specCompassFeature = false;
775 //bool specAccelerometerFeature = false;
776 //bool specProximityFeature = false;
777 //bool specAmbientLightFeature = false;
778 //bool specLiveWallpaperFeature = false;
779
Dianne Hackborn723738c2009-06-25 19:48:04 -0700780 int targetSdk = 0;
781 int smallScreen = 1;
782 int normalScreen = 1;
783 int largeScreen = 1;
Dianne Hackbornf43489d2010-08-20 12:44:33 -0700784 int xlargeScreen = 1;
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700785 int anyDensity = 1;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700786 int requiresSmallestWidthDp = 0;
787 int compatibleWidthLimitDp = 0;
788 int largestWidthLimitDp = 0;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700789 String8 pkg;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800790 String8 activityName;
791 String8 activityLabel;
792 String8 activityIcon;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800793 String8 activityBanner;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700794 String8 receiverName;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700795 String8 serviceName;
Michael Wrighteaeb1902013-09-05 18:15:57 -0700796 Vector<String8> supportedInput;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800797 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
798 if (code == ResXMLTree::END_TAG) {
799 depth--;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700800 if (depth < 2) {
Michael Wrighteaeb1902013-09-05 18:15:57 -0700801 if (withinSupportsInput && !supportedInput.isEmpty()) {
802 printf("supports-input: '");
803 const size_t N = supportedInput.size();
804 for (size_t i=0; i<N; i++) {
805 printf("%s", supportedInput[i].string());
806 if (i != N - 1) {
807 printf("' '");
808 } else {
809 printf("'\n");
810 }
811 }
812 supportedInput.clear();
813 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700814 withinApplication = false;
Michael Wrighteaeb1902013-09-05 18:15:57 -0700815 withinSupportsInput = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700816 } else if (depth < 3) {
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800817 if (withinActivity && isMainActivity) {
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700818 const char *aName = getComponentName(pkg, activityName);
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800819 if (isLauncherActivity) {
820 printf("launchable-activity:");
821 if (aName != NULL) {
822 printf(" name='%s' ", aName);
823 }
824 printf(" label='%s' icon='%s'\n",
825 activityLabel.string(),
826 activityIcon.string());
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700827 }
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800828 if (isLeanbackLauncherActivity) {
829 printf("leanback-launchable-activity:");
830 if (aName != NULL) {
831 printf(" name='%s' ", aName);
832 }
833 printf(" label='%s' icon='%s' banner='%s'\n",
834 activityLabel.string(),
835 activityIcon.string(),
836 activityBanner.string());
837 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700838 }
839 if (!hasIntentFilter) {
840 hasOtherActivities |= withinActivity;
841 hasOtherReceivers |= withinReceiver;
842 hasOtherServices |= withinService;
Adam Lesinskid831e802013-09-27 11:14:57 -0700843 } else {
844 if (withinService) {
845 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
846 hasBindNfcServicePermission);
847 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
848 hasBindNfcServicePermission);
849 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700850 }
851 withinActivity = false;
852 withinService = false;
853 withinReceiver = false;
854 hasIntentFilter = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800855 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700856 } else if (depth < 4) {
857 if (withinIntentFilter) {
858 if (withinActivity) {
859 hasMainActivity |= actMainActivity;
860 hasOtherActivities |= !actMainActivity;
861 } else if (withinReceiver) {
862 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700863 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
864 hasBindDeviceAdminPermission);
865 hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled);
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700866 } else if (withinService) {
867 hasImeService |= actImeService;
868 hasWallpaperService |= actWallpaperService;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700869 hasAccessibilityService |= (actAccessibilityService &&
870 hasBindAccessibilityServicePermission);
871 hasPrintService |= (actPrintService && hasBindPrintServicePermission);
872 hasOtherServices |= (!actImeService && !actWallpaperService &&
Adam Lesinskid831e802013-09-27 11:14:57 -0700873 !actAccessibilityService && !actPrintService &&
874 !actHostApduService && !actOffHostApduService);
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700875 }
876 }
877 withinIntentFilter = false;
878 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800879 continue;
880 }
881 if (code != ResXMLTree::START_TAG) {
882 continue;
883 }
884 depth++;
885 String8 tag(tree.getElementName(&len));
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700886 //printf("Depth %d, %s\n", depth, tag.string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800887 if (depth == 1) {
888 if (tag != "manifest") {
889 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
890 goto bail;
891 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700892 pkg = getAttribute(tree, NULL, "package", NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800893 printf("package: name='%s' ", pkg.string());
894 int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
895 if (error != "") {
896 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
897 goto bail;
898 }
899 if (versionCode > 0) {
900 printf("versionCode='%d' ", versionCode);
901 } else {
902 printf("versionCode='' ");
903 }
Dianne Hackborncf244ad2010-03-09 15:00:30 -0800904 String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800905 if (error != "") {
906 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
907 goto bail;
908 }
909 printf("versionName='%s'\n", versionName.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700910 } else if (depth == 2) {
911 withinApplication = false;
912 if (tag == "application") {
913 withinApplication = true;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700914
915 String8 label;
916 const size_t NL = locales.size();
917 for (size_t i=0; i<NL; i++) {
918 const char* localeStr = locales[i].string();
919 assets.setLocale(localeStr != NULL ? localeStr : "");
920 String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
921 if (llabel != "") {
922 if (localeStr == NULL || strlen(localeStr) == 0) {
923 label = llabel;
924 printf("application-label:'%s'\n", llabel.string());
925 } else {
926 if (label == "") {
927 label = llabel;
928 }
929 printf("application-label-%s:'%s'\n", localeStr,
930 llabel.string());
931 }
932 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700933 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700934
935 ResTable_config tmpConfig = config;
936 const size_t ND = densities.size();
937 for (size_t i=0; i<ND; i++) {
938 tmpConfig.density = densities[i];
939 assets.setConfiguration(tmpConfig);
940 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
941 if (icon != "") {
942 printf("application-icon-%d:'%s'\n", densities[i], icon.string());
943 }
944 }
945 assets.setConfiguration(config);
946
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700947 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
948 if (error != "") {
949 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
950 goto bail;
951 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700952 int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700953 if (error != "") {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700954 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700955 goto bail;
956 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700957 printf("application: label='%s' ", label.string());
958 printf("icon='%s'\n", icon.string());
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700959 if (testOnly != 0) {
960 printf("testOnly='%d'\n", testOnly);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700961 }
Dan Morrillb6ec11e2012-04-03 12:44:40 -0700962
963 int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0);
964 if (error != "") {
965 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string());
966 goto bail;
967 }
968 if (debuggable != 0) {
969 printf("application-debuggable\n");
970 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700971 } else if (tag == "uses-sdk") {
972 int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
973 if (error != "") {
974 error = "";
975 String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
976 if (error != "") {
977 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
978 error.string());
979 goto bail;
980 }
Dianne Hackborn723738c2009-06-25 19:48:04 -0700981 if (name == "Donut") targetSdk = 4;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700982 printf("sdkVersion:'%s'\n", name.string());
983 } else if (code != -1) {
Dianne Hackborn723738c2009-06-25 19:48:04 -0700984 targetSdk = code;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700985 printf("sdkVersion:'%d'\n", code);
986 }
Suchi Amalapurapu75c49842009-08-14 15:13:09 -0700987 code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
988 if (code != -1) {
989 printf("maxSdkVersion:'%d'\n", code);
990 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700991 code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
992 if (error != "") {
993 error = "";
994 String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
995 if (error != "") {
996 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
997 error.string());
998 goto bail;
999 }
Dianne Hackborn723738c2009-06-25 19:48:04 -07001000 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001001 printf("targetSdkVersion:'%s'\n", name.string());
1002 } else if (code != -1) {
Dianne Hackborn723738c2009-06-25 19:48:04 -07001003 if (targetSdk < code) {
1004 targetSdk = code;
1005 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001006 printf("targetSdkVersion:'%d'\n", code);
1007 }
1008 } else if (tag == "uses-configuration") {
1009 int32_t reqTouchScreen = getIntegerAttribute(tree,
1010 REQ_TOUCH_SCREEN_ATTR, NULL, 0);
1011 int32_t reqKeyboardType = getIntegerAttribute(tree,
1012 REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
1013 int32_t reqHardKeyboard = getIntegerAttribute(tree,
1014 REQ_HARD_KEYBOARD_ATTR, NULL, 0);
1015 int32_t reqNavigation = getIntegerAttribute(tree,
1016 REQ_NAVIGATION_ATTR, NULL, 0);
1017 int32_t reqFiveWayNav = getIntegerAttribute(tree,
1018 REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
Dianne Hackborncb2d50d2010-01-06 11:29:54 -08001019 printf("uses-configuration:");
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001020 if (reqTouchScreen != 0) {
1021 printf(" reqTouchScreen='%d'", reqTouchScreen);
1022 }
1023 if (reqKeyboardType != 0) {
1024 printf(" reqKeyboardType='%d'", reqKeyboardType);
1025 }
1026 if (reqHardKeyboard != 0) {
1027 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1028 }
1029 if (reqNavigation != 0) {
1030 printf(" reqNavigation='%d'", reqNavigation);
1031 }
1032 if (reqFiveWayNav != 0) {
1033 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1034 }
1035 printf("\n");
Michael Wrighteaeb1902013-09-05 18:15:57 -07001036 } else if (tag == "supports-input") {
1037 withinSupportsInput = true;
Dianne Hackborn723738c2009-06-25 19:48:04 -07001038 } else if (tag == "supports-screens") {
1039 smallScreen = getIntegerAttribute(tree,
1040 SMALL_SCREEN_ATTR, NULL, 1);
1041 normalScreen = getIntegerAttribute(tree,
1042 NORMAL_SCREEN_ATTR, NULL, 1);
1043 largeScreen = getIntegerAttribute(tree,
1044 LARGE_SCREEN_ATTR, NULL, 1);
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001045 xlargeScreen = getIntegerAttribute(tree,
1046 XLARGE_SCREEN_ATTR, NULL, 1);
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001047 anyDensity = getIntegerAttribute(tree,
1048 ANY_DENSITY_ATTR, NULL, 1);
Dianne Hackborne289bff2011-06-13 19:33:22 -07001049 requiresSmallestWidthDp = getIntegerAttribute(tree,
1050 REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
1051 compatibleWidthLimitDp = getIntegerAttribute(tree,
1052 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
1053 largestWidthLimitDp = getIntegerAttribute(tree,
1054 LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
Dianne Hackborne5276a72009-08-27 16:28:44 -07001055 } else if (tag == "uses-feature") {
1056 String8 name = getAttribute(tree, NAME_ATTR, &error);
Suchi Amalapurapu40b94722009-09-20 13:39:37 -07001057
1058 if (name != "" && error == "") {
Dianne Hackborne5276a72009-08-27 16:28:44 -07001059 int req = getIntegerAttribute(tree,
1060 REQUIRED_ATTR, NULL, 1);
Dan Morrill89d97c12010-05-03 16:13:14 -07001061
Dianne Hackborne5276a72009-08-27 16:28:44 -07001062 if (name == "android.hardware.camera") {
1063 specCameraFeature = true;
Dan Morrill89d97c12010-05-03 16:13:14 -07001064 } else if (name == "android.hardware.camera.autofocus") {
1065 // these have no corresponding permission to check for,
1066 // but should imply the foundational camera permission
1067 reqCameraAutofocusFeature = reqCameraAutofocusFeature || req;
1068 specCameraAutofocusFeature = true;
1069 } else if (req && (name == "android.hardware.camera.flash")) {
1070 // these have no corresponding permission to check for,
1071 // but should imply the foundational camera permission
1072 reqCameraFlashFeature = true;
1073 } else if (name == "android.hardware.location") {
1074 specLocationFeature = true;
1075 } else if (name == "android.hardware.location.network") {
1076 specNetworkLocFeature = true;
1077 reqNetworkLocFeature = reqNetworkLocFeature || req;
Dianne Hackbornef05e072010-03-01 17:43:39 -08001078 } else if (name == "android.hardware.location.gps") {
1079 specGpsFeature = true;
Dan Morrill89d97c12010-05-03 16:13:14 -07001080 reqGpsFeature = reqGpsFeature || req;
1081 } else if (name == "android.hardware.bluetooth") {
1082 specBluetoothFeature = true;
1083 } else if (name == "android.hardware.touchscreen") {
1084 specTouchscreenFeature = true;
1085 } else if (name == "android.hardware.touchscreen.multitouch") {
1086 specMultitouchFeature = true;
1087 } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
1088 reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req;
1089 } else if (name == "android.hardware.microphone") {
1090 specMicrophoneFeature = true;
1091 } else if (name == "android.hardware.wifi") {
1092 specWiFiFeature = true;
1093 } else if (name == "android.hardware.telephony") {
1094 specTelephonyFeature = true;
1095 } else if (req && (name == "android.hardware.telephony.gsm" ||
1096 name == "android.hardware.telephony.cdma")) {
1097 // these have no corresponding permission to check for,
1098 // but should imply the foundational telephony permission
1099 reqTelephonySubFeature = true;
Dianne Hackborne289bff2011-06-13 19:33:22 -07001100 } else if (name == "android.hardware.screen.portrait") {
1101 specScreenPortraitFeature = true;
1102 } else if (name == "android.hardware.screen.landscape") {
1103 specScreenLandscapeFeature = true;
Dianne Hackborne5276a72009-08-27 16:28:44 -07001104 }
1105 printf("uses-feature%s:'%s'\n",
1106 req ? "" : "-not-required", name.string());
1107 } else {
1108 int vers = getIntegerAttribute(tree,
1109 GL_ES_VERSION_ATTR, &error);
1110 if (error == "") {
1111 printf("uses-gl-es:'0x%x'\n", vers);
1112 }
1113 }
1114 } else if (tag == "uses-permission") {
1115 String8 name = getAttribute(tree, NAME_ATTR, &error);
Suchi Amalapurapu40b94722009-09-20 13:39:37 -07001116 if (name != "" && error == "") {
Dianne Hackborne5276a72009-08-27 16:28:44 -07001117 if (name == "android.permission.CAMERA") {
1118 hasCameraPermission = true;
Dianne Hackbornef05e072010-03-01 17:43:39 -08001119 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
1120 hasGpsPermission = true;
Dan Morrill89d97c12010-05-03 16:13:14 -07001121 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
1122 hasMockLocPermission = true;
1123 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
1124 hasCoarseLocPermission = true;
1125 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
1126 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
1127 hasGeneralLocPermission = true;
1128 } else if (name == "android.permission.BLUETOOTH" ||
1129 name == "android.permission.BLUETOOTH_ADMIN") {
1130 hasBluetoothPermission = true;
1131 } else if (name == "android.permission.RECORD_AUDIO") {
1132 hasRecordAudioPermission = true;
1133 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1134 name == "android.permission.CHANGE_WIFI_STATE" ||
1135 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
1136 hasWiFiPermission = true;
1137 } else if (name == "android.permission.CALL_PHONE" ||
1138 name == "android.permission.CALL_PRIVILEGED" ||
1139 name == "android.permission.MODIFY_PHONE_STATE" ||
1140 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1141 name == "android.permission.READ_SMS" ||
1142 name == "android.permission.RECEIVE_SMS" ||
1143 name == "android.permission.RECEIVE_MMS" ||
1144 name == "android.permission.RECEIVE_WAP_PUSH" ||
1145 name == "android.permission.SEND_SMS" ||
1146 name == "android.permission.WRITE_APN_SETTINGS" ||
1147 name == "android.permission.WRITE_SMS") {
1148 hasTelephonyPermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001149 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1150 hasWriteExternalStoragePermission = true;
Dianne Hackborn79245122012-03-12 10:51:26 -07001151 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1152 hasReadExternalStoragePermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001153 } else if (name == "android.permission.READ_PHONE_STATE") {
1154 hasReadPhoneStatePermission = true;
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001155 } else if (name == "android.permission.READ_CONTACTS") {
1156 hasReadContactsPermission = true;
1157 } else if (name == "android.permission.WRITE_CONTACTS") {
1158 hasWriteContactsPermission = true;
1159 } else if (name == "android.permission.READ_CALL_LOG") {
1160 hasReadCallLogPermission = true;
1161 } else if (name == "android.permission.WRITE_CALL_LOG") {
1162 hasWriteCallLogPermission = true;
Dianne Hackborne5276a72009-08-27 16:28:44 -07001163 }
1164 printf("uses-permission:'%s'\n", name.string());
Nick Kralevich1bcc3d62013-04-03 09:00:02 -07001165 int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
1166 if (!req) {
1167 printf("optional-permission:'%s'\n", name.string());
1168 }
Dianne Hackborne5276a72009-08-27 16:28:44 -07001169 } else {
1170 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1171 error.string());
1172 goto bail;
1173 }
Dianne Hackborn43b68032010-09-02 17:14:41 -07001174 } else if (tag == "uses-package") {
1175 String8 name = getAttribute(tree, NAME_ATTR, &error);
1176 if (name != "" && error == "") {
1177 printf("uses-package:'%s'\n", name.string());
1178 } else {
1179 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1180 error.string());
1181 goto bail;
1182 }
Jeff Hamiltone2c17f92010-02-12 13:45:16 -06001183 } else if (tag == "original-package") {
1184 String8 name = getAttribute(tree, NAME_ATTR, &error);
1185 if (name != "" && error == "") {
1186 printf("original-package:'%s'\n", name.string());
1187 } else {
1188 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1189 error.string());
1190 goto bail;
1191 }
Dan Morrill096b67f2010-12-13 16:25:54 -08001192 } else if (tag == "supports-gl-texture") {
Dan Morrill6f51fc12010-10-13 14:33:43 -07001193 String8 name = getAttribute(tree, NAME_ATTR, &error);
1194 if (name != "" && error == "") {
Dan Morrill096b67f2010-12-13 16:25:54 -08001195 printf("supports-gl-texture:'%s'\n", name.string());
Dan Morrill6f51fc12010-10-13 14:33:43 -07001196 } else {
1197 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1198 error.string());
1199 goto bail;
1200 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001201 } else if (tag == "compatible-screens") {
1202 printCompatibleScreens(tree);
1203 depth--;
Kenny Root56088a52011-09-29 13:49:45 -07001204 } else if (tag == "package-verifier") {
1205 String8 name = getAttribute(tree, NAME_ATTR, &error);
1206 if (name != "" && error == "") {
1207 String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
1208 if (publicKey != "" && error == "") {
1209 printf("package-verifier: name='%s' publicKey='%s'\n",
1210 name.string(), publicKey.string());
1211 }
1212 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001213 }
Michael Wrighteaeb1902013-09-05 18:15:57 -07001214 } else if (depth == 3) {
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001215 withinActivity = false;
1216 withinReceiver = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001217 withinService = false;
1218 hasIntentFilter = false;
Adam Lesinskid831e802013-09-27 11:14:57 -07001219 hasMetaHostPaymentCategory = false;
1220 hasMetaOffHostPaymentCategory = false;
1221 hasBindDeviceAdminPermission = false;
1222 hasBindInputMethodPermission = false;
1223 hasBindAccessibilityServicePermission = false;
1224 hasBindPrintServicePermission = false;
1225 hasBindNfcServicePermission = false;
Michael Wrighteaeb1902013-09-05 18:15:57 -07001226 if (withinApplication) {
1227 if(tag == "activity") {
1228 withinActivity = true;
1229 activityName = getAttribute(tree, NAME_ATTR, &error);
1230 if (error != "") {
1231 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1232 error.string());
1233 goto bail;
1234 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001235
Michael Wrighteaeb1902013-09-05 18:15:57 -07001236 activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1237 if (error != "") {
1238 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
1239 error.string());
1240 goto bail;
1241 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001242
Michael Wrighteaeb1902013-09-05 18:15:57 -07001243 activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1244 if (error != "") {
1245 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1246 error.string());
1247 goto bail;
1248 }
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001249
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001250 activityBanner = getResolvedAttribute(&res, tree, BANNER_ATTR, &error);
1251 if (error != "") {
1252 fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
1253 error.string());
1254 goto bail;
1255 }
1256
Michael Wrighteaeb1902013-09-05 18:15:57 -07001257 int32_t orien = getResolvedIntegerAttribute(&res, tree,
1258 SCREEN_ORIENTATION_ATTR, &error);
1259 if (error == "") {
1260 if (orien == 0 || orien == 6 || orien == 8) {
1261 // Requests landscape, sensorLandscape, or reverseLandscape.
1262 reqScreenLandscapeFeature = true;
1263 } else if (orien == 1 || orien == 7 || orien == 9) {
1264 // Requests portrait, sensorPortrait, or reversePortrait.
1265 reqScreenPortraitFeature = true;
1266 }
1267 }
1268 } else if (tag == "uses-library") {
1269 String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1270 if (error != "") {
1271 fprintf(stderr,
1272 "ERROR getting 'android:name' attribute for uses-library"
1273 " %s\n", error.string());
1274 goto bail;
1275 }
1276 int req = getIntegerAttribute(tree,
1277 REQUIRED_ATTR, NULL, 1);
1278 printf("uses-library%s:'%s'\n",
1279 req ? "" : "-not-required", libraryName.string());
1280 } else if (tag == "receiver") {
1281 withinReceiver = true;
1282 receiverName = getAttribute(tree, NAME_ATTR, &error);
1283
1284 if (error != "") {
1285 fprintf(stderr,
1286 "ERROR getting 'android:name' attribute for receiver:"
1287 " %s\n", error.string());
1288 goto bail;
1289 }
Adam Lesinskib1249b82013-09-25 17:03:50 -07001290
1291 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1292 if (error == "") {
1293 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1294 hasBindDeviceAdminPermission = true;
1295 }
1296 } else {
1297 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1298 " receiver '%s': %s\n", receiverName.string(), error.string());
1299 }
Michael Wrighteaeb1902013-09-05 18:15:57 -07001300 } else if (tag == "service") {
1301 withinService = true;
1302 serviceName = getAttribute(tree, NAME_ATTR, &error);
1303
1304 if (error != "") {
1305 fprintf(stderr, "ERROR getting 'android:name' attribute for"
1306 " service: %s\n", error.string());
1307 goto bail;
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001308 }
Adam Lesinskib1249b82013-09-25 17:03:50 -07001309
1310 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1311 if (error == "") {
1312 if (permission == "android.permission.BIND_INPUT_METHOD") {
1313 hasBindInputMethodPermission = true;
1314 } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") {
1315 hasBindAccessibilityServicePermission = true;
1316 } else if (permission == "android.permission.BIND_PRINT_SERVICE") {
1317 hasBindPrintServicePermission = true;
Adam Lesinskid831e802013-09-27 11:14:57 -07001318 } else if (permission == "android.permission.BIND_NFC_SERVICE") {
1319 hasBindNfcServicePermission = true;
Adam Lesinskib1249b82013-09-25 17:03:50 -07001320 }
1321 } else {
1322 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1323 " service '%s': %s\n", serviceName.string(), error.string());
1324 }
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001325 }
Michael Wrighteaeb1902013-09-05 18:15:57 -07001326 } else if (withinSupportsInput && tag == "input-type") {
1327 String8 name = getAttribute(tree, NAME_ATTR, &error);
1328 if (name != "" && error == "") {
1329 supportedInput.add(name);
1330 } else {
1331 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1332 error.string());
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001333 goto bail;
1334 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001335 }
Adam Lesinskid831e802013-09-27 11:14:57 -07001336 } else if (depth == 4) {
1337 if (tag == "intent-filter") {
1338 hasIntentFilter = true;
1339 withinIntentFilter = true;
1340 actMainActivity = false;
1341 actWidgetReceivers = false;
1342 actImeService = false;
1343 actWallpaperService = false;
1344 actAccessibilityService = false;
1345 actPrintService = false;
1346 actDeviceAdminEnabled = false;
1347 actHostApduService = false;
1348 actOffHostApduService = false;
1349 } else if (withinService && tag == "meta-data") {
1350 String8 name = getAttribute(tree, NAME_ATTR, &error);
1351 if (error != "") {
1352 fprintf(stderr, "ERROR getting 'android:name' attribute for"
1353 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1354 goto bail;
1355 }
1356
1357 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1358 name == "android.nfc.cardemulation.off_host_apdu_service") {
1359 bool offHost = true;
1360 if (name == "android.nfc.cardemulation.host_apdu_service") {
1361 offHost = false;
1362 }
1363
1364 String8 xmlPath = getResolvedAttribute(&res, tree, RESOURCE_ATTR, &error);
1365 if (error != "") {
1366 fprintf(stderr, "ERROR getting 'android:resource' attribute for"
1367 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1368 goto bail;
1369 }
1370
1371 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1372 offHost, &error);
1373 if (error != "") {
1374 fprintf(stderr, "ERROR getting AID category for service '%s'\n",
1375 serviceName.string());
1376 goto bail;
1377 }
1378
1379 const size_t catLen = categories.size();
1380 for (size_t i = 0; i < catLen; i++) {
1381 bool paymentCategory = (categories[i] == "payment");
1382 if (offHost) {
1383 hasMetaOffHostPaymentCategory |= paymentCategory;
1384 } else {
1385 hasMetaHostPaymentCategory |= paymentCategory;
1386 }
1387 }
1388 }
1389 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001390 } else if ((depth == 5) && withinIntentFilter){
1391 String8 action;
1392 if (tag == "action") {
1393 action = getAttribute(tree, NAME_ATTR, &error);
1394 if (error != "") {
1395 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1396 goto bail;
1397 }
1398 if (withinActivity) {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001399 if (action == "android.intent.action.MAIN") {
1400 isMainActivity = true;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001401 actMainActivity = true;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001402 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001403 } else if (withinReceiver) {
1404 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1405 actWidgetReceivers = true;
Adam Lesinskib1249b82013-09-25 17:03:50 -07001406 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1407 actDeviceAdminEnabled = true;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001408 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001409 } else if (withinService) {
1410 if (action == "android.view.InputMethod") {
1411 actImeService = true;
1412 } else if (action == "android.service.wallpaper.WallpaperService") {
1413 actWallpaperService = true;
Adam Lesinskib1249b82013-09-25 17:03:50 -07001414 } else if (action == "android.accessibilityservice.AccessibilityService") {
1415 actAccessibilityService = true;
1416 } else if (action == "android.printservice.PrintService") {
1417 actPrintService = true;
Adam Lesinskid831e802013-09-27 11:14:57 -07001418 } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
1419 actHostApduService = true;
1420 } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
1421 actOffHostApduService = true;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001422 }
1423 }
1424 if (action == "android.intent.action.SEARCH") {
1425 isSearchable = true;
1426 }
1427 }
1428
1429 if (tag == "category") {
1430 String8 category = getAttribute(tree, NAME_ATTR, &error);
1431 if (error != "") {
1432 fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string());
1433 goto bail;
1434 }
1435 if (withinActivity) {
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001436 if (category == "android.intent.category.LAUNCHER") {
1437 isLauncherActivity = true;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001438 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
1439 isLeanbackLauncherActivity = true;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001440 }
1441 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001442 }
1443 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001444 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001445
Kenny Root063a44e2011-12-08 08:46:03 -08001446 // Pre-1.6 implicitly granted permission compatibility logic
1447 if (targetSdk < 4) {
1448 if (!hasWriteExternalStoragePermission) {
1449 printf("uses-permission:'android.permission.WRITE_EXTERNAL_STORAGE'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001450 printf("uses-implied-permission:'android.permission.WRITE_EXTERNAL_STORAGE'," \
1451 "'targetSdkVersion < 4'\n");
Dianne Hackborn79245122012-03-12 10:51:26 -07001452 hasWriteExternalStoragePermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001453 }
1454 if (!hasReadPhoneStatePermission) {
1455 printf("uses-permission:'android.permission.READ_PHONE_STATE'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001456 printf("uses-implied-permission:'android.permission.READ_PHONE_STATE'," \
1457 "'targetSdkVersion < 4'\n");
Kenny Root063a44e2011-12-08 08:46:03 -08001458 }
1459 }
1460
Dianne Hackborn79245122012-03-12 10:51:26 -07001461 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
Dianne Hackborn2bd8d042012-06-11 12:27:05 -07001462 // force them to always take READ_EXTERNAL_STORAGE as well. We always
1463 // do this (regardless of target API version) because we can't have
1464 // an app with write permission but not read permission.
Dianne Hackborn79245122012-03-12 10:51:26 -07001465 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
1466 printf("uses-permission:'android.permission.READ_EXTERNAL_STORAGE'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001467 printf("uses-implied-permission:'android.permission.READ_EXTERNAL_STORAGE'," \
1468 "'requested WRITE_EXTERNAL_STORAGE'\n");
Dianne Hackborn79245122012-03-12 10:51:26 -07001469 }
1470
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001471 // Pre-JellyBean call log permission compatibility.
1472 if (targetSdk < 16) {
1473 if (!hasReadCallLogPermission && hasReadContactsPermission) {
1474 printf("uses-permission:'android.permission.READ_CALL_LOG'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001475 printf("uses-implied-permission:'android.permission.READ_CALL_LOG'," \
1476 "'targetSdkVersion < 16 and requested READ_CONTACTS'\n");
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001477 }
1478 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
1479 printf("uses-permission:'android.permission.WRITE_CALL_LOG'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001480 printf("uses-implied-permission:'android.permission.WRITE_CALL_LOG'," \
1481 "'targetSdkVersion < 16 and requested WRITE_CONTACTS'\n");
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001482 }
1483 }
1484
Dan Morrill89d97c12010-05-03 16:13:14 -07001485 /* The following blocks handle printing "inferred" uses-features, based
1486 * on whether related features or permissions are used by the app.
1487 * Note that the various spec*Feature variables denote whether the
1488 * relevant tag was *present* in the AndroidManfest, not that it was
1489 * present and set to true.
1490 */
1491 // Camera-related back-compatibility logic
1492 if (!specCameraFeature) {
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001493 if (reqCameraFlashFeature) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001494 // if app requested a sub-feature (autofocus or flash) and didn't
1495 // request the base camera feature, we infer that it meant to
1496 printf("uses-feature:'android.hardware.camera'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001497 printf("uses-implied-feature:'android.hardware.camera'," \
1498 "'requested android.hardware.camera.flash feature'\n");
1499 } else if (reqCameraAutofocusFeature) {
1500 // if app requested a sub-feature (autofocus or flash) and didn't
1501 // request the base camera feature, we infer that it meant to
1502 printf("uses-feature:'android.hardware.camera'\n");
1503 printf("uses-implied-feature:'android.hardware.camera'," \
1504 "'requested android.hardware.camera.autofocus feature'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001505 } else if (hasCameraPermission) {
1506 // if app wants to use camera but didn't request the feature, we infer
1507 // that it meant to, and further that it wants autofocus
1508 // (which was the 1.0 - 1.5 behavior)
1509 printf("uses-feature:'android.hardware.camera'\n");
1510 if (!specCameraAutofocusFeature) {
1511 printf("uses-feature:'android.hardware.camera.autofocus'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001512 printf("uses-implied-feature:'android.hardware.camera.autofocus'," \
1513 "'requested android.permission.CAMERA permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001514 }
1515 }
Dianne Hackborne5276a72009-08-27 16:28:44 -07001516 }
Doug Zongkerdbe7a682009-10-09 11:24:51 -07001517
Dan Morrill89d97c12010-05-03 16:13:14 -07001518 // Location-related back-compatibility logic
1519 if (!specLocationFeature &&
1520 (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission ||
1521 hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) {
1522 // if app either takes a location-related permission or requests one of the
1523 // sub-features, we infer that it also meant to request the base location feature
1524 printf("uses-feature:'android.hardware.location'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001525 printf("uses-implied-feature:'android.hardware.location'," \
1526 "'requested a location access permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001527 }
Dianne Hackbornef05e072010-03-01 17:43:39 -08001528 if (!specGpsFeature && hasGpsPermission) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001529 // if app takes GPS (FINE location) perm but does not request the GPS
1530 // feature, we infer that it meant to
Dianne Hackbornef05e072010-03-01 17:43:39 -08001531 printf("uses-feature:'android.hardware.location.gps'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001532 printf("uses-implied-feature:'android.hardware.location.gps'," \
1533 "'requested android.permission.ACCESS_FINE_LOCATION permission'\n");
Dianne Hackbornef05e072010-03-01 17:43:39 -08001534 }
Dan Morrill89d97c12010-05-03 16:13:14 -07001535 if (!specNetworkLocFeature && hasCoarseLocPermission) {
1536 // if app takes Network location (COARSE location) perm but does not request the
1537 // network location feature, we infer that it meant to
1538 printf("uses-feature:'android.hardware.location.network'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001539 printf("uses-implied-feature:'android.hardware.location.network'," \
Dianne Hackbornec559ff2013-02-25 15:42:07 -08001540 "'requested android.permission.ACCESS_COARSE_LOCATION permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001541 }
1542
1543 // Bluetooth-related compatibility logic
Dan Morrill6b22d812010-06-15 21:41:42 -07001544 if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001545 // if app takes a Bluetooth permission but does not request the Bluetooth
1546 // feature, we infer that it meant to
1547 printf("uses-feature:'android.hardware.bluetooth'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001548 printf("uses-implied-feature:'android.hardware.bluetooth'," \
1549 "'requested android.permission.BLUETOOTH or android.permission.BLUETOOTH_ADMIN " \
1550 "permission and targetSdkVersion > 4'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001551 }
1552
1553 // Microphone-related compatibility logic
1554 if (!specMicrophoneFeature && hasRecordAudioPermission) {
1555 // if app takes the record-audio permission but does not request the microphone
1556 // feature, we infer that it meant to
1557 printf("uses-feature:'android.hardware.microphone'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001558 printf("uses-implied-feature:'android.hardware.microphone'," \
1559 "'requested android.permission.RECORD_AUDIO permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001560 }
1561
1562 // WiFi-related compatibility logic
1563 if (!specWiFiFeature && hasWiFiPermission) {
1564 // if app takes one of the WiFi permissions but does not request the WiFi
1565 // feature, we infer that it meant to
1566 printf("uses-feature:'android.hardware.wifi'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001567 printf("uses-implied-feature:'android.hardware.wifi'," \
1568 "'requested android.permission.ACCESS_WIFI_STATE, " \
1569 "android.permission.CHANGE_WIFI_STATE, or " \
1570 "android.permission.CHANGE_WIFI_MULTICAST_STATE permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001571 }
1572
1573 // Telephony-related compatibility logic
1574 if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) {
1575 // if app takes one of the telephony permissions or requests a sub-feature but
1576 // does not request the base telephony feature, we infer that it meant to
1577 printf("uses-feature:'android.hardware.telephony'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001578 printf("uses-implied-feature:'android.hardware.telephony'," \
1579 "'requested a telephony-related permission or feature'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001580 }
1581
1582 // Touchscreen-related back-compatibility logic
1583 if (!specTouchscreenFeature) { // not a typo!
1584 // all apps are presumed to require a touchscreen, unless they explicitly say
1585 // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
1586 // Note that specTouchscreenFeature is true if the tag is present, regardless
1587 // of whether its value is true or false, so this is safe
1588 printf("uses-feature:'android.hardware.touchscreen'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001589 printf("uses-implied-feature:'android.hardware.touchscreen'," \
1590 "'assumed you require a touch screen unless explicitly made optional'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001591 }
1592 if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
1593 // if app takes one of the telephony permissions or requests a sub-feature but
1594 // does not request the base telephony feature, we infer that it meant to
1595 printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001596 printf("uses-implied-feature:'android.hardware.touchscreen.multitouch'," \
1597 "'requested android.hardware.touchscreen.multitouch.distinct feature'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001598 }
Dianne Hackbornef05e072010-03-01 17:43:39 -08001599
Dianne Hackborne289bff2011-06-13 19:33:22 -07001600 // Landscape/portrait-related compatibility logic
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001601 if (!specScreenLandscapeFeature && !specScreenPortraitFeature) {
1602 // If the app has specified any activities in its manifest
1603 // that request a specific orientation, then assume that
1604 // orientation is required.
1605 if (reqScreenLandscapeFeature) {
1606 printf("uses-feature:'android.hardware.screen.landscape'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001607 printf("uses-implied-feature:'android.hardware.screen.landscape'," \
1608 "'one or more activities have specified a landscape orientation'\n");
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001609 }
1610 if (reqScreenPortraitFeature) {
1611 printf("uses-feature:'android.hardware.screen.portrait'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001612 printf("uses-implied-feature:'android.hardware.screen.portrait'," \
1613 "'one or more activities have specified a portrait orientation'\n");
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001614 }
Dianne Hackborne289bff2011-06-13 19:33:22 -07001615 }
1616
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001617 if (hasMainActivity) {
1618 printf("main\n");
1619 }
1620 if (hasWidgetReceivers) {
1621 printf("app-widget\n");
1622 }
Adam Lesinskib1249b82013-09-25 17:03:50 -07001623 if (hasDeviceAdminReceiver) {
1624 printf("device-admin\n");
1625 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001626 if (hasImeService) {
1627 printf("ime\n");
1628 }
1629 if (hasWallpaperService) {
1630 printf("wallpaper\n");
1631 }
Adam Lesinskib1249b82013-09-25 17:03:50 -07001632 if (hasAccessibilityService) {
1633 printf("accessibility\n");
1634 }
1635 if (hasPrintService) {
1636 printf("print\n");
1637 }
Adam Lesinskid831e802013-09-27 11:14:57 -07001638 if (hasPaymentService) {
1639 printf("payment\n");
1640 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001641 if (hasOtherActivities) {
1642 printf("other-activities\n");
1643 }
1644 if (isSearchable) {
1645 printf("search\n");
1646 }
1647 if (hasOtherReceivers) {
1648 printf("other-receivers\n");
1649 }
1650 if (hasOtherServices) {
1651 printf("other-services\n");
1652 }
1653
Dianne Hackborne289bff2011-06-13 19:33:22 -07001654 // For modern apps, if screen size buckets haven't been specified
1655 // but the new width ranges have, then infer the buckets from them.
1656 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1657 && requiresSmallestWidthDp > 0) {
1658 int compatWidth = compatibleWidthLimitDp;
1659 if (compatWidth <= 0) compatWidth = requiresSmallestWidthDp;
1660 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1661 smallScreen = -1;
1662 } else {
1663 smallScreen = 0;
1664 }
1665 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1666 normalScreen = -1;
1667 } else {
1668 normalScreen = 0;
1669 }
1670 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1671 largeScreen = -1;
1672 } else {
1673 largeScreen = 0;
1674 }
1675 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1676 xlargeScreen = -1;
1677 } else {
1678 xlargeScreen = 0;
1679 }
1680 }
1681
Dianne Hackborn723738c2009-06-25 19:48:04 -07001682 // Determine default values for any unspecified screen sizes,
1683 // based on the target SDK of the package. As of 4 (donut)
1684 // the screen size support was introduced, so all default to
1685 // enabled.
1686 if (smallScreen > 0) {
1687 smallScreen = targetSdk >= 4 ? -1 : 0;
1688 }
1689 if (normalScreen > 0) {
1690 normalScreen = -1;
1691 }
1692 if (largeScreen > 0) {
1693 largeScreen = targetSdk >= 4 ? -1 : 0;
1694 }
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001695 if (xlargeScreen > 0) {
Scott Maind58fb972010-11-04 18:32:00 -07001696 // Introduced in Gingerbread.
1697 xlargeScreen = targetSdk >= 9 ? -1 : 0;
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001698 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001699 if (anyDensity > 0) {
Dianne Hackborne289bff2011-06-13 19:33:22 -07001700 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1701 || compatibleWidthLimitDp > 0) ? -1 : 0;
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001702 }
Dianne Hackborn723738c2009-06-25 19:48:04 -07001703 printf("supports-screens:");
1704 if (smallScreen != 0) printf(" 'small'");
1705 if (normalScreen != 0) printf(" 'normal'");
1706 if (largeScreen != 0) printf(" 'large'");
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001707 if (xlargeScreen != 0) printf(" 'xlarge'");
Dianne Hackborn723738c2009-06-25 19:48:04 -07001708 printf("\n");
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001709 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
Dianne Hackborne289bff2011-06-13 19:33:22 -07001710 if (requiresSmallestWidthDp > 0) {
1711 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1712 }
1713 if (compatibleWidthLimitDp > 0) {
1714 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1715 }
1716 if (largestWidthLimitDp > 0) {
1717 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1718 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001719
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001720 printf("locales:");
Dianne Hackborne17086b2009-06-19 15:13:28 -07001721 const size_t NL = locales.size();
1722 for (size_t i=0; i<NL; i++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001723 const char* localeStr = locales[i].string();
1724 if (localeStr == NULL || strlen(localeStr) == 0) {
1725 localeStr = "--_--";
1726 }
1727 printf(" '%s'", localeStr);
1728 }
1729 printf("\n");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001730
Dianne Hackborne17086b2009-06-19 15:13:28 -07001731 printf("densities:");
1732 const size_t ND = densities.size();
1733 for (size_t i=0; i<ND; i++) {
1734 printf(" '%d'", densities[i]);
1735 }
1736 printf("\n");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001737
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001738 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1739 if (dir != NULL) {
1740 if (dir->getFileCount() > 0) {
1741 printf("native-code:");
1742 for (size_t i=0; i<dir->getFileCount(); i++) {
1743 printf(" '%s'", dir->getFileName(i).string());
1744 }
1745 printf("\n");
1746 }
1747 delete dir;
1748 }
Dan Morrille74763e2012-01-06 10:47:10 -08001749 } else if (strcmp("badger", option) == 0) {
Dianne Hackborn6c997a92012-01-31 11:27:43 -08001750 printf("%s", CONSOLE_DATA);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001751 } else if (strcmp("configurations", option) == 0) {
1752 Vector<ResTable_config> configs;
1753 res.getConfigurations(&configs);
1754 const size_t N = configs.size();
1755 for (size_t i=0; i<N; i++) {
1756 printf("%s\n", configs[i].toString().string());
1757 }
1758 } else {
1759 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
1760 goto bail;
1761 }
1762 }
1763
1764 result = NO_ERROR;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001765
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001766bail:
1767 if (asset) {
1768 delete asset;
1769 }
1770 return (result != NO_ERROR);
1771}
1772
1773
1774/*
1775 * Handle the "add" command, which wants to add files to a new or
1776 * pre-existing archive.
1777 */
1778int doAdd(Bundle* bundle)
1779{
1780 ZipFile* zip = NULL;
1781 status_t result = UNKNOWN_ERROR;
1782 const char* zipFileName;
1783
1784 if (bundle->getUpdate()) {
1785 /* avoid confusion */
1786 fprintf(stderr, "ERROR: can't use '-u' with add\n");
1787 goto bail;
1788 }
1789
1790 if (bundle->getFileSpecCount() < 1) {
1791 fprintf(stderr, "ERROR: must specify zip file name\n");
1792 goto bail;
1793 }
1794 zipFileName = bundle->getFileSpecEntry(0);
1795
1796 if (bundle->getFileSpecCount() < 2) {
1797 fprintf(stderr, "NOTE: nothing to do\n");
1798 goto bail;
1799 }
1800
1801 zip = openReadWrite(zipFileName, true);
1802 if (zip == NULL) {
1803 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
1804 goto bail;
1805 }
1806
1807 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1808 const char* fileName = bundle->getFileSpecEntry(i);
1809
1810 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
1811 printf(" '%s'... (from gzip)\n", fileName);
1812 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
1813 } else {
Doug Zongkerdbe7a682009-10-09 11:24:51 -07001814 if (bundle->getJunkPath()) {
1815 String8 storageName = String8(fileName).getPathLeaf();
1816 printf(" '%s' as '%s'...\n", fileName, storageName.string());
1817 result = zip->add(fileName, storageName.string(),
1818 bundle->getCompressionMethod(), NULL);
1819 } else {
1820 printf(" '%s'...\n", fileName);
1821 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
1822 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001823 }
1824 if (result != NO_ERROR) {
1825 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
1826 if (result == NAME_NOT_FOUND)
1827 fprintf(stderr, ": file not found\n");
1828 else if (result == ALREADY_EXISTS)
1829 fprintf(stderr, ": already exists in archive\n");
1830 else
1831 fprintf(stderr, "\n");
1832 goto bail;
1833 }
1834 }
1835
1836 result = NO_ERROR;
1837
1838bail:
1839 delete zip;
1840 return (result != NO_ERROR);
1841}
1842
1843
1844/*
1845 * Delete files from an existing archive.
1846 */
1847int doRemove(Bundle* bundle)
1848{
1849 ZipFile* zip = NULL;
1850 status_t result = UNKNOWN_ERROR;
1851 const char* zipFileName;
1852
1853 if (bundle->getFileSpecCount() < 1) {
1854 fprintf(stderr, "ERROR: must specify zip file name\n");
1855 goto bail;
1856 }
1857 zipFileName = bundle->getFileSpecEntry(0);
1858
1859 if (bundle->getFileSpecCount() < 2) {
1860 fprintf(stderr, "NOTE: nothing to do\n");
1861 goto bail;
1862 }
1863
1864 zip = openReadWrite(zipFileName, false);
1865 if (zip == NULL) {
1866 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
1867 zipFileName);
1868 goto bail;
1869 }
1870
1871 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1872 const char* fileName = bundle->getFileSpecEntry(i);
1873 ZipEntry* entry;
1874
1875 entry = zip->getEntryByName(fileName);
1876 if (entry == NULL) {
1877 printf(" '%s' NOT FOUND\n", fileName);
1878 continue;
1879 }
1880
1881 result = zip->remove(entry);
1882
1883 if (result != NO_ERROR) {
1884 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
1885 bundle->getFileSpecEntry(i), zipFileName);
1886 goto bail;
1887 }
1888 }
1889
1890 /* update the archive */
1891 zip->flush();
1892
1893bail:
1894 delete zip;
1895 return (result != NO_ERROR);
1896}
1897
1898
1899/*
1900 * Package up an asset directory and associated application files.
1901 */
1902int doPackage(Bundle* bundle)
1903{
1904 const char* outputAPKFile;
1905 int retVal = 1;
1906 status_t err;
1907 sp<AaptAssets> assets;
1908 int N;
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001909 FILE* fp;
1910 String8 dependencyFile;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001911
Anton Krumina2ef5c02014-03-12 14:46:44 -07001912 // -c en_XA or/and ar_XB means do pseudolocalization
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001913 ResourceFilter filter;
1914 err = filter.parse(bundle->getConfigurations());
1915 if (err != NO_ERROR) {
1916 goto bail;
1917 }
1918 if (filter.containsPseudo()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07001919 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
1920 }
1921 if (filter.containsPseudoBidi()) {
1922 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001923 }
1924
1925 N = bundle->getFileSpecCount();
1926 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
Adam Lesinski09384302014-01-22 16:07:42 -08001927 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001928 fprintf(stderr, "ERROR: no input files\n");
1929 goto bail;
1930 }
1931
1932 outputAPKFile = bundle->getOutputAPKFile();
1933
1934 // Make sure the filenames provided exist and are of the appropriate type.
1935 if (outputAPKFile) {
1936 FileType type;
1937 type = getFileType(outputAPKFile);
1938 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
1939 fprintf(stderr,
1940 "ERROR: output file '%s' exists but is not regular file\n",
1941 outputAPKFile);
1942 goto bail;
1943 }
1944 }
1945
1946 // Load the assets.
1947 assets = new AaptAssets();
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001948
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001949 // Set up the resource gathering in assets if we're going to generate
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001950 // dependency files. Every time we encounter a resource while slurping
1951 // the tree, we'll add it to these stores so we have full resource paths
1952 // to write to a dependency file.
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001953 if (bundle->getGenDependencies()) {
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001954 sp<FilePathStore> resPathStore = new FilePathStore;
1955 assets->setFullResPaths(resPathStore);
1956 sp<FilePathStore> assetPathStore = new FilePathStore;
1957 assets->setFullAssetPaths(assetPathStore);
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001958 }
1959
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001960 err = assets->slurpFromArgs(bundle);
1961 if (err < 0) {
1962 goto bail;
1963 }
1964
1965 if (bundle->getVerbose()) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001966 assets->print(String8());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001967 }
1968
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001969 // If they asked for any fileAs that need to be compiled, do so.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001970 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
1971 err = buildResources(bundle, assets);
1972 if (err != 0) {
1973 goto bail;
1974 }
1975 }
1976
1977 // At this point we've read everything and processed everything. From here
1978 // on out it's just writing output files.
1979 if (SourcePos::hasErrors()) {
1980 goto bail;
1981 }
1982
Dianne Hackborn1644c6d72012-02-06 15:33:21 -08001983 // Update symbols with information about which ones are needed as Java symbols.
1984 assets->applyJavaSymbols();
1985 if (SourcePos::hasErrors()) {
1986 goto bail;
1987 }
1988
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001989 // If we've been asked to generate a dependency file, do that here
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001990 if (bundle->getGenDependencies()) {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001991 // If this is the packaging step, generate the dependency file next to
1992 // the output apk (e.g. bin/resources.ap_.d)
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001993 if (outputAPKFile) {
1994 dependencyFile = String8(outputAPKFile);
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001995 // Add the .d extension to the dependency file.
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001996 dependencyFile.append(".d");
1997 } else {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001998 // Else if this is the R.java dependency generation step,
1999 // generate the dependency file in the R.java package subdirectory
2000 // e.g. gen/com/foo/app/R.java.d
Josiah Gaskin03589cc2011-06-27 16:26:02 -07002001 dependencyFile = String8(bundle->getRClassDir());
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07002002 dependencyFile.appendPath("R.java.d");
Josiah Gaskin03589cc2011-06-27 16:26:02 -07002003 }
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07002004 // Make sure we have a clean dependency file to start with
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07002005 fp = fopen(dependencyFile, "w");
2006 fclose(fp);
2007 }
2008
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002009 // Write out R.java constants
Dianne Hackborn1644c6d72012-02-06 15:33:21 -08002010 if (!assets->havePrivateSymbols()) {
Xavier Ducrohet63459ad2009-11-30 18:05:10 -08002011 if (bundle->getCustomPackage() == NULL) {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07002012 // Write the R.java file into the appropriate class directory
2013 // e.g. gen/com/foo/app/R.java
Xavier Ducrohet63459ad2009-11-30 18:05:10 -08002014 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
2015 } else {
2016 const String8 customPkg(bundle->getCustomPackage());
2017 err = writeResourceSymbols(bundle, assets, customPkg, true);
2018 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002019 if (err < 0) {
2020 goto bail;
2021 }
Ying Wang002f5372012-04-25 18:53:49 -07002022 // If we have library files, we're going to write our R.java file into
2023 // the appropriate class directory for those libraries as well.
2024 // e.g. gen/com/foo/app/lib/R.java
2025 if (bundle->getExtraPackages() != NULL) {
2026 // Split on colon
2027 String8 libs(bundle->getExtraPackages());
2028 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2029 while (packageString != NULL) {
2030 // Write the R.java file out with the correct package name
2031 err = writeResourceSymbols(bundle, assets, String8(packageString), true);
2032 if (err < 0) {
2033 goto bail;
2034 }
2035 packageString = strtok(NULL, ":");
2036 }
2037 libs.unlockBuffer();
2038 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002039 } else {
2040 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
2041 if (err < 0) {
2042 goto bail;
2043 }
2044 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
2045 if (err < 0) {
2046 goto bail;
2047 }
2048 }
2049
Joe Onorato1553c822009-08-30 13:36:22 -07002050 // Write out the ProGuard file
2051 err = writeProguardFile(bundle, assets);
2052 if (err < 0) {
2053 goto bail;
2054 }
2055
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002056 // Write the apk
2057 if (outputAPKFile) {
2058 err = writeAPK(bundle, assets, String8(outputAPKFile));
2059 if (err != NO_ERROR) {
2060 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
2061 goto bail;
2062 }
2063 }
2064
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07002065 // If we've been asked to generate a dependency file, we need to finish up here.
2066 // the writeResourceSymbols and writeAPK functions have already written the target
2067 // half of the dependency file, now we need to write the prerequisites. (files that
2068 // the R.java file or .ap_ file depend on)
Josiah Gaskin03589cc2011-06-27 16:26:02 -07002069 if (bundle->getGenDependencies()) {
2070 // Now that writeResourceSymbols or writeAPK has taken care of writing
2071 // the targets to our dependency file, we'll write the prereqs
2072 fp = fopen(dependencyFile, "a+");
2073 fprintf(fp, " : ");
2074 bool includeRaw = (outputAPKFile != NULL);
2075 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07002076 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2077 // and therefore was not added to our pathstores during slurping
Josiah Gaskin03589cc2011-06-27 16:26:02 -07002078 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2079 fclose(fp);
2080 }
2081
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002082 retVal = 0;
2083bail:
2084 if (SourcePos::hasErrors()) {
2085 SourcePos::printErrors(stderr);
2086 }
2087 return retVal;
2088}
Josiah Gaskin8a39da82011-06-06 17:00:35 -07002089
2090/*
2091 * Do PNG Crunching
2092 * PRECONDITIONS
2093 * -S flag points to a source directory containing drawable* folders
2094 * -C flag points to destination directory. The folder structure in the
2095 * source directory will be mirrored to the destination (cache) directory
2096 *
2097 * POSTCONDITIONS
2098 * Destination directory will be updated to match the PNG files in
2099 * the source directory.
2100 */
2101int doCrunch(Bundle* bundle)
2102{
2103 fprintf(stdout, "Crunching PNG Files in ");
2104 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2105 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2106
2107 updatePreProcessedCache(bundle);
2108
2109 return NO_ERROR;
2110}
Dan Morrille74763e2012-01-06 10:47:10 -08002111
Xavier Ducrohetb1f6ad82012-12-21 09:54:02 -08002112/*
2113 * Do PNG Crunching on a single flag
2114 * -i points to a single png file
2115 * -o points to a single png output file
2116 */
2117int doSingleCrunch(Bundle* bundle)
2118{
2119 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2120 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2121
2122 String8 input(bundle->getSingleCrunchInputFile());
2123 String8 output(bundle->getSingleCrunchOutputFile());
Xavier Ducrohetb7de2192013-01-15 16:41:29 -08002124
Xavier Ducrohetc75d3f52013-01-14 18:02:40 -08002125 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2126 // we can't return the status_t as it gets truncate to the lower 8 bits.
2127 return 42;
2128 }
Xavier Ducrohetb7de2192013-01-15 16:41:29 -08002129
Xavier Ducrohetc75d3f52013-01-14 18:02:40 -08002130 return NO_ERROR;
Xavier Ducrohetb1f6ad82012-12-21 09:54:02 -08002131}
2132
Dan Morrille74763e2012-01-06 10:47:10 -08002133char CONSOLE_DATA[2925] = {
2134 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2135 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2136 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2137 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2138 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2139 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2140 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2141 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2142 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2143 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2144 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2145 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2146 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2147 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2148 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2149 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2150 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2151 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2152 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2153 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2154 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2155 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2156 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2157 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2158 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2159 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2160 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2161 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2162 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2163 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2164 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2165 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2166 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2167 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2168 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2169 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2170 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2171 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2172 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2173 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2174 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2175 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2176 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2177 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2178 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2179 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2180 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2181 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2182 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2183 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2184 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2185 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2186 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2187 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2188 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2189 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2190 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2191 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2192 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2193 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2194 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2195 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2196 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2197 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2198 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2199 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2200 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2201 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2202 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2203 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2204 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2205 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2206 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2207 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2208 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2209 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2210 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2211 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2212 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2213 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2214 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2215 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2216 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2217 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2218 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2219 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2220 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2221 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2222 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2223 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2224 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2225 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2226 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2227 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2228 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2229 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2230 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2231 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2232 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2233 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2234 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2235 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2236 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2237 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2238 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2239 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2240 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2241 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2242 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2243 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2244 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2245 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2246 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2247 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2248 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2249 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2250 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2251 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2252 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2253 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2254 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2255 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2256 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2257 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2258 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2259 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2260 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2261 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2262 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2263 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2264 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2265 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2266 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2267 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2268 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2269 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2270 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2271 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2272 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2273 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2274 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2275 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2276 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2277 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2278 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2279 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2280 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2281 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2282 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2283 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2284 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2285 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2286 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2287 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2288 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2289 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2290 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2291 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2292 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2293 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2294 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2295 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2296 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2297 };