blob: 632efe04f3c210259059f028b06b505d9e69c164 [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;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700508 void* assetsCookie;
509 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;
520 config.language[0] = 'e';
521 config.language[1] = 'n';
522 config.country[0] = 'U';
523 config.country[1] = 'S';
524 config.orientation = ResTable_config::ORIENTATION_PORT;
525 config.density = ResTable_config::DENSITY_MEDIUM;
526 config.sdkVersion = 10000; // Very high.
527 config.screenWidthDp = 320;
528 config.screenHeightDp = 480;
529 config.smallestScreenWidthDp = 320;
530 assets.setConfiguration(config);
531
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800532 const ResTable& res = assets.getResources(false);
533 if (&res == NULL) {
534 fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
535 goto bail;
536 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700537
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800538 if (strcmp("resources", option) == 0) {
Steve Blockf1ff21a2010-06-14 17:34:04 +0100539#ifndef HAVE_ANDROID_OS
Dianne Hackborne17086b2009-06-19 15:13:28 -0700540 res.print(bundle->getValues());
Steve Blockf1ff21a2010-06-14 17:34:04 +0100541#endif
Dianne Hackborn6c997a92012-01-31 11:27:43 -0800542
543 } else if (strcmp("strings", option) == 0) {
544 const ResStringPool* pool = res.getTableStringBlock(0);
545 printStringPool(pool);
546
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800547 } else if (strcmp("xmltree", option) == 0) {
548 if (bundle->getFileSpecCount() < 3) {
549 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
550 goto bail;
551 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700552
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800553 for (int i=2; i<bundle->getFileSpecCount(); i++) {
554 const char* resname = bundle->getFileSpecEntry(i);
555 ResXMLTree tree;
556 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
557 if (asset == NULL) {
Kenny Root44b283d2009-09-01 19:03:11 -0500558 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800559 goto bail;
560 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700561
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800562 if (tree.setTo(asset->getBuffer(true),
563 asset->getLength()) != NO_ERROR) {
564 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
565 goto bail;
566 }
567 tree.restart();
568 printXMLBlock(&tree);
Kenny Root19138462009-12-04 09:38:48 -0800569 tree.uninit();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800570 delete asset;
571 asset = NULL;
572 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700573
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800574 } else if (strcmp("xmlstrings", option) == 0) {
575 if (bundle->getFileSpecCount() < 3) {
576 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
577 goto bail;
578 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700579
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800580 for (int i=2; i<bundle->getFileSpecCount(); i++) {
581 const char* resname = bundle->getFileSpecEntry(i);
582 ResXMLTree tree;
583 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
584 if (asset == NULL) {
Kenny Root44b283d2009-09-01 19:03:11 -0500585 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800586 goto bail;
587 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700588
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800589 if (tree.setTo(asset->getBuffer(true),
590 asset->getLength()) != NO_ERROR) {
591 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
592 goto bail;
593 }
594 printStringPool(&tree.getStrings());
595 delete asset;
596 asset = NULL;
597 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700598
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800599 } else {
600 ResXMLTree tree;
601 asset = assets.openNonAsset("AndroidManifest.xml",
602 Asset::ACCESS_BUFFER);
603 if (asset == NULL) {
604 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
605 goto bail;
606 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700607
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800608 if (tree.setTo(asset->getBuffer(true),
609 asset->getLength()) != NO_ERROR) {
610 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
611 goto bail;
612 }
613 tree.restart();
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700614
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800615 if (strcmp("permissions", option) == 0) {
616 size_t len;
617 ResXMLTree::event_code_t code;
618 int depth = 0;
619 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
620 if (code == ResXMLTree::END_TAG) {
621 depth--;
622 continue;
623 }
624 if (code != ResXMLTree::START_TAG) {
625 continue;
626 }
627 depth++;
628 String8 tag(tree.getElementName(&len));
629 //printf("Depth %d tag %s\n", depth, tag.string());
630 if (depth == 1) {
631 if (tag != "manifest") {
632 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
633 goto bail;
634 }
635 String8 pkg = getAttribute(tree, NULL, "package", NULL);
636 printf("package: %s\n", pkg.string());
637 } else if (depth == 2 && tag == "permission") {
638 String8 error;
639 String8 name = getAttribute(tree, NAME_ATTR, &error);
640 if (error != "") {
641 fprintf(stderr, "ERROR: %s\n", error.string());
642 goto bail;
643 }
644 printf("permission: %s\n", name.string());
645 } else if (depth == 2 && tag == "uses-permission") {
646 String8 error;
647 String8 name = getAttribute(tree, NAME_ATTR, &error);
648 if (error != "") {
649 fprintf(stderr, "ERROR: %s\n", error.string());
650 goto bail;
651 }
652 printf("uses-permission: %s\n", name.string());
Nick Kralevich1bcc3d62013-04-03 09:00:02 -0700653 int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
654 if (!req) {
655 printf("optional-permission: %s\n", name.string());
656 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800657 }
658 }
659 } else if (strcmp("badging", option) == 0) {
Dianne Hackborne289bff2011-06-13 19:33:22 -0700660 Vector<String8> locales;
661 res.getLocales(&locales);
662
663 Vector<ResTable_config> configs;
664 res.getConfigurations(&configs);
665 SortedVector<int> densities;
666 const size_t NC = configs.size();
667 for (size_t i=0; i<NC; i++) {
668 int dens = configs[i].density;
669 if (dens == 0) dens = 160;
670 densities.add(dens);
671 }
672
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800673 size_t len;
674 ResXMLTree::event_code_t code;
675 int depth = 0;
676 String8 error;
677 bool withinActivity = false;
678 bool isMainActivity = false;
679 bool isLauncherActivity = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700680 bool isSearchable = false;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700681 bool withinApplication = false;
Michael Wrighteaeb1902013-09-05 18:15:57 -0700682 bool withinSupportsInput = false;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700683 bool withinReceiver = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700684 bool withinService = false;
685 bool withinIntentFilter = false;
686 bool hasMainActivity = false;
687 bool hasOtherActivities = false;
688 bool hasOtherReceivers = false;
689 bool hasOtherServices = false;
690 bool hasWallpaperService = false;
691 bool hasImeService = false;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700692 bool hasAccessibilityService = false;
693 bool hasPrintService = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700694 bool hasWidgetReceivers = false;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700695 bool hasDeviceAdminReceiver = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700696 bool hasIntentFilter = false;
Adam Lesinskid831e802013-09-27 11:14:57 -0700697 bool hasPaymentService = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700698 bool actMainActivity = false;
699 bool actWidgetReceivers = false;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700700 bool actDeviceAdminEnabled = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700701 bool actImeService = false;
702 bool actWallpaperService = false;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700703 bool actAccessibilityService = false;
704 bool actPrintService = false;
Adam Lesinskid831e802013-09-27 11:14:57 -0700705 bool actHostApduService = false;
706 bool actOffHostApduService = false;
707 bool hasMetaHostPaymentCategory = false;
708 bool hasMetaOffHostPaymentCategory = false;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700709
710 // These permissions are required by services implementing services
711 // the system binds to (IME, Accessibility, PrintServices, etc.)
712 bool hasBindDeviceAdminPermission = false;
713 bool hasBindInputMethodPermission = false;
714 bool hasBindAccessibilityServicePermission = false;
715 bool hasBindPrintServicePermission = false;
Adam Lesinskid831e802013-09-27 11:14:57 -0700716 bool hasBindNfcServicePermission = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700717
Kenny Root063a44e2011-12-08 08:46:03 -0800718 // These two implement the implicit permissions that are granted
719 // to pre-1.6 applications.
720 bool hasWriteExternalStoragePermission = false;
721 bool hasReadPhoneStatePermission = false;
722
Dianne Hackborn79245122012-03-12 10:51:26 -0700723 // If an app requests write storage, they will also get read storage.
724 bool hasReadExternalStoragePermission = false;
725
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -0700726 // Implement transition to read and write call log.
727 bool hasReadContactsPermission = false;
728 bool hasWriteContactsPermission = false;
729 bool hasReadCallLogPermission = false;
730 bool hasWriteCallLogPermission = false;
731
Dan Morrill89d97c12010-05-03 16:13:14 -0700732 // This next group of variables is used to implement a group of
733 // backward-compatibility heuristics necessitated by the addition of
734 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
735 // heuristic is "if an app requests a permission but doesn't explicitly
736 // request the corresponding <uses-feature>, presume it's there anyway".
737 bool specCameraFeature = false; // camera-related
738 bool specCameraAutofocusFeature = false;
739 bool reqCameraAutofocusFeature = false;
740 bool reqCameraFlashFeature = false;
Dianne Hackborne5276a72009-08-27 16:28:44 -0700741 bool hasCameraPermission = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700742 bool specLocationFeature = false; // location-related
743 bool specNetworkLocFeature = false;
744 bool reqNetworkLocFeature = false;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800745 bool specGpsFeature = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700746 bool reqGpsFeature = false;
747 bool hasMockLocPermission = false;
748 bool hasCoarseLocPermission = false;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800749 bool hasGpsPermission = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700750 bool hasGeneralLocPermission = false;
751 bool specBluetoothFeature = false; // Bluetooth API-related
752 bool hasBluetoothPermission = false;
753 bool specMicrophoneFeature = false; // microphone-related
754 bool hasRecordAudioPermission = false;
755 bool specWiFiFeature = false;
756 bool hasWiFiPermission = false;
757 bool specTelephonyFeature = false; // telephony-related
758 bool reqTelephonySubFeature = false;
759 bool hasTelephonyPermission = false;
760 bool specTouchscreenFeature = false; // touchscreen-related
761 bool specMultitouchFeature = false;
762 bool reqDistinctMultitouchFeature = false;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700763 bool specScreenPortraitFeature = false;
764 bool specScreenLandscapeFeature = false;
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -0700765 bool reqScreenPortraitFeature = false;
766 bool reqScreenLandscapeFeature = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700767 // 2.2 also added some other features that apps can request, but that
768 // have no corresponding permission, so we cannot implement any
769 // back-compatibility heuristic for them. The below are thus unnecessary
770 // (but are retained here for documentary purposes.)
771 //bool specCompassFeature = false;
772 //bool specAccelerometerFeature = false;
773 //bool specProximityFeature = false;
774 //bool specAmbientLightFeature = false;
775 //bool specLiveWallpaperFeature = false;
776
Dianne Hackborn723738c2009-06-25 19:48:04 -0700777 int targetSdk = 0;
778 int smallScreen = 1;
779 int normalScreen = 1;
780 int largeScreen = 1;
Dianne Hackbornf43489d2010-08-20 12:44:33 -0700781 int xlargeScreen = 1;
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700782 int anyDensity = 1;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700783 int requiresSmallestWidthDp = 0;
784 int compatibleWidthLimitDp = 0;
785 int largestWidthLimitDp = 0;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700786 String8 pkg;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800787 String8 activityName;
788 String8 activityLabel;
789 String8 activityIcon;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700790 String8 receiverName;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700791 String8 serviceName;
Michael Wrighteaeb1902013-09-05 18:15:57 -0700792 Vector<String8> supportedInput;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800793 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
794 if (code == ResXMLTree::END_TAG) {
795 depth--;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700796 if (depth < 2) {
Michael Wrighteaeb1902013-09-05 18:15:57 -0700797 if (withinSupportsInput && !supportedInput.isEmpty()) {
798 printf("supports-input: '");
799 const size_t N = supportedInput.size();
800 for (size_t i=0; i<N; i++) {
801 printf("%s", supportedInput[i].string());
802 if (i != N - 1) {
803 printf("' '");
804 } else {
805 printf("'\n");
806 }
807 }
808 supportedInput.clear();
809 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700810 withinApplication = false;
Michael Wrighteaeb1902013-09-05 18:15:57 -0700811 withinSupportsInput = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700812 } else if (depth < 3) {
813 if (withinActivity && isMainActivity && isLauncherActivity) {
814 const char *aName = getComponentName(pkg, activityName);
Dianne Hackborne289bff2011-06-13 19:33:22 -0700815 printf("launchable-activity:");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700816 if (aName != NULL) {
Dianne Hackborne289bff2011-06-13 19:33:22 -0700817 printf(" name='%s' ", aName);
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700818 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700819 printf(" label='%s' icon='%s'\n",
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700820 activityLabel.string(),
821 activityIcon.string());
822 }
823 if (!hasIntentFilter) {
824 hasOtherActivities |= withinActivity;
825 hasOtherReceivers |= withinReceiver;
826 hasOtherServices |= withinService;
Adam Lesinskid831e802013-09-27 11:14:57 -0700827 } else {
828 if (withinService) {
829 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
830 hasBindNfcServicePermission);
831 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
832 hasBindNfcServicePermission);
833 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700834 }
835 withinActivity = false;
836 withinService = false;
837 withinReceiver = false;
838 hasIntentFilter = false;
839 isMainActivity = isLauncherActivity = false;
840 } else if (depth < 4) {
841 if (withinIntentFilter) {
842 if (withinActivity) {
843 hasMainActivity |= actMainActivity;
844 hasOtherActivities |= !actMainActivity;
845 } else if (withinReceiver) {
846 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700847 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
848 hasBindDeviceAdminPermission);
849 hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled);
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700850 } else if (withinService) {
851 hasImeService |= actImeService;
852 hasWallpaperService |= actWallpaperService;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700853 hasAccessibilityService |= (actAccessibilityService &&
854 hasBindAccessibilityServicePermission);
855 hasPrintService |= (actPrintService && hasBindPrintServicePermission);
856 hasOtherServices |= (!actImeService && !actWallpaperService &&
Adam Lesinskid831e802013-09-27 11:14:57 -0700857 !actAccessibilityService && !actPrintService &&
858 !actHostApduService && !actOffHostApduService);
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700859 }
860 }
861 withinIntentFilter = false;
862 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800863 continue;
864 }
865 if (code != ResXMLTree::START_TAG) {
866 continue;
867 }
868 depth++;
869 String8 tag(tree.getElementName(&len));
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700870 //printf("Depth %d, %s\n", depth, tag.string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800871 if (depth == 1) {
872 if (tag != "manifest") {
873 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
874 goto bail;
875 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700876 pkg = getAttribute(tree, NULL, "package", NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800877 printf("package: name='%s' ", pkg.string());
878 int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
879 if (error != "") {
880 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
881 goto bail;
882 }
883 if (versionCode > 0) {
884 printf("versionCode='%d' ", versionCode);
885 } else {
886 printf("versionCode='' ");
887 }
Dianne Hackborncf244ad2010-03-09 15:00:30 -0800888 String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800889 if (error != "") {
890 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
891 goto bail;
892 }
893 printf("versionName='%s'\n", versionName.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700894 } else if (depth == 2) {
895 withinApplication = false;
896 if (tag == "application") {
897 withinApplication = true;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700898
899 String8 label;
900 const size_t NL = locales.size();
901 for (size_t i=0; i<NL; i++) {
902 const char* localeStr = locales[i].string();
903 assets.setLocale(localeStr != NULL ? localeStr : "");
904 String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
905 if (llabel != "") {
906 if (localeStr == NULL || strlen(localeStr) == 0) {
907 label = llabel;
908 printf("application-label:'%s'\n", llabel.string());
909 } else {
910 if (label == "") {
911 label = llabel;
912 }
913 printf("application-label-%s:'%s'\n", localeStr,
914 llabel.string());
915 }
916 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700917 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700918
919 ResTable_config tmpConfig = config;
920 const size_t ND = densities.size();
921 for (size_t i=0; i<ND; i++) {
922 tmpConfig.density = densities[i];
923 assets.setConfiguration(tmpConfig);
924 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
925 if (icon != "") {
926 printf("application-icon-%d:'%s'\n", densities[i], icon.string());
927 }
928 }
929 assets.setConfiguration(config);
930
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700931 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
932 if (error != "") {
933 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
934 goto bail;
935 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700936 int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700937 if (error != "") {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700938 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700939 goto bail;
940 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700941 printf("application: label='%s' ", label.string());
942 printf("icon='%s'\n", icon.string());
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700943 if (testOnly != 0) {
944 printf("testOnly='%d'\n", testOnly);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700945 }
Dan Morrillb6ec11e2012-04-03 12:44:40 -0700946
947 int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0);
948 if (error != "") {
949 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string());
950 goto bail;
951 }
952 if (debuggable != 0) {
953 printf("application-debuggable\n");
954 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700955 } else if (tag == "uses-sdk") {
956 int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
957 if (error != "") {
958 error = "";
959 String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
960 if (error != "") {
961 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
962 error.string());
963 goto bail;
964 }
Dianne Hackborn723738c2009-06-25 19:48:04 -0700965 if (name == "Donut") targetSdk = 4;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700966 printf("sdkVersion:'%s'\n", name.string());
967 } else if (code != -1) {
Dianne Hackborn723738c2009-06-25 19:48:04 -0700968 targetSdk = code;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700969 printf("sdkVersion:'%d'\n", code);
970 }
Suchi Amalapurapu75c49842009-08-14 15:13:09 -0700971 code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
972 if (code != -1) {
973 printf("maxSdkVersion:'%d'\n", code);
974 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700975 code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
976 if (error != "") {
977 error = "";
978 String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
979 if (error != "") {
980 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
981 error.string());
982 goto bail;
983 }
Dianne Hackborn723738c2009-06-25 19:48:04 -0700984 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700985 printf("targetSdkVersion:'%s'\n", name.string());
986 } else if (code != -1) {
Dianne Hackborn723738c2009-06-25 19:48:04 -0700987 if (targetSdk < code) {
988 targetSdk = code;
989 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700990 printf("targetSdkVersion:'%d'\n", code);
991 }
992 } else if (tag == "uses-configuration") {
993 int32_t reqTouchScreen = getIntegerAttribute(tree,
994 REQ_TOUCH_SCREEN_ATTR, NULL, 0);
995 int32_t reqKeyboardType = getIntegerAttribute(tree,
996 REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
997 int32_t reqHardKeyboard = getIntegerAttribute(tree,
998 REQ_HARD_KEYBOARD_ATTR, NULL, 0);
999 int32_t reqNavigation = getIntegerAttribute(tree,
1000 REQ_NAVIGATION_ATTR, NULL, 0);
1001 int32_t reqFiveWayNav = getIntegerAttribute(tree,
1002 REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
Dianne Hackborncb2d50d2010-01-06 11:29:54 -08001003 printf("uses-configuration:");
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001004 if (reqTouchScreen != 0) {
1005 printf(" reqTouchScreen='%d'", reqTouchScreen);
1006 }
1007 if (reqKeyboardType != 0) {
1008 printf(" reqKeyboardType='%d'", reqKeyboardType);
1009 }
1010 if (reqHardKeyboard != 0) {
1011 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1012 }
1013 if (reqNavigation != 0) {
1014 printf(" reqNavigation='%d'", reqNavigation);
1015 }
1016 if (reqFiveWayNav != 0) {
1017 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1018 }
1019 printf("\n");
Michael Wrighteaeb1902013-09-05 18:15:57 -07001020 } else if (tag == "supports-input") {
1021 withinSupportsInput = true;
Dianne Hackborn723738c2009-06-25 19:48:04 -07001022 } else if (tag == "supports-screens") {
1023 smallScreen = getIntegerAttribute(tree,
1024 SMALL_SCREEN_ATTR, NULL, 1);
1025 normalScreen = getIntegerAttribute(tree,
1026 NORMAL_SCREEN_ATTR, NULL, 1);
1027 largeScreen = getIntegerAttribute(tree,
1028 LARGE_SCREEN_ATTR, NULL, 1);
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001029 xlargeScreen = getIntegerAttribute(tree,
1030 XLARGE_SCREEN_ATTR, NULL, 1);
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001031 anyDensity = getIntegerAttribute(tree,
1032 ANY_DENSITY_ATTR, NULL, 1);
Dianne Hackborne289bff2011-06-13 19:33:22 -07001033 requiresSmallestWidthDp = getIntegerAttribute(tree,
1034 REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
1035 compatibleWidthLimitDp = getIntegerAttribute(tree,
1036 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
1037 largestWidthLimitDp = getIntegerAttribute(tree,
1038 LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
Dianne Hackborne5276a72009-08-27 16:28:44 -07001039 } else if (tag == "uses-feature") {
1040 String8 name = getAttribute(tree, NAME_ATTR, &error);
Suchi Amalapurapu40b94722009-09-20 13:39:37 -07001041
1042 if (name != "" && error == "") {
Dianne Hackborne5276a72009-08-27 16:28:44 -07001043 int req = getIntegerAttribute(tree,
1044 REQUIRED_ATTR, NULL, 1);
Dan Morrill89d97c12010-05-03 16:13:14 -07001045
Dianne Hackborne5276a72009-08-27 16:28:44 -07001046 if (name == "android.hardware.camera") {
1047 specCameraFeature = true;
Dan Morrill89d97c12010-05-03 16:13:14 -07001048 } else if (name == "android.hardware.camera.autofocus") {
1049 // these have no corresponding permission to check for,
1050 // but should imply the foundational camera permission
1051 reqCameraAutofocusFeature = reqCameraAutofocusFeature || req;
1052 specCameraAutofocusFeature = true;
1053 } else if (req && (name == "android.hardware.camera.flash")) {
1054 // these have no corresponding permission to check for,
1055 // but should imply the foundational camera permission
1056 reqCameraFlashFeature = true;
1057 } else if (name == "android.hardware.location") {
1058 specLocationFeature = true;
1059 } else if (name == "android.hardware.location.network") {
1060 specNetworkLocFeature = true;
1061 reqNetworkLocFeature = reqNetworkLocFeature || req;
Dianne Hackbornef05e072010-03-01 17:43:39 -08001062 } else if (name == "android.hardware.location.gps") {
1063 specGpsFeature = true;
Dan Morrill89d97c12010-05-03 16:13:14 -07001064 reqGpsFeature = reqGpsFeature || req;
1065 } else if (name == "android.hardware.bluetooth") {
1066 specBluetoothFeature = true;
1067 } else if (name == "android.hardware.touchscreen") {
1068 specTouchscreenFeature = true;
1069 } else if (name == "android.hardware.touchscreen.multitouch") {
1070 specMultitouchFeature = true;
1071 } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
1072 reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req;
1073 } else if (name == "android.hardware.microphone") {
1074 specMicrophoneFeature = true;
1075 } else if (name == "android.hardware.wifi") {
1076 specWiFiFeature = true;
1077 } else if (name == "android.hardware.telephony") {
1078 specTelephonyFeature = true;
1079 } else if (req && (name == "android.hardware.telephony.gsm" ||
1080 name == "android.hardware.telephony.cdma")) {
1081 // these have no corresponding permission to check for,
1082 // but should imply the foundational telephony permission
1083 reqTelephonySubFeature = true;
Dianne Hackborne289bff2011-06-13 19:33:22 -07001084 } else if (name == "android.hardware.screen.portrait") {
1085 specScreenPortraitFeature = true;
1086 } else if (name == "android.hardware.screen.landscape") {
1087 specScreenLandscapeFeature = true;
Dianne Hackborne5276a72009-08-27 16:28:44 -07001088 }
1089 printf("uses-feature%s:'%s'\n",
1090 req ? "" : "-not-required", name.string());
1091 } else {
1092 int vers = getIntegerAttribute(tree,
1093 GL_ES_VERSION_ATTR, &error);
1094 if (error == "") {
1095 printf("uses-gl-es:'0x%x'\n", vers);
1096 }
1097 }
1098 } else if (tag == "uses-permission") {
1099 String8 name = getAttribute(tree, NAME_ATTR, &error);
Suchi Amalapurapu40b94722009-09-20 13:39:37 -07001100 if (name != "" && error == "") {
Dianne Hackborne5276a72009-08-27 16:28:44 -07001101 if (name == "android.permission.CAMERA") {
1102 hasCameraPermission = true;
Dianne Hackbornef05e072010-03-01 17:43:39 -08001103 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
1104 hasGpsPermission = true;
Dan Morrill89d97c12010-05-03 16:13:14 -07001105 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
1106 hasMockLocPermission = true;
1107 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
1108 hasCoarseLocPermission = true;
1109 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
1110 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
1111 hasGeneralLocPermission = true;
1112 } else if (name == "android.permission.BLUETOOTH" ||
1113 name == "android.permission.BLUETOOTH_ADMIN") {
1114 hasBluetoothPermission = true;
1115 } else if (name == "android.permission.RECORD_AUDIO") {
1116 hasRecordAudioPermission = true;
1117 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1118 name == "android.permission.CHANGE_WIFI_STATE" ||
1119 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
1120 hasWiFiPermission = true;
1121 } else if (name == "android.permission.CALL_PHONE" ||
1122 name == "android.permission.CALL_PRIVILEGED" ||
1123 name == "android.permission.MODIFY_PHONE_STATE" ||
1124 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1125 name == "android.permission.READ_SMS" ||
1126 name == "android.permission.RECEIVE_SMS" ||
1127 name == "android.permission.RECEIVE_MMS" ||
1128 name == "android.permission.RECEIVE_WAP_PUSH" ||
1129 name == "android.permission.SEND_SMS" ||
1130 name == "android.permission.WRITE_APN_SETTINGS" ||
1131 name == "android.permission.WRITE_SMS") {
1132 hasTelephonyPermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001133 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1134 hasWriteExternalStoragePermission = true;
Dianne Hackborn79245122012-03-12 10:51:26 -07001135 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1136 hasReadExternalStoragePermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001137 } else if (name == "android.permission.READ_PHONE_STATE") {
1138 hasReadPhoneStatePermission = true;
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001139 } else if (name == "android.permission.READ_CONTACTS") {
1140 hasReadContactsPermission = true;
1141 } else if (name == "android.permission.WRITE_CONTACTS") {
1142 hasWriteContactsPermission = true;
1143 } else if (name == "android.permission.READ_CALL_LOG") {
1144 hasReadCallLogPermission = true;
1145 } else if (name == "android.permission.WRITE_CALL_LOG") {
1146 hasWriteCallLogPermission = true;
Dianne Hackborne5276a72009-08-27 16:28:44 -07001147 }
1148 printf("uses-permission:'%s'\n", name.string());
Nick Kralevich1bcc3d62013-04-03 09:00:02 -07001149 int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
1150 if (!req) {
1151 printf("optional-permission:'%s'\n", name.string());
1152 }
Dianne Hackborne5276a72009-08-27 16:28:44 -07001153 } else {
1154 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1155 error.string());
1156 goto bail;
1157 }
Dianne Hackborn43b68032010-09-02 17:14:41 -07001158 } else if (tag == "uses-package") {
1159 String8 name = getAttribute(tree, NAME_ATTR, &error);
1160 if (name != "" && error == "") {
1161 printf("uses-package:'%s'\n", name.string());
1162 } else {
1163 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1164 error.string());
1165 goto bail;
1166 }
Jeff Hamiltone2c17f92010-02-12 13:45:16 -06001167 } else if (tag == "original-package") {
1168 String8 name = getAttribute(tree, NAME_ATTR, &error);
1169 if (name != "" && error == "") {
1170 printf("original-package:'%s'\n", name.string());
1171 } else {
1172 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1173 error.string());
1174 goto bail;
1175 }
Dan Morrill096b67f2010-12-13 16:25:54 -08001176 } else if (tag == "supports-gl-texture") {
Dan Morrill6f51fc12010-10-13 14:33:43 -07001177 String8 name = getAttribute(tree, NAME_ATTR, &error);
1178 if (name != "" && error == "") {
Dan Morrill096b67f2010-12-13 16:25:54 -08001179 printf("supports-gl-texture:'%s'\n", name.string());
Dan Morrill6f51fc12010-10-13 14:33:43 -07001180 } else {
1181 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1182 error.string());
1183 goto bail;
1184 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001185 } else if (tag == "compatible-screens") {
1186 printCompatibleScreens(tree);
1187 depth--;
Kenny Root56088a52011-09-29 13:49:45 -07001188 } else if (tag == "package-verifier") {
1189 String8 name = getAttribute(tree, NAME_ATTR, &error);
1190 if (name != "" && error == "") {
1191 String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
1192 if (publicKey != "" && error == "") {
1193 printf("package-verifier: name='%s' publicKey='%s'\n",
1194 name.string(), publicKey.string());
1195 }
1196 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001197 }
Michael Wrighteaeb1902013-09-05 18:15:57 -07001198 } else if (depth == 3) {
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001199 withinActivity = false;
1200 withinReceiver = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001201 withinService = false;
1202 hasIntentFilter = false;
Adam Lesinskid831e802013-09-27 11:14:57 -07001203 hasMetaHostPaymentCategory = false;
1204 hasMetaOffHostPaymentCategory = false;
1205 hasBindDeviceAdminPermission = false;
1206 hasBindInputMethodPermission = false;
1207 hasBindAccessibilityServicePermission = false;
1208 hasBindPrintServicePermission = false;
1209 hasBindNfcServicePermission = false;
Michael Wrighteaeb1902013-09-05 18:15:57 -07001210 if (withinApplication) {
1211 if(tag == "activity") {
1212 withinActivity = true;
1213 activityName = getAttribute(tree, NAME_ATTR, &error);
1214 if (error != "") {
1215 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1216 error.string());
1217 goto bail;
1218 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001219
Michael Wrighteaeb1902013-09-05 18:15:57 -07001220 activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1221 if (error != "") {
1222 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
1223 error.string());
1224 goto bail;
1225 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001226
Michael Wrighteaeb1902013-09-05 18:15:57 -07001227 activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1228 if (error != "") {
1229 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1230 error.string());
1231 goto bail;
1232 }
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001233
Michael Wrighteaeb1902013-09-05 18:15:57 -07001234 int32_t orien = getResolvedIntegerAttribute(&res, tree,
1235 SCREEN_ORIENTATION_ATTR, &error);
1236 if (error == "") {
1237 if (orien == 0 || orien == 6 || orien == 8) {
1238 // Requests landscape, sensorLandscape, or reverseLandscape.
1239 reqScreenLandscapeFeature = true;
1240 } else if (orien == 1 || orien == 7 || orien == 9) {
1241 // Requests portrait, sensorPortrait, or reversePortrait.
1242 reqScreenPortraitFeature = true;
1243 }
1244 }
1245 } else if (tag == "uses-library") {
1246 String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1247 if (error != "") {
1248 fprintf(stderr,
1249 "ERROR getting 'android:name' attribute for uses-library"
1250 " %s\n", error.string());
1251 goto bail;
1252 }
1253 int req = getIntegerAttribute(tree,
1254 REQUIRED_ATTR, NULL, 1);
1255 printf("uses-library%s:'%s'\n",
1256 req ? "" : "-not-required", libraryName.string());
1257 } else if (tag == "receiver") {
1258 withinReceiver = true;
1259 receiverName = getAttribute(tree, NAME_ATTR, &error);
1260
1261 if (error != "") {
1262 fprintf(stderr,
1263 "ERROR getting 'android:name' attribute for receiver:"
1264 " %s\n", error.string());
1265 goto bail;
1266 }
Adam Lesinskib1249b82013-09-25 17:03:50 -07001267
1268 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1269 if (error == "") {
1270 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1271 hasBindDeviceAdminPermission = true;
1272 }
1273 } else {
1274 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1275 " receiver '%s': %s\n", receiverName.string(), error.string());
1276 }
Michael Wrighteaeb1902013-09-05 18:15:57 -07001277 } else if (tag == "service") {
1278 withinService = true;
1279 serviceName = getAttribute(tree, NAME_ATTR, &error);
1280
1281 if (error != "") {
1282 fprintf(stderr, "ERROR getting 'android:name' attribute for"
1283 " service: %s\n", error.string());
1284 goto bail;
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001285 }
Adam Lesinskib1249b82013-09-25 17:03:50 -07001286
1287 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1288 if (error == "") {
1289 if (permission == "android.permission.BIND_INPUT_METHOD") {
1290 hasBindInputMethodPermission = true;
1291 } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") {
1292 hasBindAccessibilityServicePermission = true;
1293 } else if (permission == "android.permission.BIND_PRINT_SERVICE") {
1294 hasBindPrintServicePermission = true;
Adam Lesinskid831e802013-09-27 11:14:57 -07001295 } else if (permission == "android.permission.BIND_NFC_SERVICE") {
1296 hasBindNfcServicePermission = true;
Adam Lesinskib1249b82013-09-25 17:03:50 -07001297 }
1298 } else {
1299 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1300 " service '%s': %s\n", serviceName.string(), error.string());
1301 }
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001302 }
Michael Wrighteaeb1902013-09-05 18:15:57 -07001303 } else if (withinSupportsInput && tag == "input-type") {
1304 String8 name = getAttribute(tree, NAME_ATTR, &error);
1305 if (name != "" && error == "") {
1306 supportedInput.add(name);
1307 } else {
1308 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1309 error.string());
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001310 goto bail;
1311 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001312 }
Adam Lesinskid831e802013-09-27 11:14:57 -07001313 } else if (depth == 4) {
1314 if (tag == "intent-filter") {
1315 hasIntentFilter = true;
1316 withinIntentFilter = true;
1317 actMainActivity = false;
1318 actWidgetReceivers = false;
1319 actImeService = false;
1320 actWallpaperService = false;
1321 actAccessibilityService = false;
1322 actPrintService = false;
1323 actDeviceAdminEnabled = false;
1324 actHostApduService = false;
1325 actOffHostApduService = false;
1326 } else if (withinService && tag == "meta-data") {
1327 String8 name = getAttribute(tree, NAME_ATTR, &error);
1328 if (error != "") {
1329 fprintf(stderr, "ERROR getting 'android:name' attribute for"
1330 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1331 goto bail;
1332 }
1333
1334 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1335 name == "android.nfc.cardemulation.off_host_apdu_service") {
1336 bool offHost = true;
1337 if (name == "android.nfc.cardemulation.host_apdu_service") {
1338 offHost = false;
1339 }
1340
1341 String8 xmlPath = getResolvedAttribute(&res, tree, RESOURCE_ATTR, &error);
1342 if (error != "") {
1343 fprintf(stderr, "ERROR getting 'android:resource' attribute for"
1344 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1345 goto bail;
1346 }
1347
1348 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1349 offHost, &error);
1350 if (error != "") {
1351 fprintf(stderr, "ERROR getting AID category for service '%s'\n",
1352 serviceName.string());
1353 goto bail;
1354 }
1355
1356 const size_t catLen = categories.size();
1357 for (size_t i = 0; i < catLen; i++) {
1358 bool paymentCategory = (categories[i] == "payment");
1359 if (offHost) {
1360 hasMetaOffHostPaymentCategory |= paymentCategory;
1361 } else {
1362 hasMetaHostPaymentCategory |= paymentCategory;
1363 }
1364 }
1365 }
1366 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001367 } else if ((depth == 5) && withinIntentFilter){
1368 String8 action;
1369 if (tag == "action") {
1370 action = getAttribute(tree, NAME_ATTR, &error);
1371 if (error != "") {
1372 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1373 goto bail;
1374 }
1375 if (withinActivity) {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001376 if (action == "android.intent.action.MAIN") {
1377 isMainActivity = true;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001378 actMainActivity = true;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001379 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001380 } else if (withinReceiver) {
1381 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1382 actWidgetReceivers = true;
Adam Lesinskib1249b82013-09-25 17:03:50 -07001383 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1384 actDeviceAdminEnabled = true;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001385 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001386 } else if (withinService) {
1387 if (action == "android.view.InputMethod") {
1388 actImeService = true;
1389 } else if (action == "android.service.wallpaper.WallpaperService") {
1390 actWallpaperService = true;
Adam Lesinskib1249b82013-09-25 17:03:50 -07001391 } else if (action == "android.accessibilityservice.AccessibilityService") {
1392 actAccessibilityService = true;
1393 } else if (action == "android.printservice.PrintService") {
1394 actPrintService = true;
Adam Lesinskid831e802013-09-27 11:14:57 -07001395 } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
1396 actHostApduService = true;
1397 } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
1398 actOffHostApduService = true;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001399 }
1400 }
1401 if (action == "android.intent.action.SEARCH") {
1402 isSearchable = true;
1403 }
1404 }
1405
1406 if (tag == "category") {
1407 String8 category = getAttribute(tree, NAME_ATTR, &error);
1408 if (error != "") {
1409 fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string());
1410 goto bail;
1411 }
1412 if (withinActivity) {
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001413 if (category == "android.intent.category.LAUNCHER") {
1414 isLauncherActivity = true;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001415 }
1416 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001417 }
1418 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001419 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001420
Kenny Root063a44e2011-12-08 08:46:03 -08001421 // Pre-1.6 implicitly granted permission compatibility logic
1422 if (targetSdk < 4) {
1423 if (!hasWriteExternalStoragePermission) {
1424 printf("uses-permission:'android.permission.WRITE_EXTERNAL_STORAGE'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001425 printf("uses-implied-permission:'android.permission.WRITE_EXTERNAL_STORAGE'," \
1426 "'targetSdkVersion < 4'\n");
Dianne Hackborn79245122012-03-12 10:51:26 -07001427 hasWriteExternalStoragePermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001428 }
1429 if (!hasReadPhoneStatePermission) {
1430 printf("uses-permission:'android.permission.READ_PHONE_STATE'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001431 printf("uses-implied-permission:'android.permission.READ_PHONE_STATE'," \
1432 "'targetSdkVersion < 4'\n");
Kenny Root063a44e2011-12-08 08:46:03 -08001433 }
1434 }
1435
Dianne Hackborn79245122012-03-12 10:51:26 -07001436 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
Dianne Hackborn2bd8d042012-06-11 12:27:05 -07001437 // force them to always take READ_EXTERNAL_STORAGE as well. We always
1438 // do this (regardless of target API version) because we can't have
1439 // an app with write permission but not read permission.
Dianne Hackborn79245122012-03-12 10:51:26 -07001440 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
1441 printf("uses-permission:'android.permission.READ_EXTERNAL_STORAGE'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001442 printf("uses-implied-permission:'android.permission.READ_EXTERNAL_STORAGE'," \
1443 "'requested WRITE_EXTERNAL_STORAGE'\n");
Dianne Hackborn79245122012-03-12 10:51:26 -07001444 }
1445
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001446 // Pre-JellyBean call log permission compatibility.
1447 if (targetSdk < 16) {
1448 if (!hasReadCallLogPermission && hasReadContactsPermission) {
1449 printf("uses-permission:'android.permission.READ_CALL_LOG'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001450 printf("uses-implied-permission:'android.permission.READ_CALL_LOG'," \
1451 "'targetSdkVersion < 16 and requested READ_CONTACTS'\n");
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001452 }
1453 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
1454 printf("uses-permission:'android.permission.WRITE_CALL_LOG'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001455 printf("uses-implied-permission:'android.permission.WRITE_CALL_LOG'," \
1456 "'targetSdkVersion < 16 and requested WRITE_CONTACTS'\n");
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001457 }
1458 }
1459
Dan Morrill89d97c12010-05-03 16:13:14 -07001460 /* The following blocks handle printing "inferred" uses-features, based
1461 * on whether related features or permissions are used by the app.
1462 * Note that the various spec*Feature variables denote whether the
1463 * relevant tag was *present* in the AndroidManfest, not that it was
1464 * present and set to true.
1465 */
1466 // Camera-related back-compatibility logic
1467 if (!specCameraFeature) {
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001468 if (reqCameraFlashFeature) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001469 // if app requested a sub-feature (autofocus or flash) and didn't
1470 // request the base camera feature, we infer that it meant to
1471 printf("uses-feature:'android.hardware.camera'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001472 printf("uses-implied-feature:'android.hardware.camera'," \
1473 "'requested android.hardware.camera.flash feature'\n");
1474 } else if (reqCameraAutofocusFeature) {
1475 // if app requested a sub-feature (autofocus or flash) and didn't
1476 // request the base camera feature, we infer that it meant to
1477 printf("uses-feature:'android.hardware.camera'\n");
1478 printf("uses-implied-feature:'android.hardware.camera'," \
1479 "'requested android.hardware.camera.autofocus feature'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001480 } else if (hasCameraPermission) {
1481 // if app wants to use camera but didn't request the feature, we infer
1482 // that it meant to, and further that it wants autofocus
1483 // (which was the 1.0 - 1.5 behavior)
1484 printf("uses-feature:'android.hardware.camera'\n");
1485 if (!specCameraAutofocusFeature) {
1486 printf("uses-feature:'android.hardware.camera.autofocus'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001487 printf("uses-implied-feature:'android.hardware.camera.autofocus'," \
1488 "'requested android.permission.CAMERA permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001489 }
1490 }
Dianne Hackborne5276a72009-08-27 16:28:44 -07001491 }
Doug Zongkerdbe7a682009-10-09 11:24:51 -07001492
Dan Morrill89d97c12010-05-03 16:13:14 -07001493 // Location-related back-compatibility logic
1494 if (!specLocationFeature &&
1495 (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission ||
1496 hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) {
1497 // if app either takes a location-related permission or requests one of the
1498 // sub-features, we infer that it also meant to request the base location feature
1499 printf("uses-feature:'android.hardware.location'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001500 printf("uses-implied-feature:'android.hardware.location'," \
1501 "'requested a location access permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001502 }
Dianne Hackbornef05e072010-03-01 17:43:39 -08001503 if (!specGpsFeature && hasGpsPermission) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001504 // if app takes GPS (FINE location) perm but does not request the GPS
1505 // feature, we infer that it meant to
Dianne Hackbornef05e072010-03-01 17:43:39 -08001506 printf("uses-feature:'android.hardware.location.gps'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001507 printf("uses-implied-feature:'android.hardware.location.gps'," \
1508 "'requested android.permission.ACCESS_FINE_LOCATION permission'\n");
Dianne Hackbornef05e072010-03-01 17:43:39 -08001509 }
Dan Morrill89d97c12010-05-03 16:13:14 -07001510 if (!specNetworkLocFeature && hasCoarseLocPermission) {
1511 // if app takes Network location (COARSE location) perm but does not request the
1512 // network location feature, we infer that it meant to
1513 printf("uses-feature:'android.hardware.location.network'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001514 printf("uses-implied-feature:'android.hardware.location.network'," \
Dianne Hackbornec559ff2013-02-25 15:42:07 -08001515 "'requested android.permission.ACCESS_COARSE_LOCATION permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001516 }
1517
1518 // Bluetooth-related compatibility logic
Dan Morrill6b22d812010-06-15 21:41:42 -07001519 if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001520 // if app takes a Bluetooth permission but does not request the Bluetooth
1521 // feature, we infer that it meant to
1522 printf("uses-feature:'android.hardware.bluetooth'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001523 printf("uses-implied-feature:'android.hardware.bluetooth'," \
1524 "'requested android.permission.BLUETOOTH or android.permission.BLUETOOTH_ADMIN " \
1525 "permission and targetSdkVersion > 4'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001526 }
1527
1528 // Microphone-related compatibility logic
1529 if (!specMicrophoneFeature && hasRecordAudioPermission) {
1530 // if app takes the record-audio permission but does not request the microphone
1531 // feature, we infer that it meant to
1532 printf("uses-feature:'android.hardware.microphone'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001533 printf("uses-implied-feature:'android.hardware.microphone'," \
1534 "'requested android.permission.RECORD_AUDIO permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001535 }
1536
1537 // WiFi-related compatibility logic
1538 if (!specWiFiFeature && hasWiFiPermission) {
1539 // if app takes one of the WiFi permissions but does not request the WiFi
1540 // feature, we infer that it meant to
1541 printf("uses-feature:'android.hardware.wifi'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001542 printf("uses-implied-feature:'android.hardware.wifi'," \
1543 "'requested android.permission.ACCESS_WIFI_STATE, " \
1544 "android.permission.CHANGE_WIFI_STATE, or " \
1545 "android.permission.CHANGE_WIFI_MULTICAST_STATE permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001546 }
1547
1548 // Telephony-related compatibility logic
1549 if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) {
1550 // if app takes one of the telephony permissions or requests a sub-feature but
1551 // does not request the base telephony feature, we infer that it meant to
1552 printf("uses-feature:'android.hardware.telephony'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001553 printf("uses-implied-feature:'android.hardware.telephony'," \
1554 "'requested a telephony-related permission or feature'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001555 }
1556
1557 // Touchscreen-related back-compatibility logic
1558 if (!specTouchscreenFeature) { // not a typo!
1559 // all apps are presumed to require a touchscreen, unless they explicitly say
1560 // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
1561 // Note that specTouchscreenFeature is true if the tag is present, regardless
1562 // of whether its value is true or false, so this is safe
1563 printf("uses-feature:'android.hardware.touchscreen'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001564 printf("uses-implied-feature:'android.hardware.touchscreen'," \
1565 "'assumed you require a touch screen unless explicitly made optional'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001566 }
1567 if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
1568 // if app takes one of the telephony permissions or requests a sub-feature but
1569 // does not request the base telephony feature, we infer that it meant to
1570 printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001571 printf("uses-implied-feature:'android.hardware.touchscreen.multitouch'," \
1572 "'requested android.hardware.touchscreen.multitouch.distinct feature'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001573 }
Dianne Hackbornef05e072010-03-01 17:43:39 -08001574
Dianne Hackborne289bff2011-06-13 19:33:22 -07001575 // Landscape/portrait-related compatibility logic
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001576 if (!specScreenLandscapeFeature && !specScreenPortraitFeature) {
1577 // If the app has specified any activities in its manifest
1578 // that request a specific orientation, then assume that
1579 // orientation is required.
1580 if (reqScreenLandscapeFeature) {
1581 printf("uses-feature:'android.hardware.screen.landscape'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001582 printf("uses-implied-feature:'android.hardware.screen.landscape'," \
1583 "'one or more activities have specified a landscape orientation'\n");
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001584 }
1585 if (reqScreenPortraitFeature) {
1586 printf("uses-feature:'android.hardware.screen.portrait'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001587 printf("uses-implied-feature:'android.hardware.screen.portrait'," \
1588 "'one or more activities have specified a portrait orientation'\n");
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001589 }
Dianne Hackborne289bff2011-06-13 19:33:22 -07001590 }
1591
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001592 if (hasMainActivity) {
1593 printf("main\n");
1594 }
1595 if (hasWidgetReceivers) {
1596 printf("app-widget\n");
1597 }
Adam Lesinskib1249b82013-09-25 17:03:50 -07001598 if (hasDeviceAdminReceiver) {
1599 printf("device-admin\n");
1600 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001601 if (hasImeService) {
1602 printf("ime\n");
1603 }
1604 if (hasWallpaperService) {
1605 printf("wallpaper\n");
1606 }
Adam Lesinskib1249b82013-09-25 17:03:50 -07001607 if (hasAccessibilityService) {
1608 printf("accessibility\n");
1609 }
1610 if (hasPrintService) {
1611 printf("print\n");
1612 }
Adam Lesinskid831e802013-09-27 11:14:57 -07001613 if (hasPaymentService) {
1614 printf("payment\n");
1615 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001616 if (hasOtherActivities) {
1617 printf("other-activities\n");
1618 }
1619 if (isSearchable) {
1620 printf("search\n");
1621 }
1622 if (hasOtherReceivers) {
1623 printf("other-receivers\n");
1624 }
1625 if (hasOtherServices) {
1626 printf("other-services\n");
1627 }
1628
Dianne Hackborne289bff2011-06-13 19:33:22 -07001629 // For modern apps, if screen size buckets haven't been specified
1630 // but the new width ranges have, then infer the buckets from them.
1631 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1632 && requiresSmallestWidthDp > 0) {
1633 int compatWidth = compatibleWidthLimitDp;
1634 if (compatWidth <= 0) compatWidth = requiresSmallestWidthDp;
1635 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1636 smallScreen = -1;
1637 } else {
1638 smallScreen = 0;
1639 }
1640 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1641 normalScreen = -1;
1642 } else {
1643 normalScreen = 0;
1644 }
1645 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1646 largeScreen = -1;
1647 } else {
1648 largeScreen = 0;
1649 }
1650 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1651 xlargeScreen = -1;
1652 } else {
1653 xlargeScreen = 0;
1654 }
1655 }
1656
Dianne Hackborn723738c2009-06-25 19:48:04 -07001657 // Determine default values for any unspecified screen sizes,
1658 // based on the target SDK of the package. As of 4 (donut)
1659 // the screen size support was introduced, so all default to
1660 // enabled.
1661 if (smallScreen > 0) {
1662 smallScreen = targetSdk >= 4 ? -1 : 0;
1663 }
1664 if (normalScreen > 0) {
1665 normalScreen = -1;
1666 }
1667 if (largeScreen > 0) {
1668 largeScreen = targetSdk >= 4 ? -1 : 0;
1669 }
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001670 if (xlargeScreen > 0) {
Scott Maind58fb972010-11-04 18:32:00 -07001671 // Introduced in Gingerbread.
1672 xlargeScreen = targetSdk >= 9 ? -1 : 0;
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001673 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001674 if (anyDensity > 0) {
Dianne Hackborne289bff2011-06-13 19:33:22 -07001675 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1676 || compatibleWidthLimitDp > 0) ? -1 : 0;
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001677 }
Dianne Hackborn723738c2009-06-25 19:48:04 -07001678 printf("supports-screens:");
1679 if (smallScreen != 0) printf(" 'small'");
1680 if (normalScreen != 0) printf(" 'normal'");
1681 if (largeScreen != 0) printf(" 'large'");
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001682 if (xlargeScreen != 0) printf(" 'xlarge'");
Dianne Hackborn723738c2009-06-25 19:48:04 -07001683 printf("\n");
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001684 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
Dianne Hackborne289bff2011-06-13 19:33:22 -07001685 if (requiresSmallestWidthDp > 0) {
1686 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1687 }
1688 if (compatibleWidthLimitDp > 0) {
1689 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1690 }
1691 if (largestWidthLimitDp > 0) {
1692 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1693 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001694
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001695 printf("locales:");
Dianne Hackborne17086b2009-06-19 15:13:28 -07001696 const size_t NL = locales.size();
1697 for (size_t i=0; i<NL; i++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001698 const char* localeStr = locales[i].string();
1699 if (localeStr == NULL || strlen(localeStr) == 0) {
1700 localeStr = "--_--";
1701 }
1702 printf(" '%s'", localeStr);
1703 }
1704 printf("\n");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001705
Dianne Hackborne17086b2009-06-19 15:13:28 -07001706 printf("densities:");
1707 const size_t ND = densities.size();
1708 for (size_t i=0; i<ND; i++) {
1709 printf(" '%d'", densities[i]);
1710 }
1711 printf("\n");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001712
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001713 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1714 if (dir != NULL) {
1715 if (dir->getFileCount() > 0) {
1716 printf("native-code:");
1717 for (size_t i=0; i<dir->getFileCount(); i++) {
1718 printf(" '%s'", dir->getFileName(i).string());
1719 }
1720 printf("\n");
1721 }
1722 delete dir;
1723 }
Dan Morrille74763e2012-01-06 10:47:10 -08001724 } else if (strcmp("badger", option) == 0) {
Dianne Hackborn6c997a92012-01-31 11:27:43 -08001725 printf("%s", CONSOLE_DATA);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001726 } else if (strcmp("configurations", option) == 0) {
1727 Vector<ResTable_config> configs;
1728 res.getConfigurations(&configs);
1729 const size_t N = configs.size();
1730 for (size_t i=0; i<N; i++) {
1731 printf("%s\n", configs[i].toString().string());
1732 }
1733 } else {
1734 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
1735 goto bail;
1736 }
1737 }
1738
1739 result = NO_ERROR;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001740
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001741bail:
1742 if (asset) {
1743 delete asset;
1744 }
1745 return (result != NO_ERROR);
1746}
1747
1748
1749/*
1750 * Handle the "add" command, which wants to add files to a new or
1751 * pre-existing archive.
1752 */
1753int doAdd(Bundle* bundle)
1754{
1755 ZipFile* zip = NULL;
1756 status_t result = UNKNOWN_ERROR;
1757 const char* zipFileName;
1758
1759 if (bundle->getUpdate()) {
1760 /* avoid confusion */
1761 fprintf(stderr, "ERROR: can't use '-u' with add\n");
1762 goto bail;
1763 }
1764
1765 if (bundle->getFileSpecCount() < 1) {
1766 fprintf(stderr, "ERROR: must specify zip file name\n");
1767 goto bail;
1768 }
1769 zipFileName = bundle->getFileSpecEntry(0);
1770
1771 if (bundle->getFileSpecCount() < 2) {
1772 fprintf(stderr, "NOTE: nothing to do\n");
1773 goto bail;
1774 }
1775
1776 zip = openReadWrite(zipFileName, true);
1777 if (zip == NULL) {
1778 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
1779 goto bail;
1780 }
1781
1782 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1783 const char* fileName = bundle->getFileSpecEntry(i);
1784
1785 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
1786 printf(" '%s'... (from gzip)\n", fileName);
1787 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
1788 } else {
Doug Zongkerdbe7a682009-10-09 11:24:51 -07001789 if (bundle->getJunkPath()) {
1790 String8 storageName = String8(fileName).getPathLeaf();
1791 printf(" '%s' as '%s'...\n", fileName, storageName.string());
1792 result = zip->add(fileName, storageName.string(),
1793 bundle->getCompressionMethod(), NULL);
1794 } else {
1795 printf(" '%s'...\n", fileName);
1796 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
1797 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001798 }
1799 if (result != NO_ERROR) {
1800 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
1801 if (result == NAME_NOT_FOUND)
1802 fprintf(stderr, ": file not found\n");
1803 else if (result == ALREADY_EXISTS)
1804 fprintf(stderr, ": already exists in archive\n");
1805 else
1806 fprintf(stderr, "\n");
1807 goto bail;
1808 }
1809 }
1810
1811 result = NO_ERROR;
1812
1813bail:
1814 delete zip;
1815 return (result != NO_ERROR);
1816}
1817
1818
1819/*
1820 * Delete files from an existing archive.
1821 */
1822int doRemove(Bundle* bundle)
1823{
1824 ZipFile* zip = NULL;
1825 status_t result = UNKNOWN_ERROR;
1826 const char* zipFileName;
1827
1828 if (bundle->getFileSpecCount() < 1) {
1829 fprintf(stderr, "ERROR: must specify zip file name\n");
1830 goto bail;
1831 }
1832 zipFileName = bundle->getFileSpecEntry(0);
1833
1834 if (bundle->getFileSpecCount() < 2) {
1835 fprintf(stderr, "NOTE: nothing to do\n");
1836 goto bail;
1837 }
1838
1839 zip = openReadWrite(zipFileName, false);
1840 if (zip == NULL) {
1841 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
1842 zipFileName);
1843 goto bail;
1844 }
1845
1846 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1847 const char* fileName = bundle->getFileSpecEntry(i);
1848 ZipEntry* entry;
1849
1850 entry = zip->getEntryByName(fileName);
1851 if (entry == NULL) {
1852 printf(" '%s' NOT FOUND\n", fileName);
1853 continue;
1854 }
1855
1856 result = zip->remove(entry);
1857
1858 if (result != NO_ERROR) {
1859 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
1860 bundle->getFileSpecEntry(i), zipFileName);
1861 goto bail;
1862 }
1863 }
1864
1865 /* update the archive */
1866 zip->flush();
1867
1868bail:
1869 delete zip;
1870 return (result != NO_ERROR);
1871}
1872
1873
1874/*
1875 * Package up an asset directory and associated application files.
1876 */
1877int doPackage(Bundle* bundle)
1878{
1879 const char* outputAPKFile;
1880 int retVal = 1;
1881 status_t err;
1882 sp<AaptAssets> assets;
1883 int N;
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001884 FILE* fp;
1885 String8 dependencyFile;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001886
1887 // -c zz_ZZ means do pseudolocalization
1888 ResourceFilter filter;
1889 err = filter.parse(bundle->getConfigurations());
1890 if (err != NO_ERROR) {
1891 goto bail;
1892 }
1893 if (filter.containsPseudo()) {
1894 bundle->setPseudolocalize(true);
1895 }
1896
1897 N = bundle->getFileSpecCount();
1898 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
1899 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDir() == NULL) {
1900 fprintf(stderr, "ERROR: no input files\n");
1901 goto bail;
1902 }
1903
1904 outputAPKFile = bundle->getOutputAPKFile();
1905
1906 // Make sure the filenames provided exist and are of the appropriate type.
1907 if (outputAPKFile) {
1908 FileType type;
1909 type = getFileType(outputAPKFile);
1910 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
1911 fprintf(stderr,
1912 "ERROR: output file '%s' exists but is not regular file\n",
1913 outputAPKFile);
1914 goto bail;
1915 }
1916 }
1917
1918 // Load the assets.
1919 assets = new AaptAssets();
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001920
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001921 // Set up the resource gathering in assets if we're going to generate
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001922 // dependency files. Every time we encounter a resource while slurping
1923 // the tree, we'll add it to these stores so we have full resource paths
1924 // to write to a dependency file.
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001925 if (bundle->getGenDependencies()) {
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001926 sp<FilePathStore> resPathStore = new FilePathStore;
1927 assets->setFullResPaths(resPathStore);
1928 sp<FilePathStore> assetPathStore = new FilePathStore;
1929 assets->setFullAssetPaths(assetPathStore);
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001930 }
1931
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001932 err = assets->slurpFromArgs(bundle);
1933 if (err < 0) {
1934 goto bail;
1935 }
1936
1937 if (bundle->getVerbose()) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001938 assets->print(String8());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001939 }
1940
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001941 // If they asked for any fileAs that need to be compiled, do so.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001942 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
1943 err = buildResources(bundle, assets);
1944 if (err != 0) {
1945 goto bail;
1946 }
1947 }
1948
1949 // At this point we've read everything and processed everything. From here
1950 // on out it's just writing output files.
1951 if (SourcePos::hasErrors()) {
1952 goto bail;
1953 }
1954
Dianne Hackborn1644c6d72012-02-06 15:33:21 -08001955 // Update symbols with information about which ones are needed as Java symbols.
1956 assets->applyJavaSymbols();
1957 if (SourcePos::hasErrors()) {
1958 goto bail;
1959 }
1960
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001961 // If we've been asked to generate a dependency file, do that here
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001962 if (bundle->getGenDependencies()) {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001963 // If this is the packaging step, generate the dependency file next to
1964 // the output apk (e.g. bin/resources.ap_.d)
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001965 if (outputAPKFile) {
1966 dependencyFile = String8(outputAPKFile);
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001967 // Add the .d extension to the dependency file.
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001968 dependencyFile.append(".d");
1969 } else {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001970 // Else if this is the R.java dependency generation step,
1971 // generate the dependency file in the R.java package subdirectory
1972 // e.g. gen/com/foo/app/R.java.d
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001973 dependencyFile = String8(bundle->getRClassDir());
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001974 dependencyFile.appendPath("R.java.d");
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001975 }
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001976 // Make sure we have a clean dependency file to start with
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001977 fp = fopen(dependencyFile, "w");
1978 fclose(fp);
1979 }
1980
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001981 // Write out R.java constants
Dianne Hackborn1644c6d72012-02-06 15:33:21 -08001982 if (!assets->havePrivateSymbols()) {
Xavier Ducrohet63459ad2009-11-30 18:05:10 -08001983 if (bundle->getCustomPackage() == NULL) {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001984 // Write the R.java file into the appropriate class directory
1985 // e.g. gen/com/foo/app/R.java
Xavier Ducrohet63459ad2009-11-30 18:05:10 -08001986 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
1987 } else {
1988 const String8 customPkg(bundle->getCustomPackage());
1989 err = writeResourceSymbols(bundle, assets, customPkg, true);
1990 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001991 if (err < 0) {
1992 goto bail;
1993 }
Ying Wang002f5372012-04-25 18:53:49 -07001994 // If we have library files, we're going to write our R.java file into
1995 // the appropriate class directory for those libraries as well.
1996 // e.g. gen/com/foo/app/lib/R.java
1997 if (bundle->getExtraPackages() != NULL) {
1998 // Split on colon
1999 String8 libs(bundle->getExtraPackages());
2000 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2001 while (packageString != NULL) {
2002 // Write the R.java file out with the correct package name
2003 err = writeResourceSymbols(bundle, assets, String8(packageString), true);
2004 if (err < 0) {
2005 goto bail;
2006 }
2007 packageString = strtok(NULL, ":");
2008 }
2009 libs.unlockBuffer();
2010 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002011 } else {
2012 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
2013 if (err < 0) {
2014 goto bail;
2015 }
2016 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
2017 if (err < 0) {
2018 goto bail;
2019 }
2020 }
2021
Joe Onorato1553c822009-08-30 13:36:22 -07002022 // Write out the ProGuard file
2023 err = writeProguardFile(bundle, assets);
2024 if (err < 0) {
2025 goto bail;
2026 }
2027
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002028 // Write the apk
2029 if (outputAPKFile) {
2030 err = writeAPK(bundle, assets, String8(outputAPKFile));
2031 if (err != NO_ERROR) {
2032 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
2033 goto bail;
2034 }
2035 }
2036
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07002037 // If we've been asked to generate a dependency file, we need to finish up here.
2038 // the writeResourceSymbols and writeAPK functions have already written the target
2039 // half of the dependency file, now we need to write the prerequisites. (files that
2040 // the R.java file or .ap_ file depend on)
Josiah Gaskin03589cc2011-06-27 16:26:02 -07002041 if (bundle->getGenDependencies()) {
2042 // Now that writeResourceSymbols or writeAPK has taken care of writing
2043 // the targets to our dependency file, we'll write the prereqs
2044 fp = fopen(dependencyFile, "a+");
2045 fprintf(fp, " : ");
2046 bool includeRaw = (outputAPKFile != NULL);
2047 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07002048 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2049 // and therefore was not added to our pathstores during slurping
Josiah Gaskin03589cc2011-06-27 16:26:02 -07002050 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2051 fclose(fp);
2052 }
2053
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002054 retVal = 0;
2055bail:
2056 if (SourcePos::hasErrors()) {
2057 SourcePos::printErrors(stderr);
2058 }
2059 return retVal;
2060}
Josiah Gaskin8a39da82011-06-06 17:00:35 -07002061
2062/*
2063 * Do PNG Crunching
2064 * PRECONDITIONS
2065 * -S flag points to a source directory containing drawable* folders
2066 * -C flag points to destination directory. The folder structure in the
2067 * source directory will be mirrored to the destination (cache) directory
2068 *
2069 * POSTCONDITIONS
2070 * Destination directory will be updated to match the PNG files in
2071 * the source directory.
2072 */
2073int doCrunch(Bundle* bundle)
2074{
2075 fprintf(stdout, "Crunching PNG Files in ");
2076 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2077 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2078
2079 updatePreProcessedCache(bundle);
2080
2081 return NO_ERROR;
2082}
Dan Morrille74763e2012-01-06 10:47:10 -08002083
Xavier Ducrohetb1f6ad82012-12-21 09:54:02 -08002084/*
2085 * Do PNG Crunching on a single flag
2086 * -i points to a single png file
2087 * -o points to a single png output file
2088 */
2089int doSingleCrunch(Bundle* bundle)
2090{
2091 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2092 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2093
2094 String8 input(bundle->getSingleCrunchInputFile());
2095 String8 output(bundle->getSingleCrunchOutputFile());
Xavier Ducrohetb7de2192013-01-15 16:41:29 -08002096
Xavier Ducrohetc75d3f52013-01-14 18:02:40 -08002097 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2098 // we can't return the status_t as it gets truncate to the lower 8 bits.
2099 return 42;
2100 }
Xavier Ducrohetb7de2192013-01-15 16:41:29 -08002101
Xavier Ducrohetc75d3f52013-01-14 18:02:40 -08002102 return NO_ERROR;
Xavier Ducrohetb1f6ad82012-12-21 09:54:02 -08002103}
2104
Dan Morrille74763e2012-01-06 10:47:10 -08002105char CONSOLE_DATA[2925] = {
2106 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2107 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2108 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2109 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2110 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2111 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2112 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2113 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2114 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2115 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2116 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2117 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2118 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2119 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2120 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2121 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2122 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2123 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2124 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2125 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2126 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2127 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2128 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2129 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2130 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2131 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2132 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2133 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2134 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2135 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2136 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2137 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2138 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2139 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2140 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2141 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2142 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2143 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2144 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2145 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2146 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2147 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2148 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2149 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2150 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2151 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2152 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2153 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2154 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2155 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2156 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2157 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2158 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2159 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2160 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2161 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2162 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2163 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2164 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2165 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2166 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2167 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2168 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2169 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2170 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2171 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2172 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2173 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2174 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2175 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2176 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2177 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2178 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2179 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2180 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2181 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2182 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2183 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2184 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2185 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2186 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2187 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2188 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2189 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2190 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2191 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2192 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2193 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2194 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2195 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2196 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2197 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2198 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2199 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2200 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2201 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2202 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2203 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2204 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2205 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2206 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2207 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2208 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2209 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2210 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2211 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2212 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2213 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2214 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2215 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2216 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2217 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2218 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2219 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2220 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2221 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2222 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2223 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2224 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2225 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2226 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2227 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2228 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2229 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2230 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2231 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2232 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2233 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2234 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2235 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2236 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2237 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2238 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2239 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2240 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2241 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2242 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2243 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2244 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2245 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2246 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2247 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2248 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2249 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2250 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2251 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2252 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2253 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2254 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2255 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2256 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2257 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2258 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2259 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2260 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2261 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2262 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2263 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2264 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2265 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2266 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2267 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2268 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2269 };