blob: 7e7d3465bbbc967c2bfa230949c58446cb98b6c0 [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
Andreas Gampeb8dc7bc2014-10-01 19:07:51 -0700199#ifdef HAVE_ANDROID_OS
200 static const bool kHaveAndroidOs = true;
201#else
202 static const bool kHaveAndroidOs = false;
Steve Blockf1ff21a2010-06-14 17:34:04 +0100203#endif
Andreas Gampeb8dc7bc2014-10-01 19:07:51 -0700204 const ResTable& res = assets.getResources(false);
205 if (!kHaveAndroidOs) {
206 printf("\nResource table:\n");
207 res.print(false);
208 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700209
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800210 Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
211 Asset::ACCESS_BUFFER);
212 if (manifestAsset == NULL) {
213 printf("\nNo AndroidManifest.xml found.\n");
214 } else {
215 printf("\nAndroid manifest:\n");
216 ResXMLTree tree;
217 tree.setTo(manifestAsset->getBuffer(true),
218 manifestAsset->getLength());
219 printXMLBlock(&tree);
220 }
221 delete manifestAsset;
222 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700223
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800224 result = 0;
225
226bail:
227 delete zip;
228 return result;
229}
230
231static ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes)
232{
233 size_t N = tree.getAttributeCount();
234 for (size_t i=0; i<N; i++) {
235 if (tree.getAttributeNameResID(i) == attrRes) {
236 return (ssize_t)i;
237 }
238 }
239 return -1;
240}
241
Joe Onorato1553c822009-08-30 13:36:22 -0700242String8 getAttribute(const ResXMLTree& tree, const char* ns,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800243 const char* attr, String8* outError)
244{
245 ssize_t idx = tree.indexOfAttribute(ns, attr);
246 if (idx < 0) {
247 return String8();
248 }
249 Res_value value;
250 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
251 if (value.dataType != Res_value::TYPE_STRING) {
252 if (outError != NULL) *outError = "attribute is not a string value";
253 return String8();
254 }
255 }
256 size_t len;
Dan Albertf348c152014-09-08 18:28:00 -0700257 const char16_t* str = tree.getAttributeStringValue(idx, &len);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800258 return str ? String8(str, len) : String8();
259}
260
261static String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError)
262{
263 ssize_t idx = indexOfAttribute(tree, attrRes);
264 if (idx < 0) {
265 return String8();
266 }
267 Res_value value;
268 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
269 if (value.dataType != Res_value::TYPE_STRING) {
270 if (outError != NULL) *outError = "attribute is not a string value";
271 return String8();
272 }
273 }
274 size_t len;
Dan Albertf348c152014-09-08 18:28:00 -0700275 const char16_t* str = tree.getAttributeStringValue(idx, &len);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800276 return str ? String8(str, len) : String8();
277}
278
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700279static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes,
280 String8* outError, int32_t defValue = -1)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800281{
282 ssize_t idx = indexOfAttribute(tree, attrRes);
283 if (idx < 0) {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700284 return defValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800285 }
286 Res_value value;
287 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700288 if (value.dataType < Res_value::TYPE_FIRST_INT
289 || value.dataType > Res_value::TYPE_LAST_INT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800290 if (outError != NULL) *outError = "attribute is not an integer value";
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700291 return defValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800292 }
293 }
294 return value.data;
295}
296
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -0700297static int32_t getResolvedIntegerAttribute(const ResTable* resTable, const ResXMLTree& tree,
298 uint32_t attrRes, String8* outError, int32_t defValue = -1)
299{
300 ssize_t idx = indexOfAttribute(tree, attrRes);
301 if (idx < 0) {
302 return defValue;
303 }
304 Res_value value;
305 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
306 if (value.dataType == Res_value::TYPE_REFERENCE) {
307 resTable->resolveReference(&value, 0);
308 }
309 if (value.dataType < Res_value::TYPE_FIRST_INT
310 || value.dataType > Res_value::TYPE_LAST_INT) {
311 if (outError != NULL) *outError = "attribute is not an integer value";
312 return defValue;
313 }
314 }
315 return value.data;
316}
317
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800318static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree,
319 uint32_t attrRes, String8* outError)
320{
321 ssize_t idx = indexOfAttribute(tree, attrRes);
322 if (idx < 0) {
323 return String8();
324 }
325 Res_value value;
326 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
327 if (value.dataType == Res_value::TYPE_STRING) {
328 size_t len;
Dan Albertf348c152014-09-08 18:28:00 -0700329 const char16_t* str = tree.getAttributeStringValue(idx, &len);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330 return str ? String8(str, len) : String8();
331 }
332 resTable->resolveReference(&value, 0);
333 if (value.dataType != Res_value::TYPE_STRING) {
334 if (outError != NULL) *outError = "attribute is not a string value";
335 return String8();
336 }
337 }
338 size_t len;
339 const Res_value* value2 = &value;
340 const char16_t* str = const_cast<ResTable*>(resTable)->valueToString(value2, 0, NULL, &len);
341 return str ? String8(str, len) : String8();
342}
343
344// These are attribute resource constants for the platform, as found
345// in android.R.attr
346enum {
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -0700347 LABEL_ATTR = 0x01010001,
348 ICON_ATTR = 0x01010002,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800349 NAME_ATTR = 0x01010003,
Adam Lesinskib1249b82013-09-25 17:03:50 -0700350 PERMISSION_ATTR = 0x01010006,
Adam Lesinskid831e802013-09-27 11:14:57 -0700351 RESOURCE_ATTR = 0x01010025,
Dan Morrillb6ec11e2012-04-03 12:44:40 -0700352 DEBUGGABLE_ATTR = 0x0101000f,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353 VERSION_CODE_ATTR = 0x0101021b,
354 VERSION_NAME_ATTR = 0x0101021c,
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -0700355 SCREEN_ORIENTATION_ATTR = 0x0101001e,
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700356 MIN_SDK_VERSION_ATTR = 0x0101020c,
Suchi Amalapurapu75c49842009-08-14 15:13:09 -0700357 MAX_SDK_VERSION_ATTR = 0x01010271,
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700358 REQ_TOUCH_SCREEN_ATTR = 0x01010227,
359 REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
360 REQ_HARD_KEYBOARD_ATTR = 0x01010229,
361 REQ_NAVIGATION_ATTR = 0x0101022a,
362 REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
363 TARGET_SDK_VERSION_ATTR = 0x01010270,
364 TEST_ONLY_ATTR = 0x01010272,
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700365 ANY_DENSITY_ATTR = 0x0101026c,
Dianne Hackborne5276a72009-08-27 16:28:44 -0700366 GL_ES_VERSION_ATTR = 0x01010281,
Dianne Hackborn723738c2009-06-25 19:48:04 -0700367 SMALL_SCREEN_ATTR = 0x01010284,
368 NORMAL_SCREEN_ATTR = 0x01010285,
369 LARGE_SCREEN_ATTR = 0x01010286,
Dianne Hackbornf43489d2010-08-20 12:44:33 -0700370 XLARGE_SCREEN_ATTR = 0x010102bf,
Dianne Hackborne5276a72009-08-27 16:28:44 -0700371 REQUIRED_ATTR = 0x0101028e,
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700372 SCREEN_SIZE_ATTR = 0x010102ca,
373 SCREEN_DENSITY_ATTR = 0x010102cb,
Dianne Hackborne289bff2011-06-13 19:33:22 -0700374 REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
375 COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
376 LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
Kenny Root56088a52011-09-29 13:49:45 -0700377 PUBLIC_KEY_ATTR = 0x010103a6,
Adam Lesinskid831e802013-09-27 11:14:57 -0700378 CATEGORY_ATTR = 0x010103e8,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800379};
380
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700381const char *getComponentName(String8 &pkgName, String8 &componentName) {
382 ssize_t idx = componentName.find(".");
383 String8 retStr(pkgName);
384 if (idx == 0) {
385 retStr += componentName;
386 } else if (idx < 0) {
387 retStr += ".";
388 retStr += componentName;
389 } else {
390 return componentName.string();
391 }
392 return retStr.string();
393}
394
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700395static void printCompatibleScreens(ResXMLTree& tree) {
396 size_t len;
397 ResXMLTree::event_code_t code;
398 int depth = 0;
399 bool first = true;
400 printf("compatible-screens:");
401 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
402 if (code == ResXMLTree::END_TAG) {
403 depth--;
404 if (depth < 0) {
405 break;
406 }
407 continue;
408 }
409 if (code != ResXMLTree::START_TAG) {
410 continue;
411 }
412 depth++;
413 String8 tag(tree.getElementName(&len));
414 if (tag == "screen") {
415 int32_t screenSize = getIntegerAttribute(tree,
416 SCREEN_SIZE_ATTR, NULL, -1);
417 int32_t screenDensity = getIntegerAttribute(tree,
418 SCREEN_DENSITY_ATTR, NULL, -1);
419 if (screenSize > 0 && screenDensity > 0) {
420 if (!first) {
421 printf(",");
422 }
423 first = false;
424 printf("'%d/%d'", screenSize, screenDensity);
425 }
426 }
427 }
428 printf("\n");
429}
430
Adam Lesinskid831e802013-09-27 11:14:57 -0700431Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool offHost,
432 String8 *outError = NULL)
433{
434 Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER);
435 if (aidAsset == NULL) {
436 if (outError != NULL) *outError = "xml resource does not exist";
437 return Vector<String8>();
438 }
439
440 const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service");
441
442 bool withinApduService = false;
443 Vector<String8> categories;
444
445 String8 error;
446 ResXMLTree tree;
447 tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength());
448
449 size_t len;
450 int depth = 0;
451 ResXMLTree::event_code_t code;
452 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
453 if (code == ResXMLTree::END_TAG) {
454 depth--;
455 String8 tag(tree.getElementName(&len));
456
457 if (depth == 0 && tag == serviceTagName) {
458 withinApduService = false;
459 }
460
461 } else if (code == ResXMLTree::START_TAG) {
462 depth++;
463 String8 tag(tree.getElementName(&len));
464
465 if (depth == 1) {
466 if (tag == serviceTagName) {
467 withinApduService = true;
468 }
469 } else if (depth == 2 && withinApduService) {
470 if (tag == "aid-group") {
471 String8 category = getAttribute(tree, CATEGORY_ATTR, &error);
472 if (error != "") {
473 if (outError != NULL) *outError = error;
474 return Vector<String8>();
475 }
476
477 categories.add(category);
478 }
479 }
480 }
481 }
482 aidAsset->close();
483 return categories;
484}
485
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800486/*
487 * Handle the "dump" command, to extract select data from an archive.
488 */
Dan Morrille74763e2012-01-06 10:47:10 -0800489extern char CONSOLE_DATA[2925]; // see EOF
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800490int doDump(Bundle* bundle)
491{
492 status_t result = UNKNOWN_ERROR;
493 Asset* asset = NULL;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700494
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800495 if (bundle->getFileSpecCount() < 1) {
496 fprintf(stderr, "ERROR: no dump option specified\n");
497 return 1;
498 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700499
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800500 if (bundle->getFileSpecCount() < 2) {
501 fprintf(stderr, "ERROR: no dump file specified\n");
502 return 1;
503 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700504
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800505 const char* option = bundle->getFileSpecEntry(0);
506 const char* filename = bundle->getFileSpecEntry(1);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700507
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800508 AssetManager assets;
Narayan Kamath745d4ef2014-01-27 11:17:22 +0000509 int32_t assetsCookie;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700510 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800511 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
512 return 1;
513 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700514
Dianne Hackborne289bff2011-06-13 19:33:22 -0700515 // Make a dummy config for retrieving resources... we need to supply
516 // non-default values for some configs so that we can retrieve resources
517 // in the app that don't have a default. The most important of these is
518 // the API version because key resources like icons will have an implicit
519 // version if they are using newer config types like density.
520 ResTable_config config;
Narayan Kamath788fa412014-01-21 15:32:36 +0000521 memset(&config, 0, sizeof(ResTable_config));
Dianne Hackborne289bff2011-06-13 19:33:22 -0700522 config.language[0] = 'e';
523 config.language[1] = 'n';
524 config.country[0] = 'U';
525 config.country[1] = 'S';
526 config.orientation = ResTable_config::ORIENTATION_PORT;
527 config.density = ResTable_config::DENSITY_MEDIUM;
528 config.sdkVersion = 10000; // Very high.
529 config.screenWidthDp = 320;
530 config.screenHeightDp = 480;
531 config.smallestScreenWidthDp = 320;
532 assets.setConfiguration(config);
533
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800534 const ResTable& res = assets.getResources(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800535 if (strcmp("resources", option) == 0) {
Steve Blockf1ff21a2010-06-14 17:34:04 +0100536#ifndef HAVE_ANDROID_OS
Dianne Hackborne17086b2009-06-19 15:13:28 -0700537 res.print(bundle->getValues());
Steve Blockf1ff21a2010-06-14 17:34:04 +0100538#endif
Dianne Hackborn6c997a92012-01-31 11:27:43 -0800539
540 } else if (strcmp("strings", option) == 0) {
541 const ResStringPool* pool = res.getTableStringBlock(0);
542 printStringPool(pool);
543
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800544 } else if (strcmp("xmltree", option) == 0) {
545 if (bundle->getFileSpecCount() < 3) {
546 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
547 goto bail;
548 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700549
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800550 for (int i=2; i<bundle->getFileSpecCount(); i++) {
551 const char* resname = bundle->getFileSpecEntry(i);
552 ResXMLTree tree;
553 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
554 if (asset == NULL) {
Kenny Root44b283d2009-09-01 19:03:11 -0500555 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800556 goto bail;
557 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700558
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800559 if (tree.setTo(asset->getBuffer(true),
560 asset->getLength()) != NO_ERROR) {
561 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
562 goto bail;
563 }
564 tree.restart();
565 printXMLBlock(&tree);
Kenny Root19138462009-12-04 09:38:48 -0800566 tree.uninit();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800567 delete asset;
568 asset = NULL;
569 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700570
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800571 } else if (strcmp("xmlstrings", option) == 0) {
572 if (bundle->getFileSpecCount() < 3) {
573 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
574 goto bail;
575 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700576
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800577 for (int i=2; i<bundle->getFileSpecCount(); i++) {
578 const char* resname = bundle->getFileSpecEntry(i);
579 ResXMLTree tree;
580 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
581 if (asset == NULL) {
Kenny Root44b283d2009-09-01 19:03:11 -0500582 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800583 goto bail;
584 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700585
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800586 if (tree.setTo(asset->getBuffer(true),
587 asset->getLength()) != NO_ERROR) {
588 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
589 goto bail;
590 }
591 printStringPool(&tree.getStrings());
592 delete asset;
593 asset = NULL;
594 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700595
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800596 } else {
597 ResXMLTree tree;
598 asset = assets.openNonAsset("AndroidManifest.xml",
599 Asset::ACCESS_BUFFER);
600 if (asset == NULL) {
601 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
602 goto bail;
603 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700604
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800605 if (tree.setTo(asset->getBuffer(true),
606 asset->getLength()) != NO_ERROR) {
607 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
608 goto bail;
609 }
610 tree.restart();
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700611
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800612 if (strcmp("permissions", option) == 0) {
613 size_t len;
614 ResXMLTree::event_code_t code;
615 int depth = 0;
616 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
617 if (code == ResXMLTree::END_TAG) {
618 depth--;
619 continue;
620 }
621 if (code != ResXMLTree::START_TAG) {
622 continue;
623 }
624 depth++;
625 String8 tag(tree.getElementName(&len));
626 //printf("Depth %d tag %s\n", depth, tag.string());
627 if (depth == 1) {
628 if (tag != "manifest") {
629 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
630 goto bail;
631 }
632 String8 pkg = getAttribute(tree, NULL, "package", NULL);
633 printf("package: %s\n", pkg.string());
634 } else if (depth == 2 && tag == "permission") {
635 String8 error;
636 String8 name = getAttribute(tree, NAME_ATTR, &error);
637 if (error != "") {
638 fprintf(stderr, "ERROR: %s\n", error.string());
639 goto bail;
640 }
641 printf("permission: %s\n", name.string());
642 } else if (depth == 2 && tag == "uses-permission") {
643 String8 error;
644 String8 name = getAttribute(tree, NAME_ATTR, &error);
645 if (error != "") {
646 fprintf(stderr, "ERROR: %s\n", error.string());
647 goto bail;
648 }
649 printf("uses-permission: %s\n", name.string());
Nick Kralevich1bcc3d62013-04-03 09:00:02 -0700650 int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
651 if (!req) {
652 printf("optional-permission: %s\n", name.string());
653 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800654 }
655 }
656 } else if (strcmp("badging", option) == 0) {
Dianne Hackborne289bff2011-06-13 19:33:22 -0700657 Vector<String8> locales;
658 res.getLocales(&locales);
659
660 Vector<ResTable_config> configs;
661 res.getConfigurations(&configs);
662 SortedVector<int> densities;
663 const size_t NC = configs.size();
664 for (size_t i=0; i<NC; i++) {
665 int dens = configs[i].density;
666 if (dens == 0) dens = 160;
667 densities.add(dens);
668 }
669
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800670 size_t len;
671 ResXMLTree::event_code_t code;
672 int depth = 0;
673 String8 error;
674 bool withinActivity = false;
675 bool isMainActivity = false;
676 bool isLauncherActivity = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700677 bool isSearchable = false;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700678 bool withinApplication = false;
Michael Wrighteaeb1902013-09-05 18:15:57 -0700679 bool withinSupportsInput = false;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700680 bool withinReceiver = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700681 bool withinService = false;
682 bool withinIntentFilter = false;
683 bool hasMainActivity = false;
684 bool hasOtherActivities = false;
685 bool hasOtherReceivers = false;
686 bool hasOtherServices = false;
687 bool hasWallpaperService = false;
688 bool hasImeService = false;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700689 bool hasAccessibilityService = false;
690 bool hasPrintService = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700691 bool hasWidgetReceivers = false;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700692 bool hasDeviceAdminReceiver = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700693 bool hasIntentFilter = false;
Adam Lesinskid831e802013-09-27 11:14:57 -0700694 bool hasPaymentService = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700695 bool actMainActivity = false;
696 bool actWidgetReceivers = false;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700697 bool actDeviceAdminEnabled = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700698 bool actImeService = false;
699 bool actWallpaperService = false;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700700 bool actAccessibilityService = false;
701 bool actPrintService = false;
Adam Lesinskid831e802013-09-27 11:14:57 -0700702 bool actHostApduService = false;
703 bool actOffHostApduService = false;
704 bool hasMetaHostPaymentCategory = false;
705 bool hasMetaOffHostPaymentCategory = false;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700706
707 // These permissions are required by services implementing services
708 // the system binds to (IME, Accessibility, PrintServices, etc.)
709 bool hasBindDeviceAdminPermission = false;
710 bool hasBindInputMethodPermission = false;
711 bool hasBindAccessibilityServicePermission = false;
712 bool hasBindPrintServicePermission = false;
Adam Lesinskid831e802013-09-27 11:14:57 -0700713 bool hasBindNfcServicePermission = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700714
Kenny Root063a44e2011-12-08 08:46:03 -0800715 // These two implement the implicit permissions that are granted
716 // to pre-1.6 applications.
717 bool hasWriteExternalStoragePermission = false;
718 bool hasReadPhoneStatePermission = false;
719
Dianne Hackborn79245122012-03-12 10:51:26 -0700720 // If an app requests write storage, they will also get read storage.
721 bool hasReadExternalStoragePermission = false;
722
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -0700723 // Implement transition to read and write call log.
724 bool hasReadContactsPermission = false;
725 bool hasWriteContactsPermission = false;
726 bool hasReadCallLogPermission = false;
727 bool hasWriteCallLogPermission = false;
728
Dan Morrill89d97c12010-05-03 16:13:14 -0700729 // This next group of variables is used to implement a group of
730 // backward-compatibility heuristics necessitated by the addition of
731 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
732 // heuristic is "if an app requests a permission but doesn't explicitly
733 // request the corresponding <uses-feature>, presume it's there anyway".
734 bool specCameraFeature = false; // camera-related
735 bool specCameraAutofocusFeature = false;
736 bool reqCameraAutofocusFeature = false;
737 bool reqCameraFlashFeature = false;
Dianne Hackborne5276a72009-08-27 16:28:44 -0700738 bool hasCameraPermission = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700739 bool specLocationFeature = false; // location-related
740 bool specNetworkLocFeature = false;
741 bool reqNetworkLocFeature = false;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800742 bool specGpsFeature = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700743 bool reqGpsFeature = false;
744 bool hasMockLocPermission = false;
745 bool hasCoarseLocPermission = false;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800746 bool hasGpsPermission = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700747 bool hasGeneralLocPermission = false;
748 bool specBluetoothFeature = false; // Bluetooth API-related
749 bool hasBluetoothPermission = false;
750 bool specMicrophoneFeature = false; // microphone-related
751 bool hasRecordAudioPermission = false;
752 bool specWiFiFeature = false;
753 bool hasWiFiPermission = false;
754 bool specTelephonyFeature = false; // telephony-related
755 bool reqTelephonySubFeature = false;
756 bool hasTelephonyPermission = false;
757 bool specTouchscreenFeature = false; // touchscreen-related
758 bool specMultitouchFeature = false;
759 bool reqDistinctMultitouchFeature = false;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700760 bool specScreenPortraitFeature = false;
761 bool specScreenLandscapeFeature = false;
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -0700762 bool reqScreenPortraitFeature = false;
763 bool reqScreenLandscapeFeature = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700764 // 2.2 also added some other features that apps can request, but that
765 // have no corresponding permission, so we cannot implement any
766 // back-compatibility heuristic for them. The below are thus unnecessary
767 // (but are retained here for documentary purposes.)
768 //bool specCompassFeature = false;
769 //bool specAccelerometerFeature = false;
770 //bool specProximityFeature = false;
771 //bool specAmbientLightFeature = false;
772 //bool specLiveWallpaperFeature = false;
773
Dianne Hackborn723738c2009-06-25 19:48:04 -0700774 int targetSdk = 0;
775 int smallScreen = 1;
776 int normalScreen = 1;
777 int largeScreen = 1;
Dianne Hackbornf43489d2010-08-20 12:44:33 -0700778 int xlargeScreen = 1;
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700779 int anyDensity = 1;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700780 int requiresSmallestWidthDp = 0;
781 int compatibleWidthLimitDp = 0;
782 int largestWidthLimitDp = 0;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700783 String8 pkg;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800784 String8 activityName;
785 String8 activityLabel;
786 String8 activityIcon;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700787 String8 receiverName;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700788 String8 serviceName;
Michael Wrighteaeb1902013-09-05 18:15:57 -0700789 Vector<String8> supportedInput;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800790 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
791 if (code == ResXMLTree::END_TAG) {
792 depth--;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700793 if (depth < 2) {
Michael Wrighteaeb1902013-09-05 18:15:57 -0700794 if (withinSupportsInput && !supportedInput.isEmpty()) {
795 printf("supports-input: '");
796 const size_t N = supportedInput.size();
797 for (size_t i=0; i<N; i++) {
798 printf("%s", supportedInput[i].string());
799 if (i != N - 1) {
800 printf("' '");
801 } else {
802 printf("'\n");
803 }
804 }
805 supportedInput.clear();
806 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700807 withinApplication = false;
Michael Wrighteaeb1902013-09-05 18:15:57 -0700808 withinSupportsInput = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700809 } else if (depth < 3) {
810 if (withinActivity && isMainActivity && isLauncherActivity) {
811 const char *aName = getComponentName(pkg, activityName);
Dianne Hackborne289bff2011-06-13 19:33:22 -0700812 printf("launchable-activity:");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700813 if (aName != NULL) {
Dianne Hackborne289bff2011-06-13 19:33:22 -0700814 printf(" name='%s' ", aName);
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700815 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700816 printf(" label='%s' icon='%s'\n",
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700817 activityLabel.string(),
818 activityIcon.string());
819 }
820 if (!hasIntentFilter) {
821 hasOtherActivities |= withinActivity;
822 hasOtherReceivers |= withinReceiver;
823 hasOtherServices |= withinService;
Adam Lesinskid831e802013-09-27 11:14:57 -0700824 } else {
825 if (withinService) {
826 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
827 hasBindNfcServicePermission);
828 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
829 hasBindNfcServicePermission);
830 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700831 }
832 withinActivity = false;
833 withinService = false;
834 withinReceiver = false;
835 hasIntentFilter = false;
836 isMainActivity = isLauncherActivity = false;
837 } else if (depth < 4) {
838 if (withinIntentFilter) {
839 if (withinActivity) {
840 hasMainActivity |= actMainActivity;
841 hasOtherActivities |= !actMainActivity;
842 } else if (withinReceiver) {
843 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700844 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
845 hasBindDeviceAdminPermission);
846 hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled);
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700847 } else if (withinService) {
848 hasImeService |= actImeService;
849 hasWallpaperService |= actWallpaperService;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700850 hasAccessibilityService |= (actAccessibilityService &&
851 hasBindAccessibilityServicePermission);
852 hasPrintService |= (actPrintService && hasBindPrintServicePermission);
853 hasOtherServices |= (!actImeService && !actWallpaperService &&
Adam Lesinskid831e802013-09-27 11:14:57 -0700854 !actAccessibilityService && !actPrintService &&
855 !actHostApduService && !actOffHostApduService);
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700856 }
857 }
858 withinIntentFilter = false;
859 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800860 continue;
861 }
862 if (code != ResXMLTree::START_TAG) {
863 continue;
864 }
865 depth++;
866 String8 tag(tree.getElementName(&len));
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700867 //printf("Depth %d, %s\n", depth, tag.string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800868 if (depth == 1) {
869 if (tag != "manifest") {
870 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
871 goto bail;
872 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700873 pkg = getAttribute(tree, NULL, "package", NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800874 printf("package: name='%s' ", pkg.string());
875 int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
876 if (error != "") {
877 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
878 goto bail;
879 }
880 if (versionCode > 0) {
881 printf("versionCode='%d' ", versionCode);
882 } else {
883 printf("versionCode='' ");
884 }
Dianne Hackborncf244ad2010-03-09 15:00:30 -0800885 String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800886 if (error != "") {
887 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
888 goto bail;
889 }
890 printf("versionName='%s'\n", versionName.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700891 } else if (depth == 2) {
892 withinApplication = false;
893 if (tag == "application") {
894 withinApplication = true;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700895
896 String8 label;
897 const size_t NL = locales.size();
898 for (size_t i=0; i<NL; i++) {
899 const char* localeStr = locales[i].string();
900 assets.setLocale(localeStr != NULL ? localeStr : "");
901 String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
902 if (llabel != "") {
903 if (localeStr == NULL || strlen(localeStr) == 0) {
904 label = llabel;
905 printf("application-label:'%s'\n", llabel.string());
906 } else {
907 if (label == "") {
908 label = llabel;
909 }
910 printf("application-label-%s:'%s'\n", localeStr,
911 llabel.string());
912 }
913 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700914 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700915
916 ResTable_config tmpConfig = config;
917 const size_t ND = densities.size();
918 for (size_t i=0; i<ND; i++) {
919 tmpConfig.density = densities[i];
920 assets.setConfiguration(tmpConfig);
921 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
922 if (icon != "") {
923 printf("application-icon-%d:'%s'\n", densities[i], icon.string());
924 }
925 }
926 assets.setConfiguration(config);
927
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700928 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
929 if (error != "") {
930 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
931 goto bail;
932 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700933 int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700934 if (error != "") {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700935 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700936 goto bail;
937 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700938 printf("application: label='%s' ", label.string());
939 printf("icon='%s'\n", icon.string());
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700940 if (testOnly != 0) {
941 printf("testOnly='%d'\n", testOnly);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700942 }
Dan Morrillb6ec11e2012-04-03 12:44:40 -0700943
944 int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0);
945 if (error != "") {
946 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string());
947 goto bail;
948 }
949 if (debuggable != 0) {
950 printf("application-debuggable\n");
951 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700952 } else if (tag == "uses-sdk") {
953 int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
954 if (error != "") {
955 error = "";
956 String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
957 if (error != "") {
958 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
959 error.string());
960 goto bail;
961 }
Dianne Hackborn723738c2009-06-25 19:48:04 -0700962 if (name == "Donut") targetSdk = 4;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700963 printf("sdkVersion:'%s'\n", name.string());
964 } else if (code != -1) {
Dianne Hackborn723738c2009-06-25 19:48:04 -0700965 targetSdk = code;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700966 printf("sdkVersion:'%d'\n", code);
967 }
Suchi Amalapurapu75c49842009-08-14 15:13:09 -0700968 code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
969 if (code != -1) {
970 printf("maxSdkVersion:'%d'\n", code);
971 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700972 code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
973 if (error != "") {
974 error = "";
975 String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
976 if (error != "") {
977 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
978 error.string());
979 goto bail;
980 }
Dianne Hackborn723738c2009-06-25 19:48:04 -0700981 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700982 printf("targetSdkVersion:'%s'\n", name.string());
983 } else if (code != -1) {
Dianne Hackborn723738c2009-06-25 19:48:04 -0700984 if (targetSdk < code) {
985 targetSdk = code;
986 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700987 printf("targetSdkVersion:'%d'\n", code);
988 }
989 } else if (tag == "uses-configuration") {
990 int32_t reqTouchScreen = getIntegerAttribute(tree,
991 REQ_TOUCH_SCREEN_ATTR, NULL, 0);
992 int32_t reqKeyboardType = getIntegerAttribute(tree,
993 REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
994 int32_t reqHardKeyboard = getIntegerAttribute(tree,
995 REQ_HARD_KEYBOARD_ATTR, NULL, 0);
996 int32_t reqNavigation = getIntegerAttribute(tree,
997 REQ_NAVIGATION_ATTR, NULL, 0);
998 int32_t reqFiveWayNav = getIntegerAttribute(tree,
999 REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
Dianne Hackborncb2d50d2010-01-06 11:29:54 -08001000 printf("uses-configuration:");
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001001 if (reqTouchScreen != 0) {
1002 printf(" reqTouchScreen='%d'", reqTouchScreen);
1003 }
1004 if (reqKeyboardType != 0) {
1005 printf(" reqKeyboardType='%d'", reqKeyboardType);
1006 }
1007 if (reqHardKeyboard != 0) {
1008 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1009 }
1010 if (reqNavigation != 0) {
1011 printf(" reqNavigation='%d'", reqNavigation);
1012 }
1013 if (reqFiveWayNav != 0) {
1014 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1015 }
1016 printf("\n");
Michael Wrighteaeb1902013-09-05 18:15:57 -07001017 } else if (tag == "supports-input") {
1018 withinSupportsInput = true;
Dianne Hackborn723738c2009-06-25 19:48:04 -07001019 } else if (tag == "supports-screens") {
1020 smallScreen = getIntegerAttribute(tree,
1021 SMALL_SCREEN_ATTR, NULL, 1);
1022 normalScreen = getIntegerAttribute(tree,
1023 NORMAL_SCREEN_ATTR, NULL, 1);
1024 largeScreen = getIntegerAttribute(tree,
1025 LARGE_SCREEN_ATTR, NULL, 1);
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001026 xlargeScreen = getIntegerAttribute(tree,
1027 XLARGE_SCREEN_ATTR, NULL, 1);
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001028 anyDensity = getIntegerAttribute(tree,
1029 ANY_DENSITY_ATTR, NULL, 1);
Dianne Hackborne289bff2011-06-13 19:33:22 -07001030 requiresSmallestWidthDp = getIntegerAttribute(tree,
1031 REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
1032 compatibleWidthLimitDp = getIntegerAttribute(tree,
1033 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
1034 largestWidthLimitDp = getIntegerAttribute(tree,
1035 LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
Dianne Hackborne5276a72009-08-27 16:28:44 -07001036 } else if (tag == "uses-feature") {
1037 String8 name = getAttribute(tree, NAME_ATTR, &error);
Suchi Amalapurapu40b94722009-09-20 13:39:37 -07001038
1039 if (name != "" && error == "") {
Dianne Hackborne5276a72009-08-27 16:28:44 -07001040 int req = getIntegerAttribute(tree,
1041 REQUIRED_ATTR, NULL, 1);
Dan Morrill89d97c12010-05-03 16:13:14 -07001042
Dianne Hackborne5276a72009-08-27 16:28:44 -07001043 if (name == "android.hardware.camera") {
1044 specCameraFeature = true;
Dan Morrill89d97c12010-05-03 16:13:14 -07001045 } else if (name == "android.hardware.camera.autofocus") {
1046 // these have no corresponding permission to check for,
1047 // but should imply the foundational camera permission
1048 reqCameraAutofocusFeature = reqCameraAutofocusFeature || req;
1049 specCameraAutofocusFeature = true;
1050 } else if (req && (name == "android.hardware.camera.flash")) {
1051 // these have no corresponding permission to check for,
1052 // but should imply the foundational camera permission
1053 reqCameraFlashFeature = true;
1054 } else if (name == "android.hardware.location") {
1055 specLocationFeature = true;
1056 } else if (name == "android.hardware.location.network") {
1057 specNetworkLocFeature = true;
1058 reqNetworkLocFeature = reqNetworkLocFeature || req;
Dianne Hackbornef05e072010-03-01 17:43:39 -08001059 } else if (name == "android.hardware.location.gps") {
1060 specGpsFeature = true;
Dan Morrill89d97c12010-05-03 16:13:14 -07001061 reqGpsFeature = reqGpsFeature || req;
1062 } else if (name == "android.hardware.bluetooth") {
1063 specBluetoothFeature = true;
1064 } else if (name == "android.hardware.touchscreen") {
1065 specTouchscreenFeature = true;
1066 } else if (name == "android.hardware.touchscreen.multitouch") {
1067 specMultitouchFeature = true;
1068 } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
1069 reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req;
1070 } else if (name == "android.hardware.microphone") {
1071 specMicrophoneFeature = true;
1072 } else if (name == "android.hardware.wifi") {
1073 specWiFiFeature = true;
1074 } else if (name == "android.hardware.telephony") {
1075 specTelephonyFeature = true;
1076 } else if (req && (name == "android.hardware.telephony.gsm" ||
1077 name == "android.hardware.telephony.cdma")) {
1078 // these have no corresponding permission to check for,
1079 // but should imply the foundational telephony permission
1080 reqTelephonySubFeature = true;
Dianne Hackborne289bff2011-06-13 19:33:22 -07001081 } else if (name == "android.hardware.screen.portrait") {
1082 specScreenPortraitFeature = true;
1083 } else if (name == "android.hardware.screen.landscape") {
1084 specScreenLandscapeFeature = true;
Dianne Hackborne5276a72009-08-27 16:28:44 -07001085 }
1086 printf("uses-feature%s:'%s'\n",
1087 req ? "" : "-not-required", name.string());
1088 } else {
1089 int vers = getIntegerAttribute(tree,
1090 GL_ES_VERSION_ATTR, &error);
1091 if (error == "") {
1092 printf("uses-gl-es:'0x%x'\n", vers);
1093 }
1094 }
1095 } else if (tag == "uses-permission") {
1096 String8 name = getAttribute(tree, NAME_ATTR, &error);
Suchi Amalapurapu40b94722009-09-20 13:39:37 -07001097 if (name != "" && error == "") {
Dianne Hackborne5276a72009-08-27 16:28:44 -07001098 if (name == "android.permission.CAMERA") {
1099 hasCameraPermission = true;
Dianne Hackbornef05e072010-03-01 17:43:39 -08001100 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
1101 hasGpsPermission = true;
Dan Morrill89d97c12010-05-03 16:13:14 -07001102 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
1103 hasMockLocPermission = true;
1104 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
1105 hasCoarseLocPermission = true;
1106 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
1107 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
1108 hasGeneralLocPermission = true;
1109 } else if (name == "android.permission.BLUETOOTH" ||
1110 name == "android.permission.BLUETOOTH_ADMIN") {
1111 hasBluetoothPermission = true;
1112 } else if (name == "android.permission.RECORD_AUDIO") {
1113 hasRecordAudioPermission = true;
1114 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1115 name == "android.permission.CHANGE_WIFI_STATE" ||
1116 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
1117 hasWiFiPermission = true;
1118 } else if (name == "android.permission.CALL_PHONE" ||
1119 name == "android.permission.CALL_PRIVILEGED" ||
1120 name == "android.permission.MODIFY_PHONE_STATE" ||
1121 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1122 name == "android.permission.READ_SMS" ||
1123 name == "android.permission.RECEIVE_SMS" ||
1124 name == "android.permission.RECEIVE_MMS" ||
1125 name == "android.permission.RECEIVE_WAP_PUSH" ||
1126 name == "android.permission.SEND_SMS" ||
1127 name == "android.permission.WRITE_APN_SETTINGS" ||
1128 name == "android.permission.WRITE_SMS") {
1129 hasTelephonyPermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001130 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1131 hasWriteExternalStoragePermission = true;
Dianne Hackborn79245122012-03-12 10:51:26 -07001132 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1133 hasReadExternalStoragePermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001134 } else if (name == "android.permission.READ_PHONE_STATE") {
1135 hasReadPhoneStatePermission = true;
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001136 } else if (name == "android.permission.READ_CONTACTS") {
1137 hasReadContactsPermission = true;
1138 } else if (name == "android.permission.WRITE_CONTACTS") {
1139 hasWriteContactsPermission = true;
1140 } else if (name == "android.permission.READ_CALL_LOG") {
1141 hasReadCallLogPermission = true;
1142 } else if (name == "android.permission.WRITE_CALL_LOG") {
1143 hasWriteCallLogPermission = true;
Dianne Hackborne5276a72009-08-27 16:28:44 -07001144 }
1145 printf("uses-permission:'%s'\n", name.string());
Nick Kralevich1bcc3d62013-04-03 09:00:02 -07001146 int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
1147 if (!req) {
1148 printf("optional-permission:'%s'\n", name.string());
1149 }
Dianne Hackborne5276a72009-08-27 16:28:44 -07001150 } else {
1151 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1152 error.string());
1153 goto bail;
1154 }
Dianne Hackborn43b68032010-09-02 17:14:41 -07001155 } else if (tag == "uses-package") {
1156 String8 name = getAttribute(tree, NAME_ATTR, &error);
1157 if (name != "" && error == "") {
1158 printf("uses-package:'%s'\n", name.string());
1159 } else {
1160 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1161 error.string());
1162 goto bail;
1163 }
Jeff Hamiltone2c17f92010-02-12 13:45:16 -06001164 } else if (tag == "original-package") {
1165 String8 name = getAttribute(tree, NAME_ATTR, &error);
1166 if (name != "" && error == "") {
1167 printf("original-package:'%s'\n", name.string());
1168 } else {
1169 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1170 error.string());
1171 goto bail;
1172 }
Dan Morrill096b67f2010-12-13 16:25:54 -08001173 } else if (tag == "supports-gl-texture") {
Dan Morrill6f51fc12010-10-13 14:33:43 -07001174 String8 name = getAttribute(tree, NAME_ATTR, &error);
1175 if (name != "" && error == "") {
Dan Morrill096b67f2010-12-13 16:25:54 -08001176 printf("supports-gl-texture:'%s'\n", name.string());
Dan Morrill6f51fc12010-10-13 14:33:43 -07001177 } else {
1178 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1179 error.string());
1180 goto bail;
1181 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001182 } else if (tag == "compatible-screens") {
1183 printCompatibleScreens(tree);
1184 depth--;
Kenny Root56088a52011-09-29 13:49:45 -07001185 } else if (tag == "package-verifier") {
1186 String8 name = getAttribute(tree, NAME_ATTR, &error);
1187 if (name != "" && error == "") {
1188 String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
1189 if (publicKey != "" && error == "") {
1190 printf("package-verifier: name='%s' publicKey='%s'\n",
1191 name.string(), publicKey.string());
1192 }
1193 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001194 }
Michael Wrighteaeb1902013-09-05 18:15:57 -07001195 } else if (depth == 3) {
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001196 withinActivity = false;
1197 withinReceiver = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001198 withinService = false;
1199 hasIntentFilter = false;
Adam Lesinskid831e802013-09-27 11:14:57 -07001200 hasMetaHostPaymentCategory = false;
1201 hasMetaOffHostPaymentCategory = false;
1202 hasBindDeviceAdminPermission = false;
1203 hasBindInputMethodPermission = false;
1204 hasBindAccessibilityServicePermission = false;
1205 hasBindPrintServicePermission = false;
1206 hasBindNfcServicePermission = false;
Michael Wrighteaeb1902013-09-05 18:15:57 -07001207 if (withinApplication) {
1208 if(tag == "activity") {
1209 withinActivity = true;
1210 activityName = getAttribute(tree, NAME_ATTR, &error);
1211 if (error != "") {
1212 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1213 error.string());
1214 goto bail;
1215 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001216
Michael Wrighteaeb1902013-09-05 18:15:57 -07001217 activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1218 if (error != "") {
1219 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
1220 error.string());
1221 goto bail;
1222 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001223
Michael Wrighteaeb1902013-09-05 18:15:57 -07001224 activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1225 if (error != "") {
1226 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1227 error.string());
1228 goto bail;
1229 }
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001230
Michael Wrighteaeb1902013-09-05 18:15:57 -07001231 int32_t orien = getResolvedIntegerAttribute(&res, tree,
1232 SCREEN_ORIENTATION_ATTR, &error);
1233 if (error == "") {
1234 if (orien == 0 || orien == 6 || orien == 8) {
1235 // Requests landscape, sensorLandscape, or reverseLandscape.
1236 reqScreenLandscapeFeature = true;
1237 } else if (orien == 1 || orien == 7 || orien == 9) {
1238 // Requests portrait, sensorPortrait, or reversePortrait.
1239 reqScreenPortraitFeature = true;
1240 }
1241 }
1242 } else if (tag == "uses-library") {
1243 String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1244 if (error != "") {
1245 fprintf(stderr,
1246 "ERROR getting 'android:name' attribute for uses-library"
1247 " %s\n", error.string());
1248 goto bail;
1249 }
1250 int req = getIntegerAttribute(tree,
1251 REQUIRED_ATTR, NULL, 1);
1252 printf("uses-library%s:'%s'\n",
1253 req ? "" : "-not-required", libraryName.string());
1254 } else if (tag == "receiver") {
1255 withinReceiver = true;
1256 receiverName = getAttribute(tree, NAME_ATTR, &error);
1257
1258 if (error != "") {
1259 fprintf(stderr,
1260 "ERROR getting 'android:name' attribute for receiver:"
1261 " %s\n", error.string());
1262 goto bail;
1263 }
Adam Lesinskib1249b82013-09-25 17:03:50 -07001264
1265 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1266 if (error == "") {
1267 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1268 hasBindDeviceAdminPermission = true;
1269 }
1270 } else {
1271 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1272 " receiver '%s': %s\n", receiverName.string(), error.string());
1273 }
Michael Wrighteaeb1902013-09-05 18:15:57 -07001274 } else if (tag == "service") {
1275 withinService = true;
1276 serviceName = getAttribute(tree, NAME_ATTR, &error);
1277
1278 if (error != "") {
1279 fprintf(stderr, "ERROR getting 'android:name' attribute for"
1280 " service: %s\n", error.string());
1281 goto bail;
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001282 }
Adam Lesinskib1249b82013-09-25 17:03:50 -07001283
1284 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1285 if (error == "") {
1286 if (permission == "android.permission.BIND_INPUT_METHOD") {
1287 hasBindInputMethodPermission = true;
1288 } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") {
1289 hasBindAccessibilityServicePermission = true;
1290 } else if (permission == "android.permission.BIND_PRINT_SERVICE") {
1291 hasBindPrintServicePermission = true;
Adam Lesinskid831e802013-09-27 11:14:57 -07001292 } else if (permission == "android.permission.BIND_NFC_SERVICE") {
1293 hasBindNfcServicePermission = true;
Adam Lesinskib1249b82013-09-25 17:03:50 -07001294 }
1295 } else {
1296 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1297 " service '%s': %s\n", serviceName.string(), error.string());
1298 }
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001299 }
Michael Wrighteaeb1902013-09-05 18:15:57 -07001300 } else if (withinSupportsInput && tag == "input-type") {
1301 String8 name = getAttribute(tree, NAME_ATTR, &error);
1302 if (name != "" && error == "") {
1303 supportedInput.add(name);
1304 } else {
1305 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1306 error.string());
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001307 goto bail;
1308 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001309 }
Adam Lesinskid831e802013-09-27 11:14:57 -07001310 } else if (depth == 4) {
1311 if (tag == "intent-filter") {
1312 hasIntentFilter = true;
1313 withinIntentFilter = true;
1314 actMainActivity = false;
1315 actWidgetReceivers = false;
1316 actImeService = false;
1317 actWallpaperService = false;
1318 actAccessibilityService = false;
1319 actPrintService = false;
1320 actDeviceAdminEnabled = false;
1321 actHostApduService = false;
1322 actOffHostApduService = false;
1323 } else if (withinService && tag == "meta-data") {
1324 String8 name = getAttribute(tree, NAME_ATTR, &error);
1325 if (error != "") {
1326 fprintf(stderr, "ERROR getting 'android:name' attribute for"
1327 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1328 goto bail;
1329 }
1330
1331 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1332 name == "android.nfc.cardemulation.off_host_apdu_service") {
1333 bool offHost = true;
1334 if (name == "android.nfc.cardemulation.host_apdu_service") {
1335 offHost = false;
1336 }
1337
1338 String8 xmlPath = getResolvedAttribute(&res, tree, RESOURCE_ATTR, &error);
1339 if (error != "") {
1340 fprintf(stderr, "ERROR getting 'android:resource' attribute for"
1341 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1342 goto bail;
1343 }
1344
1345 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1346 offHost, &error);
1347 if (error != "") {
1348 fprintf(stderr, "ERROR getting AID category for service '%s'\n",
1349 serviceName.string());
1350 goto bail;
1351 }
1352
1353 const size_t catLen = categories.size();
1354 for (size_t i = 0; i < catLen; i++) {
1355 bool paymentCategory = (categories[i] == "payment");
1356 if (offHost) {
1357 hasMetaOffHostPaymentCategory |= paymentCategory;
1358 } else {
1359 hasMetaHostPaymentCategory |= paymentCategory;
1360 }
1361 }
1362 }
1363 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001364 } else if ((depth == 5) && withinIntentFilter){
1365 String8 action;
1366 if (tag == "action") {
1367 action = getAttribute(tree, NAME_ATTR, &error);
1368 if (error != "") {
1369 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1370 goto bail;
1371 }
1372 if (withinActivity) {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001373 if (action == "android.intent.action.MAIN") {
1374 isMainActivity = true;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001375 actMainActivity = true;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001376 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001377 } else if (withinReceiver) {
1378 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1379 actWidgetReceivers = true;
Adam Lesinskib1249b82013-09-25 17:03:50 -07001380 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1381 actDeviceAdminEnabled = true;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001382 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001383 } else if (withinService) {
1384 if (action == "android.view.InputMethod") {
1385 actImeService = true;
1386 } else if (action == "android.service.wallpaper.WallpaperService") {
1387 actWallpaperService = true;
Adam Lesinskib1249b82013-09-25 17:03:50 -07001388 } else if (action == "android.accessibilityservice.AccessibilityService") {
1389 actAccessibilityService = true;
1390 } else if (action == "android.printservice.PrintService") {
1391 actPrintService = true;
Adam Lesinskid831e802013-09-27 11:14:57 -07001392 } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
1393 actHostApduService = true;
1394 } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
1395 actOffHostApduService = true;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001396 }
1397 }
1398 if (action == "android.intent.action.SEARCH") {
1399 isSearchable = true;
1400 }
1401 }
1402
1403 if (tag == "category") {
1404 String8 category = getAttribute(tree, NAME_ATTR, &error);
1405 if (error != "") {
1406 fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string());
1407 goto bail;
1408 }
1409 if (withinActivity) {
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001410 if (category == "android.intent.category.LAUNCHER") {
1411 isLauncherActivity = true;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001412 }
1413 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001414 }
1415 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001416 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001417
Kenny Root063a44e2011-12-08 08:46:03 -08001418 // Pre-1.6 implicitly granted permission compatibility logic
1419 if (targetSdk < 4) {
1420 if (!hasWriteExternalStoragePermission) {
1421 printf("uses-permission:'android.permission.WRITE_EXTERNAL_STORAGE'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001422 printf("uses-implied-permission:'android.permission.WRITE_EXTERNAL_STORAGE'," \
1423 "'targetSdkVersion < 4'\n");
Dianne Hackborn79245122012-03-12 10:51:26 -07001424 hasWriteExternalStoragePermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001425 }
1426 if (!hasReadPhoneStatePermission) {
1427 printf("uses-permission:'android.permission.READ_PHONE_STATE'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001428 printf("uses-implied-permission:'android.permission.READ_PHONE_STATE'," \
1429 "'targetSdkVersion < 4'\n");
Kenny Root063a44e2011-12-08 08:46:03 -08001430 }
1431 }
1432
Dianne Hackborn79245122012-03-12 10:51:26 -07001433 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
Dianne Hackborn2bd8d042012-06-11 12:27:05 -07001434 // force them to always take READ_EXTERNAL_STORAGE as well. We always
1435 // do this (regardless of target API version) because we can't have
1436 // an app with write permission but not read permission.
Dianne Hackborn79245122012-03-12 10:51:26 -07001437 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
1438 printf("uses-permission:'android.permission.READ_EXTERNAL_STORAGE'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001439 printf("uses-implied-permission:'android.permission.READ_EXTERNAL_STORAGE'," \
1440 "'requested WRITE_EXTERNAL_STORAGE'\n");
Dianne Hackborn79245122012-03-12 10:51:26 -07001441 }
1442
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001443 // Pre-JellyBean call log permission compatibility.
1444 if (targetSdk < 16) {
1445 if (!hasReadCallLogPermission && hasReadContactsPermission) {
1446 printf("uses-permission:'android.permission.READ_CALL_LOG'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001447 printf("uses-implied-permission:'android.permission.READ_CALL_LOG'," \
1448 "'targetSdkVersion < 16 and requested READ_CONTACTS'\n");
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001449 }
1450 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
1451 printf("uses-permission:'android.permission.WRITE_CALL_LOG'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001452 printf("uses-implied-permission:'android.permission.WRITE_CALL_LOG'," \
1453 "'targetSdkVersion < 16 and requested WRITE_CONTACTS'\n");
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001454 }
1455 }
1456
Dan Morrill89d97c12010-05-03 16:13:14 -07001457 /* The following blocks handle printing "inferred" uses-features, based
1458 * on whether related features or permissions are used by the app.
1459 * Note that the various spec*Feature variables denote whether the
1460 * relevant tag was *present* in the AndroidManfest, not that it was
1461 * present and set to true.
1462 */
1463 // Camera-related back-compatibility logic
1464 if (!specCameraFeature) {
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001465 if (reqCameraFlashFeature) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001466 // if app requested a sub-feature (autofocus or flash) and didn't
1467 // request the base camera feature, we infer that it meant to
1468 printf("uses-feature:'android.hardware.camera'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001469 printf("uses-implied-feature:'android.hardware.camera'," \
1470 "'requested android.hardware.camera.flash feature'\n");
1471 } else if (reqCameraAutofocusFeature) {
1472 // if app requested a sub-feature (autofocus or flash) and didn't
1473 // request the base camera feature, we infer that it meant to
1474 printf("uses-feature:'android.hardware.camera'\n");
1475 printf("uses-implied-feature:'android.hardware.camera'," \
1476 "'requested android.hardware.camera.autofocus feature'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001477 } else if (hasCameraPermission) {
1478 // if app wants to use camera but didn't request the feature, we infer
1479 // that it meant to, and further that it wants autofocus
1480 // (which was the 1.0 - 1.5 behavior)
1481 printf("uses-feature:'android.hardware.camera'\n");
1482 if (!specCameraAutofocusFeature) {
1483 printf("uses-feature:'android.hardware.camera.autofocus'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001484 printf("uses-implied-feature:'android.hardware.camera.autofocus'," \
1485 "'requested android.permission.CAMERA permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001486 }
1487 }
Dianne Hackborne5276a72009-08-27 16:28:44 -07001488 }
Doug Zongkerdbe7a682009-10-09 11:24:51 -07001489
Dan Morrill89d97c12010-05-03 16:13:14 -07001490 // Location-related back-compatibility logic
1491 if (!specLocationFeature &&
1492 (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission ||
1493 hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) {
1494 // if app either takes a location-related permission or requests one of the
1495 // sub-features, we infer that it also meant to request the base location feature
1496 printf("uses-feature:'android.hardware.location'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001497 printf("uses-implied-feature:'android.hardware.location'," \
1498 "'requested a location access permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001499 }
Dianne Hackbornef05e072010-03-01 17:43:39 -08001500 if (!specGpsFeature && hasGpsPermission) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001501 // if app takes GPS (FINE location) perm but does not request the GPS
1502 // feature, we infer that it meant to
Dianne Hackbornef05e072010-03-01 17:43:39 -08001503 printf("uses-feature:'android.hardware.location.gps'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001504 printf("uses-implied-feature:'android.hardware.location.gps'," \
1505 "'requested android.permission.ACCESS_FINE_LOCATION permission'\n");
Dianne Hackbornef05e072010-03-01 17:43:39 -08001506 }
Dan Morrill89d97c12010-05-03 16:13:14 -07001507 if (!specNetworkLocFeature && hasCoarseLocPermission) {
1508 // if app takes Network location (COARSE location) perm but does not request the
1509 // network location feature, we infer that it meant to
1510 printf("uses-feature:'android.hardware.location.network'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001511 printf("uses-implied-feature:'android.hardware.location.network'," \
Dianne Hackbornec559ff2013-02-25 15:42:07 -08001512 "'requested android.permission.ACCESS_COARSE_LOCATION permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001513 }
1514
1515 // Bluetooth-related compatibility logic
Dan Morrill6b22d812010-06-15 21:41:42 -07001516 if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001517 // if app takes a Bluetooth permission but does not request the Bluetooth
1518 // feature, we infer that it meant to
1519 printf("uses-feature:'android.hardware.bluetooth'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001520 printf("uses-implied-feature:'android.hardware.bluetooth'," \
1521 "'requested android.permission.BLUETOOTH or android.permission.BLUETOOTH_ADMIN " \
1522 "permission and targetSdkVersion > 4'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001523 }
1524
1525 // Microphone-related compatibility logic
1526 if (!specMicrophoneFeature && hasRecordAudioPermission) {
1527 // if app takes the record-audio permission but does not request the microphone
1528 // feature, we infer that it meant to
1529 printf("uses-feature:'android.hardware.microphone'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001530 printf("uses-implied-feature:'android.hardware.microphone'," \
1531 "'requested android.permission.RECORD_AUDIO permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001532 }
1533
1534 // WiFi-related compatibility logic
1535 if (!specWiFiFeature && hasWiFiPermission) {
1536 // if app takes one of the WiFi permissions but does not request the WiFi
1537 // feature, we infer that it meant to
1538 printf("uses-feature:'android.hardware.wifi'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001539 printf("uses-implied-feature:'android.hardware.wifi'," \
1540 "'requested android.permission.ACCESS_WIFI_STATE, " \
1541 "android.permission.CHANGE_WIFI_STATE, or " \
1542 "android.permission.CHANGE_WIFI_MULTICAST_STATE permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001543 }
1544
1545 // Telephony-related compatibility logic
1546 if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) {
1547 // if app takes one of the telephony permissions or requests a sub-feature but
1548 // does not request the base telephony feature, we infer that it meant to
1549 printf("uses-feature:'android.hardware.telephony'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001550 printf("uses-implied-feature:'android.hardware.telephony'," \
1551 "'requested a telephony-related permission or feature'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001552 }
1553
1554 // Touchscreen-related back-compatibility logic
1555 if (!specTouchscreenFeature) { // not a typo!
1556 // all apps are presumed to require a touchscreen, unless they explicitly say
1557 // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
1558 // Note that specTouchscreenFeature is true if the tag is present, regardless
1559 // of whether its value is true or false, so this is safe
1560 printf("uses-feature:'android.hardware.touchscreen'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001561 printf("uses-implied-feature:'android.hardware.touchscreen'," \
1562 "'assumed you require a touch screen unless explicitly made optional'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001563 }
1564 if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
1565 // if app takes one of the telephony permissions or requests a sub-feature but
1566 // does not request the base telephony feature, we infer that it meant to
1567 printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001568 printf("uses-implied-feature:'android.hardware.touchscreen.multitouch'," \
1569 "'requested android.hardware.touchscreen.multitouch.distinct feature'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001570 }
Dianne Hackbornef05e072010-03-01 17:43:39 -08001571
Dianne Hackborne289bff2011-06-13 19:33:22 -07001572 // Landscape/portrait-related compatibility logic
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001573 if (!specScreenLandscapeFeature && !specScreenPortraitFeature) {
1574 // If the app has specified any activities in its manifest
1575 // that request a specific orientation, then assume that
1576 // orientation is required.
1577 if (reqScreenLandscapeFeature) {
1578 printf("uses-feature:'android.hardware.screen.landscape'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001579 printf("uses-implied-feature:'android.hardware.screen.landscape'," \
1580 "'one or more activities have specified a landscape orientation'\n");
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001581 }
1582 if (reqScreenPortraitFeature) {
1583 printf("uses-feature:'android.hardware.screen.portrait'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001584 printf("uses-implied-feature:'android.hardware.screen.portrait'," \
1585 "'one or more activities have specified a portrait orientation'\n");
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001586 }
Dianne Hackborne289bff2011-06-13 19:33:22 -07001587 }
1588
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001589 if (hasMainActivity) {
1590 printf("main\n");
1591 }
1592 if (hasWidgetReceivers) {
1593 printf("app-widget\n");
1594 }
Adam Lesinskib1249b82013-09-25 17:03:50 -07001595 if (hasDeviceAdminReceiver) {
1596 printf("device-admin\n");
1597 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001598 if (hasImeService) {
1599 printf("ime\n");
1600 }
1601 if (hasWallpaperService) {
1602 printf("wallpaper\n");
1603 }
Adam Lesinskib1249b82013-09-25 17:03:50 -07001604 if (hasAccessibilityService) {
1605 printf("accessibility\n");
1606 }
1607 if (hasPrintService) {
1608 printf("print\n");
1609 }
Adam Lesinskid831e802013-09-27 11:14:57 -07001610 if (hasPaymentService) {
1611 printf("payment\n");
1612 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001613 if (hasOtherActivities) {
1614 printf("other-activities\n");
1615 }
1616 if (isSearchable) {
1617 printf("search\n");
1618 }
1619 if (hasOtherReceivers) {
1620 printf("other-receivers\n");
1621 }
1622 if (hasOtherServices) {
1623 printf("other-services\n");
1624 }
1625
Dianne Hackborne289bff2011-06-13 19:33:22 -07001626 // For modern apps, if screen size buckets haven't been specified
1627 // but the new width ranges have, then infer the buckets from them.
1628 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1629 && requiresSmallestWidthDp > 0) {
1630 int compatWidth = compatibleWidthLimitDp;
1631 if (compatWidth <= 0) compatWidth = requiresSmallestWidthDp;
1632 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1633 smallScreen = -1;
1634 } else {
1635 smallScreen = 0;
1636 }
1637 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1638 normalScreen = -1;
1639 } else {
1640 normalScreen = 0;
1641 }
1642 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1643 largeScreen = -1;
1644 } else {
1645 largeScreen = 0;
1646 }
1647 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1648 xlargeScreen = -1;
1649 } else {
1650 xlargeScreen = 0;
1651 }
1652 }
1653
Dianne Hackborn723738c2009-06-25 19:48:04 -07001654 // Determine default values for any unspecified screen sizes,
1655 // based on the target SDK of the package. As of 4 (donut)
1656 // the screen size support was introduced, so all default to
1657 // enabled.
1658 if (smallScreen > 0) {
1659 smallScreen = targetSdk >= 4 ? -1 : 0;
1660 }
1661 if (normalScreen > 0) {
1662 normalScreen = -1;
1663 }
1664 if (largeScreen > 0) {
1665 largeScreen = targetSdk >= 4 ? -1 : 0;
1666 }
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001667 if (xlargeScreen > 0) {
Scott Maind58fb972010-11-04 18:32:00 -07001668 // Introduced in Gingerbread.
1669 xlargeScreen = targetSdk >= 9 ? -1 : 0;
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001670 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001671 if (anyDensity > 0) {
Dianne Hackborne289bff2011-06-13 19:33:22 -07001672 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1673 || compatibleWidthLimitDp > 0) ? -1 : 0;
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001674 }
Dianne Hackborn723738c2009-06-25 19:48:04 -07001675 printf("supports-screens:");
1676 if (smallScreen != 0) printf(" 'small'");
1677 if (normalScreen != 0) printf(" 'normal'");
1678 if (largeScreen != 0) printf(" 'large'");
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001679 if (xlargeScreen != 0) printf(" 'xlarge'");
Dianne Hackborn723738c2009-06-25 19:48:04 -07001680 printf("\n");
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001681 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
Dianne Hackborne289bff2011-06-13 19:33:22 -07001682 if (requiresSmallestWidthDp > 0) {
1683 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1684 }
1685 if (compatibleWidthLimitDp > 0) {
1686 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1687 }
1688 if (largestWidthLimitDp > 0) {
1689 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1690 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001691
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001692 printf("locales:");
Dianne Hackborne17086b2009-06-19 15:13:28 -07001693 const size_t NL = locales.size();
1694 for (size_t i=0; i<NL; i++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001695 const char* localeStr = locales[i].string();
1696 if (localeStr == NULL || strlen(localeStr) == 0) {
1697 localeStr = "--_--";
1698 }
1699 printf(" '%s'", localeStr);
1700 }
1701 printf("\n");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001702
Dianne Hackborne17086b2009-06-19 15:13:28 -07001703 printf("densities:");
1704 const size_t ND = densities.size();
1705 for (size_t i=0; i<ND; i++) {
1706 printf(" '%d'", densities[i]);
1707 }
1708 printf("\n");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001709
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001710 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1711 if (dir != NULL) {
1712 if (dir->getFileCount() > 0) {
1713 printf("native-code:");
1714 for (size_t i=0; i<dir->getFileCount(); i++) {
1715 printf(" '%s'", dir->getFileName(i).string());
1716 }
1717 printf("\n");
1718 }
1719 delete dir;
1720 }
Dan Morrille74763e2012-01-06 10:47:10 -08001721 } else if (strcmp("badger", option) == 0) {
Dianne Hackborn6c997a92012-01-31 11:27:43 -08001722 printf("%s", CONSOLE_DATA);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001723 } else if (strcmp("configurations", option) == 0) {
1724 Vector<ResTable_config> configs;
1725 res.getConfigurations(&configs);
1726 const size_t N = configs.size();
1727 for (size_t i=0; i<N; i++) {
1728 printf("%s\n", configs[i].toString().string());
1729 }
1730 } else {
1731 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
1732 goto bail;
1733 }
1734 }
1735
1736 result = NO_ERROR;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001737
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001738bail:
1739 if (asset) {
1740 delete asset;
1741 }
1742 return (result != NO_ERROR);
1743}
1744
1745
1746/*
1747 * Handle the "add" command, which wants to add files to a new or
1748 * pre-existing archive.
1749 */
1750int doAdd(Bundle* bundle)
1751{
1752 ZipFile* zip = NULL;
1753 status_t result = UNKNOWN_ERROR;
1754 const char* zipFileName;
1755
1756 if (bundle->getUpdate()) {
1757 /* avoid confusion */
1758 fprintf(stderr, "ERROR: can't use '-u' with add\n");
1759 goto bail;
1760 }
1761
1762 if (bundle->getFileSpecCount() < 1) {
1763 fprintf(stderr, "ERROR: must specify zip file name\n");
1764 goto bail;
1765 }
1766 zipFileName = bundle->getFileSpecEntry(0);
1767
1768 if (bundle->getFileSpecCount() < 2) {
1769 fprintf(stderr, "NOTE: nothing to do\n");
1770 goto bail;
1771 }
1772
1773 zip = openReadWrite(zipFileName, true);
1774 if (zip == NULL) {
1775 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
1776 goto bail;
1777 }
1778
1779 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1780 const char* fileName = bundle->getFileSpecEntry(i);
1781
1782 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
1783 printf(" '%s'... (from gzip)\n", fileName);
1784 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
1785 } else {
Doug Zongkerdbe7a682009-10-09 11:24:51 -07001786 if (bundle->getJunkPath()) {
1787 String8 storageName = String8(fileName).getPathLeaf();
1788 printf(" '%s' as '%s'...\n", fileName, storageName.string());
1789 result = zip->add(fileName, storageName.string(),
1790 bundle->getCompressionMethod(), NULL);
1791 } else {
1792 printf(" '%s'...\n", fileName);
1793 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
1794 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001795 }
1796 if (result != NO_ERROR) {
1797 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
1798 if (result == NAME_NOT_FOUND)
1799 fprintf(stderr, ": file not found\n");
1800 else if (result == ALREADY_EXISTS)
1801 fprintf(stderr, ": already exists in archive\n");
1802 else
1803 fprintf(stderr, "\n");
1804 goto bail;
1805 }
1806 }
1807
1808 result = NO_ERROR;
1809
1810bail:
1811 delete zip;
1812 return (result != NO_ERROR);
1813}
1814
1815
1816/*
1817 * Delete files from an existing archive.
1818 */
1819int doRemove(Bundle* bundle)
1820{
1821 ZipFile* zip = NULL;
1822 status_t result = UNKNOWN_ERROR;
1823 const char* zipFileName;
1824
1825 if (bundle->getFileSpecCount() < 1) {
1826 fprintf(stderr, "ERROR: must specify zip file name\n");
1827 goto bail;
1828 }
1829 zipFileName = bundle->getFileSpecEntry(0);
1830
1831 if (bundle->getFileSpecCount() < 2) {
1832 fprintf(stderr, "NOTE: nothing to do\n");
1833 goto bail;
1834 }
1835
1836 zip = openReadWrite(zipFileName, false);
1837 if (zip == NULL) {
1838 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
1839 zipFileName);
1840 goto bail;
1841 }
1842
1843 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1844 const char* fileName = bundle->getFileSpecEntry(i);
1845 ZipEntry* entry;
1846
1847 entry = zip->getEntryByName(fileName);
1848 if (entry == NULL) {
1849 printf(" '%s' NOT FOUND\n", fileName);
1850 continue;
1851 }
1852
1853 result = zip->remove(entry);
1854
1855 if (result != NO_ERROR) {
1856 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
1857 bundle->getFileSpecEntry(i), zipFileName);
1858 goto bail;
1859 }
1860 }
1861
1862 /* update the archive */
1863 zip->flush();
1864
1865bail:
1866 delete zip;
1867 return (result != NO_ERROR);
1868}
1869
1870
1871/*
1872 * Package up an asset directory and associated application files.
1873 */
1874int doPackage(Bundle* bundle)
1875{
1876 const char* outputAPKFile;
1877 int retVal = 1;
1878 status_t err;
1879 sp<AaptAssets> assets;
1880 int N;
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001881 FILE* fp;
1882 String8 dependencyFile;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001883
Anton Krumina2ef5c02014-03-12 14:46:44 -07001884 // -c en_XA or/and ar_XB means do pseudolocalization
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001885 ResourceFilter filter;
1886 err = filter.parse(bundle->getConfigurations());
1887 if (err != NO_ERROR) {
1888 goto bail;
1889 }
1890 if (filter.containsPseudo()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07001891 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
1892 }
1893 if (filter.containsPseudoBidi()) {
1894 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001895 }
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 };