blob: f7de5581527b73a9dc049edb489c86ec1c9c05ee [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,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800378};
379
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700380const char *getComponentName(String8 &pkgName, String8 &componentName) {
381 ssize_t idx = componentName.find(".");
382 String8 retStr(pkgName);
383 if (idx == 0) {
384 retStr += componentName;
385 } else if (idx < 0) {
386 retStr += ".";
387 retStr += componentName;
388 } else {
389 return componentName.string();
390 }
391 return retStr.string();
392}
393
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700394static void printCompatibleScreens(ResXMLTree& tree) {
395 size_t len;
396 ResXMLTree::event_code_t code;
397 int depth = 0;
398 bool first = true;
399 printf("compatible-screens:");
400 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
401 if (code == ResXMLTree::END_TAG) {
402 depth--;
403 if (depth < 0) {
404 break;
405 }
406 continue;
407 }
408 if (code != ResXMLTree::START_TAG) {
409 continue;
410 }
411 depth++;
412 String8 tag(tree.getElementName(&len));
413 if (tag == "screen") {
414 int32_t screenSize = getIntegerAttribute(tree,
415 SCREEN_SIZE_ATTR, NULL, -1);
416 int32_t screenDensity = getIntegerAttribute(tree,
417 SCREEN_DENSITY_ATTR, NULL, -1);
418 if (screenSize > 0 && screenDensity > 0) {
419 if (!first) {
420 printf(",");
421 }
422 first = false;
423 printf("'%d/%d'", screenSize, screenDensity);
424 }
425 }
426 }
427 printf("\n");
428}
429
Adam Lesinskid831e802013-09-27 11:14:57 -0700430Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool offHost,
431 String8 *outError = NULL)
432{
433 Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER);
434 if (aidAsset == NULL) {
435 if (outError != NULL) *outError = "xml resource does not exist";
436 return Vector<String8>();
437 }
438
439 const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service");
440
441 bool withinApduService = false;
442 Vector<String8> categories;
443
444 String8 error;
445 ResXMLTree tree;
446 tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength());
447
448 size_t len;
449 int depth = 0;
450 ResXMLTree::event_code_t code;
451 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
452 if (code == ResXMLTree::END_TAG) {
453 depth--;
454 String8 tag(tree.getElementName(&len));
455
456 if (depth == 0 && tag == serviceTagName) {
457 withinApduService = false;
458 }
459
460 } else if (code == ResXMLTree::START_TAG) {
461 depth++;
462 String8 tag(tree.getElementName(&len));
463
464 if (depth == 1) {
465 if (tag == serviceTagName) {
466 withinApduService = true;
467 }
468 } else if (depth == 2 && withinApduService) {
469 if (tag == "aid-group") {
470 String8 category = getAttribute(tree, CATEGORY_ATTR, &error);
471 if (error != "") {
472 if (outError != NULL) *outError = error;
473 return Vector<String8>();
474 }
475
476 categories.add(category);
477 }
478 }
479 }
480 }
481 aidAsset->close();
482 return categories;
483}
484
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800485/*
486 * Handle the "dump" command, to extract select data from an archive.
487 */
Dan Morrille74763e2012-01-06 10:47:10 -0800488extern char CONSOLE_DATA[2925]; // see EOF
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800489int doDump(Bundle* bundle)
490{
491 status_t result = UNKNOWN_ERROR;
492 Asset* asset = NULL;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700493
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800494 if (bundle->getFileSpecCount() < 1) {
495 fprintf(stderr, "ERROR: no dump option specified\n");
496 return 1;
497 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700498
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800499 if (bundle->getFileSpecCount() < 2) {
500 fprintf(stderr, "ERROR: no dump file specified\n");
501 return 1;
502 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700503
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800504 const char* option = bundle->getFileSpecEntry(0);
505 const char* filename = bundle->getFileSpecEntry(1);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700506
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800507 AssetManager assets;
Narayan Kamath745d4ef2014-01-27 11:17:22 +0000508 int32_t assetsCookie;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700509 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800510 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
511 return 1;
512 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700513
Dianne Hackborne289bff2011-06-13 19:33:22 -0700514 // Make a dummy config for retrieving resources... we need to supply
515 // non-default values for some configs so that we can retrieve resources
516 // in the app that don't have a default. The most important of these is
517 // the API version because key resources like icons will have an implicit
518 // version if they are using newer config types like density.
519 ResTable_config config;
Narayan Kamath788fa412014-01-21 15:32:36 +0000520 memset(&config, 0, sizeof(ResTable_config));
Dianne Hackborne289bff2011-06-13 19:33:22 -0700521 config.language[0] = 'e';
522 config.language[1] = 'n';
523 config.country[0] = 'U';
524 config.country[1] = 'S';
525 config.orientation = ResTable_config::ORIENTATION_PORT;
526 config.density = ResTable_config::DENSITY_MEDIUM;
527 config.sdkVersion = 10000; // Very high.
528 config.screenWidthDp = 320;
529 config.screenHeightDp = 480;
530 config.smallestScreenWidthDp = 320;
531 assets.setConfiguration(config);
532
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800533 const ResTable& res = assets.getResources(false);
534 if (&res == NULL) {
535 fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
536 goto bail;
537 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700538
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800539 if (strcmp("resources", option) == 0) {
Steve Blockf1ff21a2010-06-14 17:34:04 +0100540#ifndef HAVE_ANDROID_OS
Dianne Hackborne17086b2009-06-19 15:13:28 -0700541 res.print(bundle->getValues());
Steve Blockf1ff21a2010-06-14 17:34:04 +0100542#endif
Dianne Hackborn6c997a92012-01-31 11:27:43 -0800543
544 } else if (strcmp("strings", option) == 0) {
545 const ResStringPool* pool = res.getTableStringBlock(0);
546 printStringPool(pool);
547
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800548 } else if (strcmp("xmltree", option) == 0) {
549 if (bundle->getFileSpecCount() < 3) {
550 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
551 goto bail;
552 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700553
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800554 for (int i=2; i<bundle->getFileSpecCount(); i++) {
555 const char* resname = bundle->getFileSpecEntry(i);
556 ResXMLTree tree;
557 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
558 if (asset == NULL) {
Kenny Root44b283d2009-09-01 19:03:11 -0500559 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800560 goto bail;
561 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700562
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800563 if (tree.setTo(asset->getBuffer(true),
564 asset->getLength()) != NO_ERROR) {
565 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
566 goto bail;
567 }
568 tree.restart();
569 printXMLBlock(&tree);
Kenny Root19138462009-12-04 09:38:48 -0800570 tree.uninit();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800571 delete asset;
572 asset = NULL;
573 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700574
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800575 } else if (strcmp("xmlstrings", option) == 0) {
576 if (bundle->getFileSpecCount() < 3) {
577 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
578 goto bail;
579 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700580
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800581 for (int i=2; i<bundle->getFileSpecCount(); i++) {
582 const char* resname = bundle->getFileSpecEntry(i);
583 ResXMLTree tree;
584 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
585 if (asset == NULL) {
Kenny Root44b283d2009-09-01 19:03:11 -0500586 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800587 goto bail;
588 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700589
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800590 if (tree.setTo(asset->getBuffer(true),
591 asset->getLength()) != NO_ERROR) {
592 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
593 goto bail;
594 }
595 printStringPool(&tree.getStrings());
596 delete asset;
597 asset = NULL;
598 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700599
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800600 } else {
601 ResXMLTree tree;
602 asset = assets.openNonAsset("AndroidManifest.xml",
603 Asset::ACCESS_BUFFER);
604 if (asset == NULL) {
605 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
606 goto bail;
607 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700608
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800609 if (tree.setTo(asset->getBuffer(true),
610 asset->getLength()) != NO_ERROR) {
611 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
612 goto bail;
613 }
614 tree.restart();
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700615
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800616 if (strcmp("permissions", option) == 0) {
617 size_t len;
618 ResXMLTree::event_code_t code;
619 int depth = 0;
620 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
621 if (code == ResXMLTree::END_TAG) {
622 depth--;
623 continue;
624 }
625 if (code != ResXMLTree::START_TAG) {
626 continue;
627 }
628 depth++;
629 String8 tag(tree.getElementName(&len));
630 //printf("Depth %d tag %s\n", depth, tag.string());
631 if (depth == 1) {
632 if (tag != "manifest") {
633 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
634 goto bail;
635 }
636 String8 pkg = getAttribute(tree, NULL, "package", NULL);
637 printf("package: %s\n", pkg.string());
638 } else if (depth == 2 && tag == "permission") {
639 String8 error;
640 String8 name = getAttribute(tree, NAME_ATTR, &error);
641 if (error != "") {
642 fprintf(stderr, "ERROR: %s\n", error.string());
643 goto bail;
644 }
645 printf("permission: %s\n", name.string());
646 } else if (depth == 2 && tag == "uses-permission") {
647 String8 error;
648 String8 name = getAttribute(tree, NAME_ATTR, &error);
649 if (error != "") {
650 fprintf(stderr, "ERROR: %s\n", error.string());
651 goto bail;
652 }
653 printf("uses-permission: %s\n", name.string());
Nick Kralevich1bcc3d62013-04-03 09:00:02 -0700654 int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
655 if (!req) {
656 printf("optional-permission: %s\n", name.string());
657 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800658 }
659 }
660 } else if (strcmp("badging", option) == 0) {
Dianne Hackborne289bff2011-06-13 19:33:22 -0700661 Vector<String8> locales;
662 res.getLocales(&locales);
663
664 Vector<ResTable_config> configs;
665 res.getConfigurations(&configs);
666 SortedVector<int> densities;
667 const size_t NC = configs.size();
668 for (size_t i=0; i<NC; i++) {
669 int dens = configs[i].density;
670 if (dens == 0) dens = 160;
671 densities.add(dens);
672 }
673
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800674 size_t len;
675 ResXMLTree::event_code_t code;
676 int depth = 0;
677 String8 error;
678 bool withinActivity = false;
679 bool isMainActivity = false;
680 bool isLauncherActivity = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700681 bool isSearchable = false;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700682 bool withinApplication = false;
Michael Wrighteaeb1902013-09-05 18:15:57 -0700683 bool withinSupportsInput = false;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700684 bool withinReceiver = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700685 bool withinService = false;
686 bool withinIntentFilter = false;
687 bool hasMainActivity = false;
688 bool hasOtherActivities = false;
689 bool hasOtherReceivers = false;
690 bool hasOtherServices = false;
691 bool hasWallpaperService = false;
692 bool hasImeService = false;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700693 bool hasAccessibilityService = false;
694 bool hasPrintService = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700695 bool hasWidgetReceivers = false;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700696 bool hasDeviceAdminReceiver = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700697 bool hasIntentFilter = false;
Adam Lesinskid831e802013-09-27 11:14:57 -0700698 bool hasPaymentService = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700699 bool actMainActivity = false;
700 bool actWidgetReceivers = false;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700701 bool actDeviceAdminEnabled = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700702 bool actImeService = false;
703 bool actWallpaperService = false;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700704 bool actAccessibilityService = false;
705 bool actPrintService = false;
Adam Lesinskid831e802013-09-27 11:14:57 -0700706 bool actHostApduService = false;
707 bool actOffHostApduService = false;
708 bool hasMetaHostPaymentCategory = false;
709 bool hasMetaOffHostPaymentCategory = false;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700710
711 // These permissions are required by services implementing services
712 // the system binds to (IME, Accessibility, PrintServices, etc.)
713 bool hasBindDeviceAdminPermission = false;
714 bool hasBindInputMethodPermission = false;
715 bool hasBindAccessibilityServicePermission = false;
716 bool hasBindPrintServicePermission = false;
Adam Lesinskid831e802013-09-27 11:14:57 -0700717 bool hasBindNfcServicePermission = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700718
Kenny Root063a44e2011-12-08 08:46:03 -0800719 // These two implement the implicit permissions that are granted
720 // to pre-1.6 applications.
721 bool hasWriteExternalStoragePermission = false;
722 bool hasReadPhoneStatePermission = false;
723
Dianne Hackborn79245122012-03-12 10:51:26 -0700724 // If an app requests write storage, they will also get read storage.
725 bool hasReadExternalStoragePermission = false;
726
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -0700727 // Implement transition to read and write call log.
728 bool hasReadContactsPermission = false;
729 bool hasWriteContactsPermission = false;
730 bool hasReadCallLogPermission = false;
731 bool hasWriteCallLogPermission = false;
732
Dan Morrill89d97c12010-05-03 16:13:14 -0700733 // This next group of variables is used to implement a group of
734 // backward-compatibility heuristics necessitated by the addition of
735 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
736 // heuristic is "if an app requests a permission but doesn't explicitly
737 // request the corresponding <uses-feature>, presume it's there anyway".
738 bool specCameraFeature = false; // camera-related
739 bool specCameraAutofocusFeature = false;
740 bool reqCameraAutofocusFeature = false;
741 bool reqCameraFlashFeature = false;
Dianne Hackborne5276a72009-08-27 16:28:44 -0700742 bool hasCameraPermission = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700743 bool specLocationFeature = false; // location-related
744 bool specNetworkLocFeature = false;
745 bool reqNetworkLocFeature = false;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800746 bool specGpsFeature = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700747 bool reqGpsFeature = false;
748 bool hasMockLocPermission = false;
749 bool hasCoarseLocPermission = false;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800750 bool hasGpsPermission = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700751 bool hasGeneralLocPermission = false;
752 bool specBluetoothFeature = false; // Bluetooth API-related
753 bool hasBluetoothPermission = false;
754 bool specMicrophoneFeature = false; // microphone-related
755 bool hasRecordAudioPermission = false;
756 bool specWiFiFeature = false;
757 bool hasWiFiPermission = false;
758 bool specTelephonyFeature = false; // telephony-related
759 bool reqTelephonySubFeature = false;
760 bool hasTelephonyPermission = false;
761 bool specTouchscreenFeature = false; // touchscreen-related
762 bool specMultitouchFeature = false;
763 bool reqDistinctMultitouchFeature = false;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700764 bool specScreenPortraitFeature = false;
765 bool specScreenLandscapeFeature = false;
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -0700766 bool reqScreenPortraitFeature = false;
767 bool reqScreenLandscapeFeature = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700768 // 2.2 also added some other features that apps can request, but that
769 // have no corresponding permission, so we cannot implement any
770 // back-compatibility heuristic for them. The below are thus unnecessary
771 // (but are retained here for documentary purposes.)
772 //bool specCompassFeature = false;
773 //bool specAccelerometerFeature = false;
774 //bool specProximityFeature = false;
775 //bool specAmbientLightFeature = false;
776 //bool specLiveWallpaperFeature = false;
777
Dianne Hackborn723738c2009-06-25 19:48:04 -0700778 int targetSdk = 0;
779 int smallScreen = 1;
780 int normalScreen = 1;
781 int largeScreen = 1;
Dianne Hackbornf43489d2010-08-20 12:44:33 -0700782 int xlargeScreen = 1;
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700783 int anyDensity = 1;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700784 int requiresSmallestWidthDp = 0;
785 int compatibleWidthLimitDp = 0;
786 int largestWidthLimitDp = 0;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700787 String8 pkg;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800788 String8 activityName;
789 String8 activityLabel;
790 String8 activityIcon;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700791 String8 receiverName;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700792 String8 serviceName;
Michael Wrighteaeb1902013-09-05 18:15:57 -0700793 Vector<String8> supportedInput;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800794 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
795 if (code == ResXMLTree::END_TAG) {
796 depth--;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700797 if (depth < 2) {
Michael Wrighteaeb1902013-09-05 18:15:57 -0700798 if (withinSupportsInput && !supportedInput.isEmpty()) {
799 printf("supports-input: '");
800 const size_t N = supportedInput.size();
801 for (size_t i=0; i<N; i++) {
802 printf("%s", supportedInput[i].string());
803 if (i != N - 1) {
804 printf("' '");
805 } else {
806 printf("'\n");
807 }
808 }
809 supportedInput.clear();
810 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700811 withinApplication = false;
Michael Wrighteaeb1902013-09-05 18:15:57 -0700812 withinSupportsInput = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700813 } else if (depth < 3) {
814 if (withinActivity && isMainActivity && isLauncherActivity) {
815 const char *aName = getComponentName(pkg, activityName);
Dianne Hackborne289bff2011-06-13 19:33:22 -0700816 printf("launchable-activity:");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700817 if (aName != NULL) {
Dianne Hackborne289bff2011-06-13 19:33:22 -0700818 printf(" name='%s' ", aName);
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700819 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700820 printf(" label='%s' icon='%s'\n",
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700821 activityLabel.string(),
822 activityIcon.string());
823 }
824 if (!hasIntentFilter) {
825 hasOtherActivities |= withinActivity;
826 hasOtherReceivers |= withinReceiver;
827 hasOtherServices |= withinService;
Adam Lesinskid831e802013-09-27 11:14:57 -0700828 } else {
829 if (withinService) {
830 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
831 hasBindNfcServicePermission);
832 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
833 hasBindNfcServicePermission);
834 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700835 }
836 withinActivity = false;
837 withinService = false;
838 withinReceiver = false;
839 hasIntentFilter = false;
840 isMainActivity = isLauncherActivity = false;
841 } else if (depth < 4) {
842 if (withinIntentFilter) {
843 if (withinActivity) {
844 hasMainActivity |= actMainActivity;
845 hasOtherActivities |= !actMainActivity;
846 } else if (withinReceiver) {
847 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700848 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
849 hasBindDeviceAdminPermission);
850 hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled);
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700851 } else if (withinService) {
852 hasImeService |= actImeService;
853 hasWallpaperService |= actWallpaperService;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700854 hasAccessibilityService |= (actAccessibilityService &&
855 hasBindAccessibilityServicePermission);
856 hasPrintService |= (actPrintService && hasBindPrintServicePermission);
857 hasOtherServices |= (!actImeService && !actWallpaperService &&
Adam Lesinskid831e802013-09-27 11:14:57 -0700858 !actAccessibilityService && !actPrintService &&
859 !actHostApduService && !actOffHostApduService);
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700860 }
861 }
862 withinIntentFilter = false;
863 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800864 continue;
865 }
866 if (code != ResXMLTree::START_TAG) {
867 continue;
868 }
869 depth++;
870 String8 tag(tree.getElementName(&len));
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700871 //printf("Depth %d, %s\n", depth, tag.string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800872 if (depth == 1) {
873 if (tag != "manifest") {
874 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
875 goto bail;
876 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700877 pkg = getAttribute(tree, NULL, "package", NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800878 printf("package: name='%s' ", pkg.string());
879 int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
880 if (error != "") {
881 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
882 goto bail;
883 }
884 if (versionCode > 0) {
885 printf("versionCode='%d' ", versionCode);
886 } else {
887 printf("versionCode='' ");
888 }
Dianne Hackborncf244ad2010-03-09 15:00:30 -0800889 String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800890 if (error != "") {
891 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
892 goto bail;
893 }
894 printf("versionName='%s'\n", versionName.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700895 } else if (depth == 2) {
896 withinApplication = false;
897 if (tag == "application") {
898 withinApplication = true;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700899
900 String8 label;
901 const size_t NL = locales.size();
902 for (size_t i=0; i<NL; i++) {
903 const char* localeStr = locales[i].string();
904 assets.setLocale(localeStr != NULL ? localeStr : "");
905 String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
906 if (llabel != "") {
907 if (localeStr == NULL || strlen(localeStr) == 0) {
908 label = llabel;
909 printf("application-label:'%s'\n", llabel.string());
910 } else {
911 if (label == "") {
912 label = llabel;
913 }
914 printf("application-label-%s:'%s'\n", localeStr,
915 llabel.string());
916 }
917 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700918 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700919
920 ResTable_config tmpConfig = config;
921 const size_t ND = densities.size();
922 for (size_t i=0; i<ND; i++) {
923 tmpConfig.density = densities[i];
924 assets.setConfiguration(tmpConfig);
925 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
926 if (icon != "") {
927 printf("application-icon-%d:'%s'\n", densities[i], icon.string());
928 }
929 }
930 assets.setConfiguration(config);
931
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700932 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
933 if (error != "") {
934 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
935 goto bail;
936 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700937 int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700938 if (error != "") {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700939 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700940 goto bail;
941 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700942 printf("application: label='%s' ", label.string());
943 printf("icon='%s'\n", icon.string());
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700944 if (testOnly != 0) {
945 printf("testOnly='%d'\n", testOnly);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700946 }
Dan Morrillb6ec11e2012-04-03 12:44:40 -0700947
948 int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0);
949 if (error != "") {
950 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string());
951 goto bail;
952 }
953 if (debuggable != 0) {
954 printf("application-debuggable\n");
955 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700956 } else if (tag == "uses-sdk") {
957 int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
958 if (error != "") {
959 error = "";
960 String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
961 if (error != "") {
962 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
963 error.string());
964 goto bail;
965 }
Dianne Hackborn723738c2009-06-25 19:48:04 -0700966 if (name == "Donut") targetSdk = 4;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700967 printf("sdkVersion:'%s'\n", name.string());
968 } else if (code != -1) {
Dianne Hackborn723738c2009-06-25 19:48:04 -0700969 targetSdk = code;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700970 printf("sdkVersion:'%d'\n", code);
971 }
Suchi Amalapurapu75c49842009-08-14 15:13:09 -0700972 code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
973 if (code != -1) {
974 printf("maxSdkVersion:'%d'\n", code);
975 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700976 code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
977 if (error != "") {
978 error = "";
979 String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
980 if (error != "") {
981 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
982 error.string());
983 goto bail;
984 }
Dianne Hackborn723738c2009-06-25 19:48:04 -0700985 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700986 printf("targetSdkVersion:'%s'\n", name.string());
987 } else if (code != -1) {
Dianne Hackborn723738c2009-06-25 19:48:04 -0700988 if (targetSdk < code) {
989 targetSdk = code;
990 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700991 printf("targetSdkVersion:'%d'\n", code);
992 }
993 } else if (tag == "uses-configuration") {
994 int32_t reqTouchScreen = getIntegerAttribute(tree,
995 REQ_TOUCH_SCREEN_ATTR, NULL, 0);
996 int32_t reqKeyboardType = getIntegerAttribute(tree,
997 REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
998 int32_t reqHardKeyboard = getIntegerAttribute(tree,
999 REQ_HARD_KEYBOARD_ATTR, NULL, 0);
1000 int32_t reqNavigation = getIntegerAttribute(tree,
1001 REQ_NAVIGATION_ATTR, NULL, 0);
1002 int32_t reqFiveWayNav = getIntegerAttribute(tree,
1003 REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
Dianne Hackborncb2d50d2010-01-06 11:29:54 -08001004 printf("uses-configuration:");
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001005 if (reqTouchScreen != 0) {
1006 printf(" reqTouchScreen='%d'", reqTouchScreen);
1007 }
1008 if (reqKeyboardType != 0) {
1009 printf(" reqKeyboardType='%d'", reqKeyboardType);
1010 }
1011 if (reqHardKeyboard != 0) {
1012 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1013 }
1014 if (reqNavigation != 0) {
1015 printf(" reqNavigation='%d'", reqNavigation);
1016 }
1017 if (reqFiveWayNav != 0) {
1018 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1019 }
1020 printf("\n");
Michael Wrighteaeb1902013-09-05 18:15:57 -07001021 } else if (tag == "supports-input") {
1022 withinSupportsInput = true;
Dianne Hackborn723738c2009-06-25 19:48:04 -07001023 } else if (tag == "supports-screens") {
1024 smallScreen = getIntegerAttribute(tree,
1025 SMALL_SCREEN_ATTR, NULL, 1);
1026 normalScreen = getIntegerAttribute(tree,
1027 NORMAL_SCREEN_ATTR, NULL, 1);
1028 largeScreen = getIntegerAttribute(tree,
1029 LARGE_SCREEN_ATTR, NULL, 1);
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001030 xlargeScreen = getIntegerAttribute(tree,
1031 XLARGE_SCREEN_ATTR, NULL, 1);
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001032 anyDensity = getIntegerAttribute(tree,
1033 ANY_DENSITY_ATTR, NULL, 1);
Dianne Hackborne289bff2011-06-13 19:33:22 -07001034 requiresSmallestWidthDp = getIntegerAttribute(tree,
1035 REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
1036 compatibleWidthLimitDp = getIntegerAttribute(tree,
1037 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
1038 largestWidthLimitDp = getIntegerAttribute(tree,
1039 LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
Dianne Hackborne5276a72009-08-27 16:28:44 -07001040 } else if (tag == "uses-feature") {
1041 String8 name = getAttribute(tree, NAME_ATTR, &error);
Suchi Amalapurapu40b94722009-09-20 13:39:37 -07001042
1043 if (name != "" && error == "") {
Dianne Hackborne5276a72009-08-27 16:28:44 -07001044 int req = getIntegerAttribute(tree,
1045 REQUIRED_ATTR, NULL, 1);
Dan Morrill89d97c12010-05-03 16:13:14 -07001046
Dianne Hackborne5276a72009-08-27 16:28:44 -07001047 if (name == "android.hardware.camera") {
1048 specCameraFeature = true;
Dan Morrill89d97c12010-05-03 16:13:14 -07001049 } else if (name == "android.hardware.camera.autofocus") {
1050 // these have no corresponding permission to check for,
1051 // but should imply the foundational camera permission
1052 reqCameraAutofocusFeature = reqCameraAutofocusFeature || req;
1053 specCameraAutofocusFeature = true;
1054 } else if (req && (name == "android.hardware.camera.flash")) {
1055 // these have no corresponding permission to check for,
1056 // but should imply the foundational camera permission
1057 reqCameraFlashFeature = true;
1058 } else if (name == "android.hardware.location") {
1059 specLocationFeature = true;
1060 } else if (name == "android.hardware.location.network") {
1061 specNetworkLocFeature = true;
1062 reqNetworkLocFeature = reqNetworkLocFeature || req;
Dianne Hackbornef05e072010-03-01 17:43:39 -08001063 } else if (name == "android.hardware.location.gps") {
1064 specGpsFeature = true;
Dan Morrill89d97c12010-05-03 16:13:14 -07001065 reqGpsFeature = reqGpsFeature || req;
1066 } else if (name == "android.hardware.bluetooth") {
1067 specBluetoothFeature = true;
1068 } else if (name == "android.hardware.touchscreen") {
1069 specTouchscreenFeature = true;
1070 } else if (name == "android.hardware.touchscreen.multitouch") {
1071 specMultitouchFeature = true;
1072 } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
1073 reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req;
1074 } else if (name == "android.hardware.microphone") {
1075 specMicrophoneFeature = true;
1076 } else if (name == "android.hardware.wifi") {
1077 specWiFiFeature = true;
1078 } else if (name == "android.hardware.telephony") {
1079 specTelephonyFeature = true;
1080 } else if (req && (name == "android.hardware.telephony.gsm" ||
1081 name == "android.hardware.telephony.cdma")) {
1082 // these have no corresponding permission to check for,
1083 // but should imply the foundational telephony permission
1084 reqTelephonySubFeature = true;
Dianne Hackborne289bff2011-06-13 19:33:22 -07001085 } else if (name == "android.hardware.screen.portrait") {
1086 specScreenPortraitFeature = true;
1087 } else if (name == "android.hardware.screen.landscape") {
1088 specScreenLandscapeFeature = true;
Dianne Hackborne5276a72009-08-27 16:28:44 -07001089 }
1090 printf("uses-feature%s:'%s'\n",
1091 req ? "" : "-not-required", name.string());
1092 } else {
1093 int vers = getIntegerAttribute(tree,
1094 GL_ES_VERSION_ATTR, &error);
1095 if (error == "") {
1096 printf("uses-gl-es:'0x%x'\n", vers);
1097 }
1098 }
1099 } else if (tag == "uses-permission") {
1100 String8 name = getAttribute(tree, NAME_ATTR, &error);
Suchi Amalapurapu40b94722009-09-20 13:39:37 -07001101 if (name != "" && error == "") {
Dianne Hackborne5276a72009-08-27 16:28:44 -07001102 if (name == "android.permission.CAMERA") {
1103 hasCameraPermission = true;
Dianne Hackbornef05e072010-03-01 17:43:39 -08001104 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
1105 hasGpsPermission = true;
Dan Morrill89d97c12010-05-03 16:13:14 -07001106 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
1107 hasMockLocPermission = true;
1108 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
1109 hasCoarseLocPermission = true;
1110 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
1111 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
1112 hasGeneralLocPermission = true;
1113 } else if (name == "android.permission.BLUETOOTH" ||
1114 name == "android.permission.BLUETOOTH_ADMIN") {
1115 hasBluetoothPermission = true;
1116 } else if (name == "android.permission.RECORD_AUDIO") {
1117 hasRecordAudioPermission = true;
1118 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1119 name == "android.permission.CHANGE_WIFI_STATE" ||
1120 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
1121 hasWiFiPermission = true;
1122 } else if (name == "android.permission.CALL_PHONE" ||
1123 name == "android.permission.CALL_PRIVILEGED" ||
1124 name == "android.permission.MODIFY_PHONE_STATE" ||
1125 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1126 name == "android.permission.READ_SMS" ||
1127 name == "android.permission.RECEIVE_SMS" ||
1128 name == "android.permission.RECEIVE_MMS" ||
1129 name == "android.permission.RECEIVE_WAP_PUSH" ||
1130 name == "android.permission.SEND_SMS" ||
1131 name == "android.permission.WRITE_APN_SETTINGS" ||
1132 name == "android.permission.WRITE_SMS") {
1133 hasTelephonyPermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001134 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1135 hasWriteExternalStoragePermission = true;
Dianne Hackborn79245122012-03-12 10:51:26 -07001136 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1137 hasReadExternalStoragePermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001138 } else if (name == "android.permission.READ_PHONE_STATE") {
1139 hasReadPhoneStatePermission = true;
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001140 } else if (name == "android.permission.READ_CONTACTS") {
1141 hasReadContactsPermission = true;
1142 } else if (name == "android.permission.WRITE_CONTACTS") {
1143 hasWriteContactsPermission = true;
1144 } else if (name == "android.permission.READ_CALL_LOG") {
1145 hasReadCallLogPermission = true;
1146 } else if (name == "android.permission.WRITE_CALL_LOG") {
1147 hasWriteCallLogPermission = true;
Dianne Hackborne5276a72009-08-27 16:28:44 -07001148 }
1149 printf("uses-permission:'%s'\n", name.string());
Nick Kralevich1bcc3d62013-04-03 09:00:02 -07001150 int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
1151 if (!req) {
1152 printf("optional-permission:'%s'\n", name.string());
1153 }
Dianne Hackborne5276a72009-08-27 16:28:44 -07001154 } else {
1155 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1156 error.string());
1157 goto bail;
1158 }
Dianne Hackborn43b68032010-09-02 17:14:41 -07001159 } else if (tag == "uses-package") {
1160 String8 name = getAttribute(tree, NAME_ATTR, &error);
1161 if (name != "" && error == "") {
1162 printf("uses-package:'%s'\n", name.string());
1163 } else {
1164 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1165 error.string());
1166 goto bail;
1167 }
Jeff Hamiltone2c17f92010-02-12 13:45:16 -06001168 } else if (tag == "original-package") {
1169 String8 name = getAttribute(tree, NAME_ATTR, &error);
1170 if (name != "" && error == "") {
1171 printf("original-package:'%s'\n", name.string());
1172 } else {
1173 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1174 error.string());
1175 goto bail;
1176 }
Dan Morrill096b67f2010-12-13 16:25:54 -08001177 } else if (tag == "supports-gl-texture") {
Dan Morrill6f51fc12010-10-13 14:33:43 -07001178 String8 name = getAttribute(tree, NAME_ATTR, &error);
1179 if (name != "" && error == "") {
Dan Morrill096b67f2010-12-13 16:25:54 -08001180 printf("supports-gl-texture:'%s'\n", name.string());
Dan Morrill6f51fc12010-10-13 14:33:43 -07001181 } else {
1182 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1183 error.string());
1184 goto bail;
1185 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001186 } else if (tag == "compatible-screens") {
1187 printCompatibleScreens(tree);
1188 depth--;
Kenny Root56088a52011-09-29 13:49:45 -07001189 } else if (tag == "package-verifier") {
1190 String8 name = getAttribute(tree, NAME_ATTR, &error);
1191 if (name != "" && error == "") {
1192 String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
1193 if (publicKey != "" && error == "") {
1194 printf("package-verifier: name='%s' publicKey='%s'\n",
1195 name.string(), publicKey.string());
1196 }
1197 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001198 }
Michael Wrighteaeb1902013-09-05 18:15:57 -07001199 } else if (depth == 3) {
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001200 withinActivity = false;
1201 withinReceiver = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001202 withinService = false;
1203 hasIntentFilter = false;
Adam Lesinskid831e802013-09-27 11:14:57 -07001204 hasMetaHostPaymentCategory = false;
1205 hasMetaOffHostPaymentCategory = false;
1206 hasBindDeviceAdminPermission = false;
1207 hasBindInputMethodPermission = false;
1208 hasBindAccessibilityServicePermission = false;
1209 hasBindPrintServicePermission = false;
1210 hasBindNfcServicePermission = false;
Michael Wrighteaeb1902013-09-05 18:15:57 -07001211 if (withinApplication) {
1212 if(tag == "activity") {
1213 withinActivity = true;
1214 activityName = getAttribute(tree, NAME_ATTR, &error);
1215 if (error != "") {
1216 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1217 error.string());
1218 goto bail;
1219 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001220
Michael Wrighteaeb1902013-09-05 18:15:57 -07001221 activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1222 if (error != "") {
1223 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
1224 error.string());
1225 goto bail;
1226 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001227
Michael Wrighteaeb1902013-09-05 18:15:57 -07001228 activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1229 if (error != "") {
1230 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1231 error.string());
1232 goto bail;
1233 }
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001234
Michael Wrighteaeb1902013-09-05 18:15:57 -07001235 int32_t orien = getResolvedIntegerAttribute(&res, tree,
1236 SCREEN_ORIENTATION_ATTR, &error);
1237 if (error == "") {
1238 if (orien == 0 || orien == 6 || orien == 8) {
1239 // Requests landscape, sensorLandscape, or reverseLandscape.
1240 reqScreenLandscapeFeature = true;
1241 } else if (orien == 1 || orien == 7 || orien == 9) {
1242 // Requests portrait, sensorPortrait, or reversePortrait.
1243 reqScreenPortraitFeature = true;
1244 }
1245 }
1246 } else if (tag == "uses-library") {
1247 String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1248 if (error != "") {
1249 fprintf(stderr,
1250 "ERROR getting 'android:name' attribute for uses-library"
1251 " %s\n", error.string());
1252 goto bail;
1253 }
1254 int req = getIntegerAttribute(tree,
1255 REQUIRED_ATTR, NULL, 1);
1256 printf("uses-library%s:'%s'\n",
1257 req ? "" : "-not-required", libraryName.string());
1258 } else if (tag == "receiver") {
1259 withinReceiver = true;
1260 receiverName = getAttribute(tree, NAME_ATTR, &error);
1261
1262 if (error != "") {
1263 fprintf(stderr,
1264 "ERROR getting 'android:name' attribute for receiver:"
1265 " %s\n", error.string());
1266 goto bail;
1267 }
Adam Lesinskib1249b82013-09-25 17:03:50 -07001268
1269 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1270 if (error == "") {
1271 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1272 hasBindDeviceAdminPermission = true;
1273 }
1274 } else {
1275 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1276 " receiver '%s': %s\n", receiverName.string(), error.string());
1277 }
Michael Wrighteaeb1902013-09-05 18:15:57 -07001278 } else if (tag == "service") {
1279 withinService = true;
1280 serviceName = getAttribute(tree, NAME_ATTR, &error);
1281
1282 if (error != "") {
1283 fprintf(stderr, "ERROR getting 'android:name' attribute for"
1284 " service: %s\n", error.string());
1285 goto bail;
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001286 }
Adam Lesinskib1249b82013-09-25 17:03:50 -07001287
1288 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1289 if (error == "") {
1290 if (permission == "android.permission.BIND_INPUT_METHOD") {
1291 hasBindInputMethodPermission = true;
1292 } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") {
1293 hasBindAccessibilityServicePermission = true;
1294 } else if (permission == "android.permission.BIND_PRINT_SERVICE") {
1295 hasBindPrintServicePermission = true;
Adam Lesinskid831e802013-09-27 11:14:57 -07001296 } else if (permission == "android.permission.BIND_NFC_SERVICE") {
1297 hasBindNfcServicePermission = true;
Adam Lesinskib1249b82013-09-25 17:03:50 -07001298 }
1299 } else {
1300 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1301 " service '%s': %s\n", serviceName.string(), error.string());
1302 }
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001303 }
Michael Wrighteaeb1902013-09-05 18:15:57 -07001304 } else if (withinSupportsInput && tag == "input-type") {
1305 String8 name = getAttribute(tree, NAME_ATTR, &error);
1306 if (name != "" && error == "") {
1307 supportedInput.add(name);
1308 } else {
1309 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1310 error.string());
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001311 goto bail;
1312 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001313 }
Adam Lesinskid831e802013-09-27 11:14:57 -07001314 } else if (depth == 4) {
1315 if (tag == "intent-filter") {
1316 hasIntentFilter = true;
1317 withinIntentFilter = true;
1318 actMainActivity = false;
1319 actWidgetReceivers = false;
1320 actImeService = false;
1321 actWallpaperService = false;
1322 actAccessibilityService = false;
1323 actPrintService = false;
1324 actDeviceAdminEnabled = false;
1325 actHostApduService = false;
1326 actOffHostApduService = false;
1327 } else if (withinService && tag == "meta-data") {
1328 String8 name = getAttribute(tree, NAME_ATTR, &error);
1329 if (error != "") {
1330 fprintf(stderr, "ERROR getting 'android:name' attribute for"
1331 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1332 goto bail;
1333 }
1334
1335 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1336 name == "android.nfc.cardemulation.off_host_apdu_service") {
1337 bool offHost = true;
1338 if (name == "android.nfc.cardemulation.host_apdu_service") {
1339 offHost = false;
1340 }
1341
1342 String8 xmlPath = getResolvedAttribute(&res, tree, RESOURCE_ATTR, &error);
1343 if (error != "") {
1344 fprintf(stderr, "ERROR getting 'android:resource' attribute for"
1345 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1346 goto bail;
1347 }
1348
1349 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1350 offHost, &error);
1351 if (error != "") {
1352 fprintf(stderr, "ERROR getting AID category for service '%s'\n",
1353 serviceName.string());
1354 goto bail;
1355 }
1356
1357 const size_t catLen = categories.size();
1358 for (size_t i = 0; i < catLen; i++) {
1359 bool paymentCategory = (categories[i] == "payment");
1360 if (offHost) {
1361 hasMetaOffHostPaymentCategory |= paymentCategory;
1362 } else {
1363 hasMetaHostPaymentCategory |= paymentCategory;
1364 }
1365 }
1366 }
1367 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001368 } else if ((depth == 5) && withinIntentFilter){
1369 String8 action;
1370 if (tag == "action") {
1371 action = getAttribute(tree, NAME_ATTR, &error);
1372 if (error != "") {
1373 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1374 goto bail;
1375 }
1376 if (withinActivity) {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001377 if (action == "android.intent.action.MAIN") {
1378 isMainActivity = true;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001379 actMainActivity = true;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001380 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001381 } else if (withinReceiver) {
1382 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1383 actWidgetReceivers = true;
Adam Lesinskib1249b82013-09-25 17:03:50 -07001384 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1385 actDeviceAdminEnabled = true;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001386 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001387 } else if (withinService) {
1388 if (action == "android.view.InputMethod") {
1389 actImeService = true;
1390 } else if (action == "android.service.wallpaper.WallpaperService") {
1391 actWallpaperService = true;
Adam Lesinskib1249b82013-09-25 17:03:50 -07001392 } else if (action == "android.accessibilityservice.AccessibilityService") {
1393 actAccessibilityService = true;
1394 } else if (action == "android.printservice.PrintService") {
1395 actPrintService = true;
Adam Lesinskid831e802013-09-27 11:14:57 -07001396 } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
1397 actHostApduService = true;
1398 } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
1399 actOffHostApduService = true;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001400 }
1401 }
1402 if (action == "android.intent.action.SEARCH") {
1403 isSearchable = true;
1404 }
1405 }
1406
1407 if (tag == "category") {
1408 String8 category = getAttribute(tree, NAME_ATTR, &error);
1409 if (error != "") {
1410 fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string());
1411 goto bail;
1412 }
1413 if (withinActivity) {
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001414 if (category == "android.intent.category.LAUNCHER") {
1415 isLauncherActivity = true;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001416 }
1417 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001418 }
1419 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001420 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001421
Kenny Root063a44e2011-12-08 08:46:03 -08001422 // Pre-1.6 implicitly granted permission compatibility logic
1423 if (targetSdk < 4) {
1424 if (!hasWriteExternalStoragePermission) {
1425 printf("uses-permission:'android.permission.WRITE_EXTERNAL_STORAGE'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001426 printf("uses-implied-permission:'android.permission.WRITE_EXTERNAL_STORAGE'," \
1427 "'targetSdkVersion < 4'\n");
Dianne Hackborn79245122012-03-12 10:51:26 -07001428 hasWriteExternalStoragePermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001429 }
1430 if (!hasReadPhoneStatePermission) {
1431 printf("uses-permission:'android.permission.READ_PHONE_STATE'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001432 printf("uses-implied-permission:'android.permission.READ_PHONE_STATE'," \
1433 "'targetSdkVersion < 4'\n");
Kenny Root063a44e2011-12-08 08:46:03 -08001434 }
1435 }
1436
Dianne Hackborn79245122012-03-12 10:51:26 -07001437 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
Dianne Hackborn2bd8d042012-06-11 12:27:05 -07001438 // force them to always take READ_EXTERNAL_STORAGE as well. We always
1439 // do this (regardless of target API version) because we can't have
1440 // an app with write permission but not read permission.
Dianne Hackborn79245122012-03-12 10:51:26 -07001441 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
1442 printf("uses-permission:'android.permission.READ_EXTERNAL_STORAGE'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001443 printf("uses-implied-permission:'android.permission.READ_EXTERNAL_STORAGE'," \
1444 "'requested WRITE_EXTERNAL_STORAGE'\n");
Dianne Hackborn79245122012-03-12 10:51:26 -07001445 }
1446
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001447 // Pre-JellyBean call log permission compatibility.
1448 if (targetSdk < 16) {
1449 if (!hasReadCallLogPermission && hasReadContactsPermission) {
1450 printf("uses-permission:'android.permission.READ_CALL_LOG'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001451 printf("uses-implied-permission:'android.permission.READ_CALL_LOG'," \
1452 "'targetSdkVersion < 16 and requested READ_CONTACTS'\n");
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001453 }
1454 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
1455 printf("uses-permission:'android.permission.WRITE_CALL_LOG'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001456 printf("uses-implied-permission:'android.permission.WRITE_CALL_LOG'," \
1457 "'targetSdkVersion < 16 and requested WRITE_CONTACTS'\n");
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001458 }
1459 }
1460
Dan Morrill89d97c12010-05-03 16:13:14 -07001461 /* The following blocks handle printing "inferred" uses-features, based
1462 * on whether related features or permissions are used by the app.
1463 * Note that the various spec*Feature variables denote whether the
1464 * relevant tag was *present* in the AndroidManfest, not that it was
1465 * present and set to true.
1466 */
1467 // Camera-related back-compatibility logic
1468 if (!specCameraFeature) {
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001469 if (reqCameraFlashFeature) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001470 // if app requested a sub-feature (autofocus or flash) and didn't
1471 // request the base camera feature, we infer that it meant to
1472 printf("uses-feature:'android.hardware.camera'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001473 printf("uses-implied-feature:'android.hardware.camera'," \
1474 "'requested android.hardware.camera.flash feature'\n");
1475 } else if (reqCameraAutofocusFeature) {
1476 // if app requested a sub-feature (autofocus or flash) and didn't
1477 // request the base camera feature, we infer that it meant to
1478 printf("uses-feature:'android.hardware.camera'\n");
1479 printf("uses-implied-feature:'android.hardware.camera'," \
1480 "'requested android.hardware.camera.autofocus feature'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001481 } else if (hasCameraPermission) {
1482 // if app wants to use camera but didn't request the feature, we infer
1483 // that it meant to, and further that it wants autofocus
1484 // (which was the 1.0 - 1.5 behavior)
1485 printf("uses-feature:'android.hardware.camera'\n");
1486 if (!specCameraAutofocusFeature) {
1487 printf("uses-feature:'android.hardware.camera.autofocus'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001488 printf("uses-implied-feature:'android.hardware.camera.autofocus'," \
1489 "'requested android.permission.CAMERA permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001490 }
1491 }
Dianne Hackborne5276a72009-08-27 16:28:44 -07001492 }
Doug Zongkerdbe7a682009-10-09 11:24:51 -07001493
Dan Morrill89d97c12010-05-03 16:13:14 -07001494 // Location-related back-compatibility logic
1495 if (!specLocationFeature &&
1496 (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission ||
1497 hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) {
1498 // if app either takes a location-related permission or requests one of the
1499 // sub-features, we infer that it also meant to request the base location feature
1500 printf("uses-feature:'android.hardware.location'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001501 printf("uses-implied-feature:'android.hardware.location'," \
1502 "'requested a location access permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001503 }
Dianne Hackbornef05e072010-03-01 17:43:39 -08001504 if (!specGpsFeature && hasGpsPermission) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001505 // if app takes GPS (FINE location) perm but does not request the GPS
1506 // feature, we infer that it meant to
Dianne Hackbornef05e072010-03-01 17:43:39 -08001507 printf("uses-feature:'android.hardware.location.gps'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001508 printf("uses-implied-feature:'android.hardware.location.gps'," \
1509 "'requested android.permission.ACCESS_FINE_LOCATION permission'\n");
Dianne Hackbornef05e072010-03-01 17:43:39 -08001510 }
Dan Morrill89d97c12010-05-03 16:13:14 -07001511 if (!specNetworkLocFeature && hasCoarseLocPermission) {
1512 // if app takes Network location (COARSE location) perm but does not request the
1513 // network location feature, we infer that it meant to
1514 printf("uses-feature:'android.hardware.location.network'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001515 printf("uses-implied-feature:'android.hardware.location.network'," \
Dianne Hackbornec559ff2013-02-25 15:42:07 -08001516 "'requested android.permission.ACCESS_COARSE_LOCATION permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001517 }
1518
1519 // Bluetooth-related compatibility logic
Dan Morrill6b22d812010-06-15 21:41:42 -07001520 if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001521 // if app takes a Bluetooth permission but does not request the Bluetooth
1522 // feature, we infer that it meant to
1523 printf("uses-feature:'android.hardware.bluetooth'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001524 printf("uses-implied-feature:'android.hardware.bluetooth'," \
1525 "'requested android.permission.BLUETOOTH or android.permission.BLUETOOTH_ADMIN " \
1526 "permission and targetSdkVersion > 4'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001527 }
1528
1529 // Microphone-related compatibility logic
1530 if (!specMicrophoneFeature && hasRecordAudioPermission) {
1531 // if app takes the record-audio permission but does not request the microphone
1532 // feature, we infer that it meant to
1533 printf("uses-feature:'android.hardware.microphone'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001534 printf("uses-implied-feature:'android.hardware.microphone'," \
1535 "'requested android.permission.RECORD_AUDIO permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001536 }
1537
1538 // WiFi-related compatibility logic
1539 if (!specWiFiFeature && hasWiFiPermission) {
1540 // if app takes one of the WiFi permissions but does not request the WiFi
1541 // feature, we infer that it meant to
1542 printf("uses-feature:'android.hardware.wifi'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001543 printf("uses-implied-feature:'android.hardware.wifi'," \
1544 "'requested android.permission.ACCESS_WIFI_STATE, " \
1545 "android.permission.CHANGE_WIFI_STATE, or " \
1546 "android.permission.CHANGE_WIFI_MULTICAST_STATE permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001547 }
1548
1549 // Telephony-related compatibility logic
1550 if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) {
1551 // if app takes one of the telephony permissions or requests a sub-feature but
1552 // does not request the base telephony feature, we infer that it meant to
1553 printf("uses-feature:'android.hardware.telephony'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001554 printf("uses-implied-feature:'android.hardware.telephony'," \
1555 "'requested a telephony-related permission or feature'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001556 }
1557
1558 // Touchscreen-related back-compatibility logic
1559 if (!specTouchscreenFeature) { // not a typo!
1560 // all apps are presumed to require a touchscreen, unless they explicitly say
1561 // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
1562 // Note that specTouchscreenFeature is true if the tag is present, regardless
1563 // of whether its value is true or false, so this is safe
1564 printf("uses-feature:'android.hardware.touchscreen'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001565 printf("uses-implied-feature:'android.hardware.touchscreen'," \
1566 "'assumed you require a touch screen unless explicitly made optional'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001567 }
1568 if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
1569 // if app takes one of the telephony permissions or requests a sub-feature but
1570 // does not request the base telephony feature, we infer that it meant to
1571 printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001572 printf("uses-implied-feature:'android.hardware.touchscreen.multitouch'," \
1573 "'requested android.hardware.touchscreen.multitouch.distinct feature'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001574 }
Dianne Hackbornef05e072010-03-01 17:43:39 -08001575
Dianne Hackborne289bff2011-06-13 19:33:22 -07001576 // Landscape/portrait-related compatibility logic
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001577 if (!specScreenLandscapeFeature && !specScreenPortraitFeature) {
1578 // If the app has specified any activities in its manifest
1579 // that request a specific orientation, then assume that
1580 // orientation is required.
1581 if (reqScreenLandscapeFeature) {
1582 printf("uses-feature:'android.hardware.screen.landscape'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001583 printf("uses-implied-feature:'android.hardware.screen.landscape'," \
1584 "'one or more activities have specified a landscape orientation'\n");
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001585 }
1586 if (reqScreenPortraitFeature) {
1587 printf("uses-feature:'android.hardware.screen.portrait'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001588 printf("uses-implied-feature:'android.hardware.screen.portrait'," \
1589 "'one or more activities have specified a portrait orientation'\n");
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001590 }
Dianne Hackborne289bff2011-06-13 19:33:22 -07001591 }
1592
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001593 if (hasMainActivity) {
1594 printf("main\n");
1595 }
1596 if (hasWidgetReceivers) {
1597 printf("app-widget\n");
1598 }
Adam Lesinskib1249b82013-09-25 17:03:50 -07001599 if (hasDeviceAdminReceiver) {
1600 printf("device-admin\n");
1601 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001602 if (hasImeService) {
1603 printf("ime\n");
1604 }
1605 if (hasWallpaperService) {
1606 printf("wallpaper\n");
1607 }
Adam Lesinskib1249b82013-09-25 17:03:50 -07001608 if (hasAccessibilityService) {
1609 printf("accessibility\n");
1610 }
1611 if (hasPrintService) {
1612 printf("print\n");
1613 }
Adam Lesinskid831e802013-09-27 11:14:57 -07001614 if (hasPaymentService) {
1615 printf("payment\n");
1616 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001617 if (hasOtherActivities) {
1618 printf("other-activities\n");
1619 }
1620 if (isSearchable) {
1621 printf("search\n");
1622 }
1623 if (hasOtherReceivers) {
1624 printf("other-receivers\n");
1625 }
1626 if (hasOtherServices) {
1627 printf("other-services\n");
1628 }
1629
Dianne Hackborne289bff2011-06-13 19:33:22 -07001630 // For modern apps, if screen size buckets haven't been specified
1631 // but the new width ranges have, then infer the buckets from them.
1632 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1633 && requiresSmallestWidthDp > 0) {
1634 int compatWidth = compatibleWidthLimitDp;
1635 if (compatWidth <= 0) compatWidth = requiresSmallestWidthDp;
1636 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1637 smallScreen = -1;
1638 } else {
1639 smallScreen = 0;
1640 }
1641 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1642 normalScreen = -1;
1643 } else {
1644 normalScreen = 0;
1645 }
1646 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1647 largeScreen = -1;
1648 } else {
1649 largeScreen = 0;
1650 }
1651 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1652 xlargeScreen = -1;
1653 } else {
1654 xlargeScreen = 0;
1655 }
1656 }
1657
Dianne Hackborn723738c2009-06-25 19:48:04 -07001658 // Determine default values for any unspecified screen sizes,
1659 // based on the target SDK of the package. As of 4 (donut)
1660 // the screen size support was introduced, so all default to
1661 // enabled.
1662 if (smallScreen > 0) {
1663 smallScreen = targetSdk >= 4 ? -1 : 0;
1664 }
1665 if (normalScreen > 0) {
1666 normalScreen = -1;
1667 }
1668 if (largeScreen > 0) {
1669 largeScreen = targetSdk >= 4 ? -1 : 0;
1670 }
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001671 if (xlargeScreen > 0) {
Scott Maind58fb972010-11-04 18:32:00 -07001672 // Introduced in Gingerbread.
1673 xlargeScreen = targetSdk >= 9 ? -1 : 0;
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001674 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001675 if (anyDensity > 0) {
Dianne Hackborne289bff2011-06-13 19:33:22 -07001676 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1677 || compatibleWidthLimitDp > 0) ? -1 : 0;
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001678 }
Dianne Hackborn723738c2009-06-25 19:48:04 -07001679 printf("supports-screens:");
1680 if (smallScreen != 0) printf(" 'small'");
1681 if (normalScreen != 0) printf(" 'normal'");
1682 if (largeScreen != 0) printf(" 'large'");
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001683 if (xlargeScreen != 0) printf(" 'xlarge'");
Dianne Hackborn723738c2009-06-25 19:48:04 -07001684 printf("\n");
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001685 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
Dianne Hackborne289bff2011-06-13 19:33:22 -07001686 if (requiresSmallestWidthDp > 0) {
1687 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1688 }
1689 if (compatibleWidthLimitDp > 0) {
1690 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1691 }
1692 if (largestWidthLimitDp > 0) {
1693 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1694 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001695
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001696 printf("locales:");
Dianne Hackborne17086b2009-06-19 15:13:28 -07001697 const size_t NL = locales.size();
1698 for (size_t i=0; i<NL; i++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001699 const char* localeStr = locales[i].string();
1700 if (localeStr == NULL || strlen(localeStr) == 0) {
1701 localeStr = "--_--";
1702 }
1703 printf(" '%s'", localeStr);
1704 }
1705 printf("\n");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001706
Dianne Hackborne17086b2009-06-19 15:13:28 -07001707 printf("densities:");
1708 const size_t ND = densities.size();
1709 for (size_t i=0; i<ND; i++) {
1710 printf(" '%d'", densities[i]);
1711 }
1712 printf("\n");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001713
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001714 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1715 if (dir != NULL) {
1716 if (dir->getFileCount() > 0) {
1717 printf("native-code:");
1718 for (size_t i=0; i<dir->getFileCount(); i++) {
1719 printf(" '%s'", dir->getFileName(i).string());
1720 }
1721 printf("\n");
1722 }
1723 delete dir;
1724 }
Dan Morrille74763e2012-01-06 10:47:10 -08001725 } else if (strcmp("badger", option) == 0) {
Dianne Hackborn6c997a92012-01-31 11:27:43 -08001726 printf("%s", CONSOLE_DATA);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001727 } else if (strcmp("configurations", option) == 0) {
1728 Vector<ResTable_config> configs;
1729 res.getConfigurations(&configs);
1730 const size_t N = configs.size();
1731 for (size_t i=0; i<N; i++) {
1732 printf("%s\n", configs[i].toString().string());
1733 }
1734 } else {
1735 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
1736 goto bail;
1737 }
1738 }
1739
1740 result = NO_ERROR;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001741
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001742bail:
1743 if (asset) {
1744 delete asset;
1745 }
1746 return (result != NO_ERROR);
1747}
1748
1749
1750/*
1751 * Handle the "add" command, which wants to add files to a new or
1752 * pre-existing archive.
1753 */
1754int doAdd(Bundle* bundle)
1755{
1756 ZipFile* zip = NULL;
1757 status_t result = UNKNOWN_ERROR;
1758 const char* zipFileName;
1759
1760 if (bundle->getUpdate()) {
1761 /* avoid confusion */
1762 fprintf(stderr, "ERROR: can't use '-u' with add\n");
1763 goto bail;
1764 }
1765
1766 if (bundle->getFileSpecCount() < 1) {
1767 fprintf(stderr, "ERROR: must specify zip file name\n");
1768 goto bail;
1769 }
1770 zipFileName = bundle->getFileSpecEntry(0);
1771
1772 if (bundle->getFileSpecCount() < 2) {
1773 fprintf(stderr, "NOTE: nothing to do\n");
1774 goto bail;
1775 }
1776
1777 zip = openReadWrite(zipFileName, true);
1778 if (zip == NULL) {
1779 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
1780 goto bail;
1781 }
1782
1783 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1784 const char* fileName = bundle->getFileSpecEntry(i);
1785
1786 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
1787 printf(" '%s'... (from gzip)\n", fileName);
1788 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
1789 } else {
Doug Zongkerdbe7a682009-10-09 11:24:51 -07001790 if (bundle->getJunkPath()) {
1791 String8 storageName = String8(fileName).getPathLeaf();
1792 printf(" '%s' as '%s'...\n", fileName, storageName.string());
1793 result = zip->add(fileName, storageName.string(),
1794 bundle->getCompressionMethod(), NULL);
1795 } else {
1796 printf(" '%s'...\n", fileName);
1797 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
1798 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001799 }
1800 if (result != NO_ERROR) {
1801 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
1802 if (result == NAME_NOT_FOUND)
1803 fprintf(stderr, ": file not found\n");
1804 else if (result == ALREADY_EXISTS)
1805 fprintf(stderr, ": already exists in archive\n");
1806 else
1807 fprintf(stderr, "\n");
1808 goto bail;
1809 }
1810 }
1811
1812 result = NO_ERROR;
1813
1814bail:
1815 delete zip;
1816 return (result != NO_ERROR);
1817}
1818
1819
1820/*
1821 * Delete files from an existing archive.
1822 */
1823int doRemove(Bundle* bundle)
1824{
1825 ZipFile* zip = NULL;
1826 status_t result = UNKNOWN_ERROR;
1827 const char* zipFileName;
1828
1829 if (bundle->getFileSpecCount() < 1) {
1830 fprintf(stderr, "ERROR: must specify zip file name\n");
1831 goto bail;
1832 }
1833 zipFileName = bundle->getFileSpecEntry(0);
1834
1835 if (bundle->getFileSpecCount() < 2) {
1836 fprintf(stderr, "NOTE: nothing to do\n");
1837 goto bail;
1838 }
1839
1840 zip = openReadWrite(zipFileName, false);
1841 if (zip == NULL) {
1842 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
1843 zipFileName);
1844 goto bail;
1845 }
1846
1847 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1848 const char* fileName = bundle->getFileSpecEntry(i);
1849 ZipEntry* entry;
1850
1851 entry = zip->getEntryByName(fileName);
1852 if (entry == NULL) {
1853 printf(" '%s' NOT FOUND\n", fileName);
1854 continue;
1855 }
1856
1857 result = zip->remove(entry);
1858
1859 if (result != NO_ERROR) {
1860 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
1861 bundle->getFileSpecEntry(i), zipFileName);
1862 goto bail;
1863 }
1864 }
1865
1866 /* update the archive */
1867 zip->flush();
1868
1869bail:
1870 delete zip;
1871 return (result != NO_ERROR);
1872}
1873
1874
1875/*
1876 * Package up an asset directory and associated application files.
1877 */
1878int doPackage(Bundle* bundle)
1879{
1880 const char* outputAPKFile;
1881 int retVal = 1;
1882 status_t err;
1883 sp<AaptAssets> assets;
1884 int N;
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001885 FILE* fp;
1886 String8 dependencyFile;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001887
Anton Krumina2ef5c02014-03-12 14:46:44 -07001888 // -c en_XA or/and ar_XB means do pseudolocalization
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001889 ResourceFilter filter;
1890 err = filter.parse(bundle->getConfigurations());
1891 if (err != NO_ERROR) {
1892 goto bail;
1893 }
1894 if (filter.containsPseudo()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07001895 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
1896 }
1897 if (filter.containsPseudoBidi()) {
1898 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001899 }
1900
1901 N = bundle->getFileSpecCount();
1902 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
1903 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDir() == NULL) {
1904 fprintf(stderr, "ERROR: no input files\n");
1905 goto bail;
1906 }
1907
1908 outputAPKFile = bundle->getOutputAPKFile();
1909
1910 // Make sure the filenames provided exist and are of the appropriate type.
1911 if (outputAPKFile) {
1912 FileType type;
1913 type = getFileType(outputAPKFile);
1914 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
1915 fprintf(stderr,
1916 "ERROR: output file '%s' exists but is not regular file\n",
1917 outputAPKFile);
1918 goto bail;
1919 }
1920 }
1921
1922 // Load the assets.
1923 assets = new AaptAssets();
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001924
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001925 // Set up the resource gathering in assets if we're going to generate
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001926 // dependency files. Every time we encounter a resource while slurping
1927 // the tree, we'll add it to these stores so we have full resource paths
1928 // to write to a dependency file.
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001929 if (bundle->getGenDependencies()) {
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001930 sp<FilePathStore> resPathStore = new FilePathStore;
1931 assets->setFullResPaths(resPathStore);
1932 sp<FilePathStore> assetPathStore = new FilePathStore;
1933 assets->setFullAssetPaths(assetPathStore);
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001934 }
1935
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001936 err = assets->slurpFromArgs(bundle);
1937 if (err < 0) {
1938 goto bail;
1939 }
1940
1941 if (bundle->getVerbose()) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001942 assets->print(String8());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001943 }
1944
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001945 // If they asked for any fileAs that need to be compiled, do so.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001946 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
1947 err = buildResources(bundle, assets);
1948 if (err != 0) {
1949 goto bail;
1950 }
1951 }
1952
1953 // At this point we've read everything and processed everything. From here
1954 // on out it's just writing output files.
1955 if (SourcePos::hasErrors()) {
1956 goto bail;
1957 }
1958
Dianne Hackborn1644c6d72012-02-06 15:33:21 -08001959 // Update symbols with information about which ones are needed as Java symbols.
1960 assets->applyJavaSymbols();
1961 if (SourcePos::hasErrors()) {
1962 goto bail;
1963 }
1964
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001965 // If we've been asked to generate a dependency file, do that here
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001966 if (bundle->getGenDependencies()) {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001967 // If this is the packaging step, generate the dependency file next to
1968 // the output apk (e.g. bin/resources.ap_.d)
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001969 if (outputAPKFile) {
1970 dependencyFile = String8(outputAPKFile);
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001971 // Add the .d extension to the dependency file.
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001972 dependencyFile.append(".d");
1973 } else {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001974 // Else if this is the R.java dependency generation step,
1975 // generate the dependency file in the R.java package subdirectory
1976 // e.g. gen/com/foo/app/R.java.d
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001977 dependencyFile = String8(bundle->getRClassDir());
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001978 dependencyFile.appendPath("R.java.d");
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001979 }
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001980 // Make sure we have a clean dependency file to start with
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001981 fp = fopen(dependencyFile, "w");
1982 fclose(fp);
1983 }
1984
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001985 // Write out R.java constants
Dianne Hackborn1644c6d72012-02-06 15:33:21 -08001986 if (!assets->havePrivateSymbols()) {
Xavier Ducrohet63459ad2009-11-30 18:05:10 -08001987 if (bundle->getCustomPackage() == NULL) {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001988 // Write the R.java file into the appropriate class directory
1989 // e.g. gen/com/foo/app/R.java
Xavier Ducrohet63459ad2009-11-30 18:05:10 -08001990 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
1991 } else {
1992 const String8 customPkg(bundle->getCustomPackage());
1993 err = writeResourceSymbols(bundle, assets, customPkg, true);
1994 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001995 if (err < 0) {
1996 goto bail;
1997 }
Ying Wang002f5372012-04-25 18:53:49 -07001998 // If we have library files, we're going to write our R.java file into
1999 // the appropriate class directory for those libraries as well.
2000 // e.g. gen/com/foo/app/lib/R.java
2001 if (bundle->getExtraPackages() != NULL) {
2002 // Split on colon
2003 String8 libs(bundle->getExtraPackages());
2004 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2005 while (packageString != NULL) {
2006 // Write the R.java file out with the correct package name
2007 err = writeResourceSymbols(bundle, assets, String8(packageString), true);
2008 if (err < 0) {
2009 goto bail;
2010 }
2011 packageString = strtok(NULL, ":");
2012 }
2013 libs.unlockBuffer();
2014 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002015 } else {
2016 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
2017 if (err < 0) {
2018 goto bail;
2019 }
2020 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
2021 if (err < 0) {
2022 goto bail;
2023 }
2024 }
2025
Joe Onorato1553c822009-08-30 13:36:22 -07002026 // Write out the ProGuard file
2027 err = writeProguardFile(bundle, assets);
2028 if (err < 0) {
2029 goto bail;
2030 }
2031
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002032 // Write the apk
2033 if (outputAPKFile) {
2034 err = writeAPK(bundle, assets, String8(outputAPKFile));
2035 if (err != NO_ERROR) {
2036 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
2037 goto bail;
2038 }
2039 }
2040
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07002041 // If we've been asked to generate a dependency file, we need to finish up here.
2042 // the writeResourceSymbols and writeAPK functions have already written the target
2043 // half of the dependency file, now we need to write the prerequisites. (files that
2044 // the R.java file or .ap_ file depend on)
Josiah Gaskin03589cc2011-06-27 16:26:02 -07002045 if (bundle->getGenDependencies()) {
2046 // Now that writeResourceSymbols or writeAPK has taken care of writing
2047 // the targets to our dependency file, we'll write the prereqs
2048 fp = fopen(dependencyFile, "a+");
2049 fprintf(fp, " : ");
2050 bool includeRaw = (outputAPKFile != NULL);
2051 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07002052 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2053 // and therefore was not added to our pathstores during slurping
Josiah Gaskin03589cc2011-06-27 16:26:02 -07002054 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2055 fclose(fp);
2056 }
2057
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002058 retVal = 0;
2059bail:
2060 if (SourcePos::hasErrors()) {
2061 SourcePos::printErrors(stderr);
2062 }
2063 return retVal;
2064}
Josiah Gaskin8a39da82011-06-06 17:00:35 -07002065
2066/*
2067 * Do PNG Crunching
2068 * PRECONDITIONS
2069 * -S flag points to a source directory containing drawable* folders
2070 * -C flag points to destination directory. The folder structure in the
2071 * source directory will be mirrored to the destination (cache) directory
2072 *
2073 * POSTCONDITIONS
2074 * Destination directory will be updated to match the PNG files in
2075 * the source directory.
2076 */
2077int doCrunch(Bundle* bundle)
2078{
2079 fprintf(stdout, "Crunching PNG Files in ");
2080 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2081 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2082
2083 updatePreProcessedCache(bundle);
2084
2085 return NO_ERROR;
2086}
Dan Morrille74763e2012-01-06 10:47:10 -08002087
Xavier Ducrohetb1f6ad82012-12-21 09:54:02 -08002088/*
2089 * Do PNG Crunching on a single flag
2090 * -i points to a single png file
2091 * -o points to a single png output file
2092 */
2093int doSingleCrunch(Bundle* bundle)
2094{
2095 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2096 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2097
2098 String8 input(bundle->getSingleCrunchInputFile());
2099 String8 output(bundle->getSingleCrunchOutputFile());
Xavier Ducrohetb7de2192013-01-15 16:41:29 -08002100
Xavier Ducrohetc75d3f52013-01-14 18:02:40 -08002101 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2102 // we can't return the status_t as it gets truncate to the lower 8 bits.
2103 return 42;
2104 }
Xavier Ducrohetb7de2192013-01-15 16:41:29 -08002105
Xavier Ducrohetc75d3f52013-01-14 18:02:40 -08002106 return NO_ERROR;
Xavier Ducrohetb1f6ad82012-12-21 09:54:02 -08002107}
2108
Dan Morrille74763e2012-01-06 10:47:10 -08002109char CONSOLE_DATA[2925] = {
2110 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2111 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2112 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2113 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2114 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2115 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2116 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2117 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2118 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2119 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2120 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2121 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2122 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2123 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2124 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2125 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2126 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2127 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2128 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2129 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2130 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2131 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2132 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2133 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2134 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2135 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2136 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2137 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2138 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2139 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2140 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2141 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2142 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2143 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2144 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2145 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2146 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2147 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2148 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2149 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2150 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2151 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2152 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2153 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2154 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2155 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2156 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2157 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2158 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2159 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2160 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2161 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2162 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2163 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2164 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2165 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2166 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2167 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2168 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2169 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2170 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2171 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2172 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2173 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2174 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2175 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2176 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2177 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2178 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2179 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2180 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2181 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2182 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2183 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2184 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2185 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2186 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2187 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2188 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2189 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2190 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2191 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2192 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2193 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2194 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2195 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2196 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2197 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2198 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2199 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2200 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2201 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2202 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2203 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2204 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2205 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2206 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2207 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2208 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2209 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2210 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2211 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2212 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2213 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2214 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2215 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2216 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2217 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2218 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2219 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2220 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2221 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2222 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2223 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2224 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2225 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2226 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2227 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2228 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2229 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2230 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2231 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2232 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2233 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2234 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2235 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2236 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2237 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2238 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2239 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2240 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2241 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2242 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2243 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2244 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2245 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2246 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2247 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2248 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2249 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2250 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2251 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2252 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2253 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2254 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2255 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2256 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2257 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2258 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2259 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2260 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2261 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2262 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2263 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2264 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2265 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2266 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2267 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2268 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2269 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2270 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2271 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2272 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2273 };