blob: 6afe20fb016e3bf31423b78d136014113ee1c589 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001//
2// Copyright 2006 The Android Open Source Project
3//
4// Android Asset Packaging Tool main entry point.
5//
6#include "Main.h"
7#include "Bundle.h"
Dianne Hackborne6b68032011-10-13 16:26:02 -07008#include "ResourceFilter.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08009#include "ResourceTable.h"
Xavier Ducrohetb1f6ad82012-12-21 09:54:02 -080010#include "Images.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080011#include "XMLNode.h"
12
Mathias Agopian3b4062e2009-05-31 19:13:00 -070013#include <utils/Log.h>
14#include <utils/threads.h>
15#include <utils/List.h>
16#include <utils/Errors.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080017
18#include <fcntl.h>
19#include <errno.h>
20
21using namespace android;
22
23/*
24 * Show version info. All the cool kids do it.
25 */
26int doVersion(Bundle* bundle)
27{
28 if (bundle->getFileSpecCount() != 0)
29 printf("(ignoring extra arguments)\n");
30 printf("Android Asset Packaging Tool, v0.2\n");
31
32 return 0;
33}
34
35
36/*
37 * Open the file read only. The call fails if the file doesn't exist.
38 *
39 * Returns NULL on failure.
40 */
41ZipFile* openReadOnly(const char* fileName)
42{
43 ZipFile* zip;
44 status_t result;
45
46 zip = new ZipFile;
47 result = zip->open(fileName, ZipFile::kOpenReadOnly);
48 if (result != NO_ERROR) {
49 if (result == NAME_NOT_FOUND)
50 fprintf(stderr, "ERROR: '%s' not found\n", fileName);
51 else if (result == PERMISSION_DENIED)
52 fprintf(stderr, "ERROR: '%s' access denied\n", fileName);
53 else
54 fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n",
55 fileName);
56 delete zip;
57 return NULL;
58 }
59
60 return zip;
61}
62
63/*
64 * Open the file read-write. The file will be created if it doesn't
65 * already exist and "okayToCreate" is set.
66 *
67 * Returns NULL on failure.
68 */
69ZipFile* openReadWrite(const char* fileName, bool okayToCreate)
70{
71 ZipFile* zip = NULL;
72 status_t result;
73 int flags;
74
75 flags = ZipFile::kOpenReadWrite;
76 if (okayToCreate)
77 flags |= ZipFile::kOpenCreate;
78
79 zip = new ZipFile;
80 result = zip->open(fileName, flags);
81 if (result != NO_ERROR) {
82 delete zip;
83 zip = NULL;
84 goto bail;
85 }
86
87bail:
88 return zip;
89}
90
91
92/*
93 * Return a short string describing the compression method.
94 */
95const char* compressionName(int method)
96{
97 if (method == ZipEntry::kCompressStored)
98 return "Stored";
99 else if (method == ZipEntry::kCompressDeflated)
100 return "Deflated";
101 else
102 return "Unknown";
103}
104
105/*
106 * Return the percent reduction in size (0% == no compression).
107 */
108int calcPercent(long uncompressedLen, long compressedLen)
109{
110 if (!uncompressedLen)
111 return 0;
112 else
113 return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5);
114}
115
116/*
117 * Handle the "list" command, which can be a simple file dump or
118 * a verbose listing.
119 *
120 * The verbose listing closely matches the output of the Info-ZIP "unzip"
121 * command.
122 */
123int doList(Bundle* bundle)
124{
125 int result = 1;
126 ZipFile* zip = NULL;
127 const ZipEntry* entry;
128 long totalUncLen, totalCompLen;
129 const char* zipFileName;
130
131 if (bundle->getFileSpecCount() != 1) {
132 fprintf(stderr, "ERROR: specify zip file name (only)\n");
133 goto bail;
134 }
135 zipFileName = bundle->getFileSpecEntry(0);
136
137 zip = openReadOnly(zipFileName);
138 if (zip == NULL)
139 goto bail;
140
141 int count, i;
142
143 if (bundle->getVerbose()) {
144 printf("Archive: %s\n", zipFileName);
145 printf(
Kenny Rootfb2a9462010-08-25 07:36:31 -0700146 " Length Method Size Ratio Offset Date Time CRC-32 Name\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800147 printf(
Kenny Rootfb2a9462010-08-25 07:36:31 -0700148 "-------- ------ ------- ----- ------- ---- ---- ------ ----\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800149 }
150
151 totalUncLen = totalCompLen = 0;
152
153 count = zip->getNumEntries();
154 for (i = 0; i < count; i++) {
155 entry = zip->getEntryByIndex(i);
156 if (bundle->getVerbose()) {
157 char dateBuf[32];
158 time_t when;
159
160 when = entry->getModWhen();
161 strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M",
162 localtime(&when));
163
Kenny Rootfb2a9462010-08-25 07:36:31 -0700164 printf("%8ld %-7.7s %7ld %3d%% %8zd %s %08lx %s\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800165 (long) entry->getUncompressedLen(),
166 compressionName(entry->getCompressionMethod()),
167 (long) entry->getCompressedLen(),
168 calcPercent(entry->getUncompressedLen(),
169 entry->getCompressedLen()),
Kenny Rootfb2a9462010-08-25 07:36:31 -0700170 (size_t) entry->getLFHOffset(),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800171 dateBuf,
172 entry->getCRC32(),
173 entry->getFileName());
174 } else {
175 printf("%s\n", entry->getFileName());
176 }
177
178 totalUncLen += entry->getUncompressedLen();
179 totalCompLen += entry->getCompressedLen();
180 }
181
182 if (bundle->getVerbose()) {
183 printf(
184 "-------- ------- --- -------\n");
185 printf("%8ld %7ld %2d%% %d files\n",
186 totalUncLen,
187 totalCompLen,
188 calcPercent(totalUncLen, totalCompLen),
189 zip->getNumEntries());
190 }
191
192 if (bundle->getAndroidList()) {
193 AssetManager assets;
194 if (!assets.addAssetPath(String8(zipFileName), NULL)) {
195 fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n");
196 goto bail;
197 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700198
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800199 const ResTable& res = assets.getResources(false);
200 if (&res == NULL) {
201 printf("\nNo resource table found.\n");
202 } else {
Steve Blockf1ff21a2010-06-14 17:34:04 +0100203#ifndef HAVE_ANDROID_OS
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204 printf("\nResource table:\n");
Dianne Hackborne17086b2009-06-19 15:13:28 -0700205 res.print(false);
Steve Blockf1ff21a2010-06-14 17:34:04 +0100206#endif
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800207 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700208
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800209 Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
210 Asset::ACCESS_BUFFER);
211 if (manifestAsset == NULL) {
212 printf("\nNo AndroidManifest.xml found.\n");
213 } else {
214 printf("\nAndroid manifest:\n");
215 ResXMLTree tree;
216 tree.setTo(manifestAsset->getBuffer(true),
217 manifestAsset->getLength());
218 printXMLBlock(&tree);
219 }
220 delete manifestAsset;
221 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700222
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800223 result = 0;
224
225bail:
226 delete zip;
227 return result;
228}
229
230static ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes)
231{
232 size_t N = tree.getAttributeCount();
233 for (size_t i=0; i<N; i++) {
234 if (tree.getAttributeNameResID(i) == attrRes) {
235 return (ssize_t)i;
236 }
237 }
238 return -1;
239}
240
Joe Onorato1553c822009-08-30 13:36:22 -0700241String8 getAttribute(const ResXMLTree& tree, const char* ns,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800242 const char* attr, String8* outError)
243{
244 ssize_t idx = tree.indexOfAttribute(ns, attr);
245 if (idx < 0) {
246 return String8();
247 }
248 Res_value value;
249 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
250 if (value.dataType != Res_value::TYPE_STRING) {
251 if (outError != NULL) *outError = "attribute is not a string value";
252 return String8();
253 }
254 }
255 size_t len;
256 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
257 return str ? String8(str, len) : String8();
258}
259
260static String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError)
261{
262 ssize_t idx = indexOfAttribute(tree, attrRes);
263 if (idx < 0) {
264 return String8();
265 }
266 Res_value value;
267 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
268 if (value.dataType != Res_value::TYPE_STRING) {
269 if (outError != NULL) *outError = "attribute is not a string value";
270 return String8();
271 }
272 }
273 size_t len;
274 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
275 return str ? String8(str, len) : String8();
276}
277
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700278static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes,
279 String8* outError, int32_t defValue = -1)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800280{
281 ssize_t idx = indexOfAttribute(tree, attrRes);
282 if (idx < 0) {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700283 return defValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800284 }
285 Res_value value;
286 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700287 if (value.dataType < Res_value::TYPE_FIRST_INT
288 || value.dataType > Res_value::TYPE_LAST_INT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800289 if (outError != NULL) *outError = "attribute is not an integer value";
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700290 return defValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800291 }
292 }
293 return value.data;
294}
295
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -0700296static int32_t getResolvedIntegerAttribute(const ResTable* resTable, const ResXMLTree& tree,
297 uint32_t attrRes, String8* outError, int32_t defValue = -1)
298{
299 ssize_t idx = indexOfAttribute(tree, attrRes);
300 if (idx < 0) {
301 return defValue;
302 }
303 Res_value value;
304 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
305 if (value.dataType == Res_value::TYPE_REFERENCE) {
306 resTable->resolveReference(&value, 0);
307 }
308 if (value.dataType < Res_value::TYPE_FIRST_INT
309 || value.dataType > Res_value::TYPE_LAST_INT) {
310 if (outError != NULL) *outError = "attribute is not an integer value";
311 return defValue;
312 }
313 }
314 return value.data;
315}
316
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800317static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree,
318 uint32_t attrRes, String8* outError)
319{
320 ssize_t idx = indexOfAttribute(tree, attrRes);
321 if (idx < 0) {
322 return String8();
323 }
324 Res_value value;
325 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
326 if (value.dataType == Res_value::TYPE_STRING) {
327 size_t len;
328 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
329 return str ? String8(str, len) : String8();
330 }
331 resTable->resolveReference(&value, 0);
332 if (value.dataType != Res_value::TYPE_STRING) {
333 if (outError != NULL) *outError = "attribute is not a string value";
334 return String8();
335 }
336 }
337 size_t len;
338 const Res_value* value2 = &value;
339 const char16_t* str = const_cast<ResTable*>(resTable)->valueToString(value2, 0, NULL, &len);
340 return str ? String8(str, len) : String8();
341}
342
343// These are attribute resource constants for the platform, as found
344// in android.R.attr
345enum {
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -0700346 LABEL_ATTR = 0x01010001,
347 ICON_ATTR = 0x01010002,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800348 NAME_ATTR = 0x01010003,
Adam Lesinskib1249b82013-09-25 17:03:50 -0700349 PERMISSION_ATTR = 0x01010006,
Dan Morrillb6ec11e2012-04-03 12:44:40 -0700350 DEBUGGABLE_ATTR = 0x0101000f,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800351 VERSION_CODE_ATTR = 0x0101021b,
352 VERSION_NAME_ATTR = 0x0101021c,
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -0700353 SCREEN_ORIENTATION_ATTR = 0x0101001e,
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700354 MIN_SDK_VERSION_ATTR = 0x0101020c,
Suchi Amalapurapu75c49842009-08-14 15:13:09 -0700355 MAX_SDK_VERSION_ATTR = 0x01010271,
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700356 REQ_TOUCH_SCREEN_ATTR = 0x01010227,
357 REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
358 REQ_HARD_KEYBOARD_ATTR = 0x01010229,
359 REQ_NAVIGATION_ATTR = 0x0101022a,
360 REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
361 TARGET_SDK_VERSION_ATTR = 0x01010270,
362 TEST_ONLY_ATTR = 0x01010272,
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700363 ANY_DENSITY_ATTR = 0x0101026c,
Dianne Hackborne5276a72009-08-27 16:28:44 -0700364 GL_ES_VERSION_ATTR = 0x01010281,
Dianne Hackborn723738c2009-06-25 19:48:04 -0700365 SMALL_SCREEN_ATTR = 0x01010284,
366 NORMAL_SCREEN_ATTR = 0x01010285,
367 LARGE_SCREEN_ATTR = 0x01010286,
Dianne Hackbornf43489d2010-08-20 12:44:33 -0700368 XLARGE_SCREEN_ATTR = 0x010102bf,
Dianne Hackborne5276a72009-08-27 16:28:44 -0700369 REQUIRED_ATTR = 0x0101028e,
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700370 SCREEN_SIZE_ATTR = 0x010102ca,
371 SCREEN_DENSITY_ATTR = 0x010102cb,
Dianne Hackborne289bff2011-06-13 19:33:22 -0700372 REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
373 COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
374 LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
Kenny Root56088a52011-09-29 13:49:45 -0700375 PUBLIC_KEY_ATTR = 0x010103a6,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800376};
377
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700378const char *getComponentName(String8 &pkgName, String8 &componentName) {
379 ssize_t idx = componentName.find(".");
380 String8 retStr(pkgName);
381 if (idx == 0) {
382 retStr += componentName;
383 } else if (idx < 0) {
384 retStr += ".";
385 retStr += componentName;
386 } else {
387 return componentName.string();
388 }
389 return retStr.string();
390}
391
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700392static void printCompatibleScreens(ResXMLTree& tree) {
393 size_t len;
394 ResXMLTree::event_code_t code;
395 int depth = 0;
396 bool first = true;
397 printf("compatible-screens:");
398 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
399 if (code == ResXMLTree::END_TAG) {
400 depth--;
401 if (depth < 0) {
402 break;
403 }
404 continue;
405 }
406 if (code != ResXMLTree::START_TAG) {
407 continue;
408 }
409 depth++;
410 String8 tag(tree.getElementName(&len));
411 if (tag == "screen") {
412 int32_t screenSize = getIntegerAttribute(tree,
413 SCREEN_SIZE_ATTR, NULL, -1);
414 int32_t screenDensity = getIntegerAttribute(tree,
415 SCREEN_DENSITY_ATTR, NULL, -1);
416 if (screenSize > 0 && screenDensity > 0) {
417 if (!first) {
418 printf(",");
419 }
420 first = false;
421 printf("'%d/%d'", screenSize, screenDensity);
422 }
423 }
424 }
425 printf("\n");
426}
427
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800428/*
429 * Handle the "dump" command, to extract select data from an archive.
430 */
Dan Morrille74763e2012-01-06 10:47:10 -0800431extern char CONSOLE_DATA[2925]; // see EOF
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800432int doDump(Bundle* bundle)
433{
434 status_t result = UNKNOWN_ERROR;
435 Asset* asset = NULL;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700436
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800437 if (bundle->getFileSpecCount() < 1) {
438 fprintf(stderr, "ERROR: no dump option specified\n");
439 return 1;
440 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700441
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800442 if (bundle->getFileSpecCount() < 2) {
443 fprintf(stderr, "ERROR: no dump file specified\n");
444 return 1;
445 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700446
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800447 const char* option = bundle->getFileSpecEntry(0);
448 const char* filename = bundle->getFileSpecEntry(1);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700449
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800450 AssetManager assets;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700451 void* assetsCookie;
452 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800453 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
454 return 1;
455 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700456
Dianne Hackborne289bff2011-06-13 19:33:22 -0700457 // Make a dummy config for retrieving resources... we need to supply
458 // non-default values for some configs so that we can retrieve resources
459 // in the app that don't have a default. The most important of these is
460 // the API version because key resources like icons will have an implicit
461 // version if they are using newer config types like density.
462 ResTable_config config;
463 config.language[0] = 'e';
464 config.language[1] = 'n';
465 config.country[0] = 'U';
466 config.country[1] = 'S';
467 config.orientation = ResTable_config::ORIENTATION_PORT;
468 config.density = ResTable_config::DENSITY_MEDIUM;
469 config.sdkVersion = 10000; // Very high.
470 config.screenWidthDp = 320;
471 config.screenHeightDp = 480;
472 config.smallestScreenWidthDp = 320;
473 assets.setConfiguration(config);
474
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800475 const ResTable& res = assets.getResources(false);
476 if (&res == NULL) {
477 fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
478 goto bail;
479 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700480
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800481 if (strcmp("resources", option) == 0) {
Steve Blockf1ff21a2010-06-14 17:34:04 +0100482#ifndef HAVE_ANDROID_OS
Dianne Hackborne17086b2009-06-19 15:13:28 -0700483 res.print(bundle->getValues());
Steve Blockf1ff21a2010-06-14 17:34:04 +0100484#endif
Dianne Hackborn6c997a92012-01-31 11:27:43 -0800485
486 } else if (strcmp("strings", option) == 0) {
487 const ResStringPool* pool = res.getTableStringBlock(0);
488 printStringPool(pool);
489
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800490 } else if (strcmp("xmltree", option) == 0) {
491 if (bundle->getFileSpecCount() < 3) {
492 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
493 goto bail;
494 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700495
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800496 for (int i=2; i<bundle->getFileSpecCount(); i++) {
497 const char* resname = bundle->getFileSpecEntry(i);
498 ResXMLTree tree;
499 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
500 if (asset == NULL) {
Kenny Root44b283d2009-09-01 19:03:11 -0500501 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800502 goto bail;
503 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700504
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800505 if (tree.setTo(asset->getBuffer(true),
506 asset->getLength()) != NO_ERROR) {
507 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
508 goto bail;
509 }
510 tree.restart();
511 printXMLBlock(&tree);
Kenny Root19138462009-12-04 09:38:48 -0800512 tree.uninit();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800513 delete asset;
514 asset = NULL;
515 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700516
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800517 } else if (strcmp("xmlstrings", option) == 0) {
518 if (bundle->getFileSpecCount() < 3) {
519 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
520 goto bail;
521 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700522
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800523 for (int i=2; i<bundle->getFileSpecCount(); i++) {
524 const char* resname = bundle->getFileSpecEntry(i);
525 ResXMLTree tree;
526 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
527 if (asset == NULL) {
Kenny Root44b283d2009-09-01 19:03:11 -0500528 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800529 goto bail;
530 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700531
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800532 if (tree.setTo(asset->getBuffer(true),
533 asset->getLength()) != NO_ERROR) {
534 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
535 goto bail;
536 }
537 printStringPool(&tree.getStrings());
538 delete asset;
539 asset = NULL;
540 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700541
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800542 } else {
543 ResXMLTree tree;
544 asset = assets.openNonAsset("AndroidManifest.xml",
545 Asset::ACCESS_BUFFER);
546 if (asset == NULL) {
547 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
548 goto bail;
549 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700550
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800551 if (tree.setTo(asset->getBuffer(true),
552 asset->getLength()) != NO_ERROR) {
553 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
554 goto bail;
555 }
556 tree.restart();
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700557
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800558 if (strcmp("permissions", option) == 0) {
559 size_t len;
560 ResXMLTree::event_code_t code;
561 int depth = 0;
562 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
563 if (code == ResXMLTree::END_TAG) {
564 depth--;
565 continue;
566 }
567 if (code != ResXMLTree::START_TAG) {
568 continue;
569 }
570 depth++;
571 String8 tag(tree.getElementName(&len));
572 //printf("Depth %d tag %s\n", depth, tag.string());
573 if (depth == 1) {
574 if (tag != "manifest") {
575 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
576 goto bail;
577 }
578 String8 pkg = getAttribute(tree, NULL, "package", NULL);
579 printf("package: %s\n", pkg.string());
580 } else if (depth == 2 && tag == "permission") {
581 String8 error;
582 String8 name = getAttribute(tree, NAME_ATTR, &error);
583 if (error != "") {
584 fprintf(stderr, "ERROR: %s\n", error.string());
585 goto bail;
586 }
587 printf("permission: %s\n", name.string());
588 } else if (depth == 2 && tag == "uses-permission") {
589 String8 error;
590 String8 name = getAttribute(tree, NAME_ATTR, &error);
591 if (error != "") {
592 fprintf(stderr, "ERROR: %s\n", error.string());
593 goto bail;
594 }
595 printf("uses-permission: %s\n", name.string());
Nick Kralevich1bcc3d62013-04-03 09:00:02 -0700596 int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
597 if (!req) {
598 printf("optional-permission: %s\n", name.string());
599 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800600 }
601 }
602 } else if (strcmp("badging", option) == 0) {
Dianne Hackborne289bff2011-06-13 19:33:22 -0700603 Vector<String8> locales;
604 res.getLocales(&locales);
605
606 Vector<ResTable_config> configs;
607 res.getConfigurations(&configs);
608 SortedVector<int> densities;
609 const size_t NC = configs.size();
610 for (size_t i=0; i<NC; i++) {
611 int dens = configs[i].density;
612 if (dens == 0) dens = 160;
613 densities.add(dens);
614 }
615
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800616 size_t len;
617 ResXMLTree::event_code_t code;
618 int depth = 0;
619 String8 error;
620 bool withinActivity = false;
621 bool isMainActivity = false;
622 bool isLauncherActivity = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700623 bool isSearchable = false;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700624 bool withinApplication = false;
Michael Wrighteaeb1902013-09-05 18:15:57 -0700625 bool withinSupportsInput = false;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700626 bool withinReceiver = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700627 bool withinService = false;
628 bool withinIntentFilter = false;
629 bool hasMainActivity = false;
630 bool hasOtherActivities = false;
631 bool hasOtherReceivers = false;
632 bool hasOtherServices = false;
633 bool hasWallpaperService = false;
634 bool hasImeService = false;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700635 bool hasAccessibilityService = false;
636 bool hasPrintService = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700637 bool hasWidgetReceivers = false;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700638 bool hasDeviceAdminReceiver = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700639 bool hasIntentFilter = false;
640 bool actMainActivity = false;
641 bool actWidgetReceivers = false;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700642 bool actDeviceAdminEnabled = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700643 bool actImeService = false;
644 bool actWallpaperService = false;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700645 bool actAccessibilityService = false;
646 bool actPrintService = false;
647
648 // These permissions are required by services implementing services
649 // the system binds to (IME, Accessibility, PrintServices, etc.)
650 bool hasBindDeviceAdminPermission = false;
651 bool hasBindInputMethodPermission = false;
652 bool hasBindAccessibilityServicePermission = false;
653 bool hasBindPrintServicePermission = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700654
Kenny Root063a44e2011-12-08 08:46:03 -0800655 // These two implement the implicit permissions that are granted
656 // to pre-1.6 applications.
657 bool hasWriteExternalStoragePermission = false;
658 bool hasReadPhoneStatePermission = false;
659
Dianne Hackborn79245122012-03-12 10:51:26 -0700660 // If an app requests write storage, they will also get read storage.
661 bool hasReadExternalStoragePermission = false;
662
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -0700663 // Implement transition to read and write call log.
664 bool hasReadContactsPermission = false;
665 bool hasWriteContactsPermission = false;
666 bool hasReadCallLogPermission = false;
667 bool hasWriteCallLogPermission = false;
668
Dan Morrill89d97c12010-05-03 16:13:14 -0700669 // This next group of variables is used to implement a group of
670 // backward-compatibility heuristics necessitated by the addition of
671 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
672 // heuristic is "if an app requests a permission but doesn't explicitly
673 // request the corresponding <uses-feature>, presume it's there anyway".
674 bool specCameraFeature = false; // camera-related
675 bool specCameraAutofocusFeature = false;
676 bool reqCameraAutofocusFeature = false;
677 bool reqCameraFlashFeature = false;
Dianne Hackborne5276a72009-08-27 16:28:44 -0700678 bool hasCameraPermission = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700679 bool specLocationFeature = false; // location-related
680 bool specNetworkLocFeature = false;
681 bool reqNetworkLocFeature = false;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800682 bool specGpsFeature = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700683 bool reqGpsFeature = false;
684 bool hasMockLocPermission = false;
685 bool hasCoarseLocPermission = false;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800686 bool hasGpsPermission = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700687 bool hasGeneralLocPermission = false;
688 bool specBluetoothFeature = false; // Bluetooth API-related
689 bool hasBluetoothPermission = false;
690 bool specMicrophoneFeature = false; // microphone-related
691 bool hasRecordAudioPermission = false;
692 bool specWiFiFeature = false;
693 bool hasWiFiPermission = false;
694 bool specTelephonyFeature = false; // telephony-related
695 bool reqTelephonySubFeature = false;
696 bool hasTelephonyPermission = false;
697 bool specTouchscreenFeature = false; // touchscreen-related
698 bool specMultitouchFeature = false;
699 bool reqDistinctMultitouchFeature = false;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700700 bool specScreenPortraitFeature = false;
701 bool specScreenLandscapeFeature = false;
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -0700702 bool reqScreenPortraitFeature = false;
703 bool reqScreenLandscapeFeature = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700704 // 2.2 also added some other features that apps can request, but that
705 // have no corresponding permission, so we cannot implement any
706 // back-compatibility heuristic for them. The below are thus unnecessary
707 // (but are retained here for documentary purposes.)
708 //bool specCompassFeature = false;
709 //bool specAccelerometerFeature = false;
710 //bool specProximityFeature = false;
711 //bool specAmbientLightFeature = false;
712 //bool specLiveWallpaperFeature = false;
713
Dianne Hackborn723738c2009-06-25 19:48:04 -0700714 int targetSdk = 0;
715 int smallScreen = 1;
716 int normalScreen = 1;
717 int largeScreen = 1;
Dianne Hackbornf43489d2010-08-20 12:44:33 -0700718 int xlargeScreen = 1;
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700719 int anyDensity = 1;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700720 int requiresSmallestWidthDp = 0;
721 int compatibleWidthLimitDp = 0;
722 int largestWidthLimitDp = 0;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700723 String8 pkg;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800724 String8 activityName;
725 String8 activityLabel;
726 String8 activityIcon;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700727 String8 receiverName;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700728 String8 serviceName;
Michael Wrighteaeb1902013-09-05 18:15:57 -0700729 Vector<String8> supportedInput;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800730 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
731 if (code == ResXMLTree::END_TAG) {
732 depth--;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700733 if (depth < 2) {
Michael Wrighteaeb1902013-09-05 18:15:57 -0700734 if (withinSupportsInput && !supportedInput.isEmpty()) {
735 printf("supports-input: '");
736 const size_t N = supportedInput.size();
737 for (size_t i=0; i<N; i++) {
738 printf("%s", supportedInput[i].string());
739 if (i != N - 1) {
740 printf("' '");
741 } else {
742 printf("'\n");
743 }
744 }
745 supportedInput.clear();
746 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700747 withinApplication = false;
Michael Wrighteaeb1902013-09-05 18:15:57 -0700748 withinSupportsInput = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700749 } else if (depth < 3) {
750 if (withinActivity && isMainActivity && isLauncherActivity) {
751 const char *aName = getComponentName(pkg, activityName);
Dianne Hackborne289bff2011-06-13 19:33:22 -0700752 printf("launchable-activity:");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700753 if (aName != NULL) {
Dianne Hackborne289bff2011-06-13 19:33:22 -0700754 printf(" name='%s' ", aName);
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700755 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700756 printf(" label='%s' icon='%s'\n",
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700757 activityLabel.string(),
758 activityIcon.string());
759 }
760 if (!hasIntentFilter) {
761 hasOtherActivities |= withinActivity;
762 hasOtherReceivers |= withinReceiver;
763 hasOtherServices |= withinService;
764 }
765 withinActivity = false;
766 withinService = false;
767 withinReceiver = false;
768 hasIntentFilter = false;
769 isMainActivity = isLauncherActivity = false;
770 } else if (depth < 4) {
771 if (withinIntentFilter) {
772 if (withinActivity) {
773 hasMainActivity |= actMainActivity;
774 hasOtherActivities |= !actMainActivity;
775 } else if (withinReceiver) {
776 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700777 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
778 hasBindDeviceAdminPermission);
779 hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled);
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700780 } else if (withinService) {
781 hasImeService |= actImeService;
782 hasWallpaperService |= actWallpaperService;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700783 hasAccessibilityService |= (actAccessibilityService &&
784 hasBindAccessibilityServicePermission);
785 hasPrintService |= (actPrintService && hasBindPrintServicePermission);
786 hasOtherServices |= (!actImeService && !actWallpaperService &&
787 !actAccessibilityService && !actPrintService);
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700788 }
789 }
790 withinIntentFilter = false;
791 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800792 continue;
793 }
794 if (code != ResXMLTree::START_TAG) {
795 continue;
796 }
797 depth++;
798 String8 tag(tree.getElementName(&len));
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700799 //printf("Depth %d, %s\n", depth, tag.string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800800 if (depth == 1) {
801 if (tag != "manifest") {
802 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
803 goto bail;
804 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700805 pkg = getAttribute(tree, NULL, "package", NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800806 printf("package: name='%s' ", pkg.string());
807 int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
808 if (error != "") {
809 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
810 goto bail;
811 }
812 if (versionCode > 0) {
813 printf("versionCode='%d' ", versionCode);
814 } else {
815 printf("versionCode='' ");
816 }
Dianne Hackborncf244ad2010-03-09 15:00:30 -0800817 String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800818 if (error != "") {
819 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
820 goto bail;
821 }
822 printf("versionName='%s'\n", versionName.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700823 } else if (depth == 2) {
824 withinApplication = false;
825 if (tag == "application") {
826 withinApplication = true;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700827
828 String8 label;
829 const size_t NL = locales.size();
830 for (size_t i=0; i<NL; i++) {
831 const char* localeStr = locales[i].string();
832 assets.setLocale(localeStr != NULL ? localeStr : "");
833 String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
834 if (llabel != "") {
835 if (localeStr == NULL || strlen(localeStr) == 0) {
836 label = llabel;
837 printf("application-label:'%s'\n", llabel.string());
838 } else {
839 if (label == "") {
840 label = llabel;
841 }
842 printf("application-label-%s:'%s'\n", localeStr,
843 llabel.string());
844 }
845 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700846 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700847
848 ResTable_config tmpConfig = config;
849 const size_t ND = densities.size();
850 for (size_t i=0; i<ND; i++) {
851 tmpConfig.density = densities[i];
852 assets.setConfiguration(tmpConfig);
853 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
854 if (icon != "") {
855 printf("application-icon-%d:'%s'\n", densities[i], icon.string());
856 }
857 }
858 assets.setConfiguration(config);
859
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700860 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
861 if (error != "") {
862 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
863 goto bail;
864 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700865 int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700866 if (error != "") {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700867 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700868 goto bail;
869 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700870 printf("application: label='%s' ", label.string());
871 printf("icon='%s'\n", icon.string());
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700872 if (testOnly != 0) {
873 printf("testOnly='%d'\n", testOnly);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700874 }
Dan Morrillb6ec11e2012-04-03 12:44:40 -0700875
876 int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0);
877 if (error != "") {
878 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string());
879 goto bail;
880 }
881 if (debuggable != 0) {
882 printf("application-debuggable\n");
883 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700884 } else if (tag == "uses-sdk") {
885 int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
886 if (error != "") {
887 error = "";
888 String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
889 if (error != "") {
890 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
891 error.string());
892 goto bail;
893 }
Dianne Hackborn723738c2009-06-25 19:48:04 -0700894 if (name == "Donut") targetSdk = 4;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700895 printf("sdkVersion:'%s'\n", name.string());
896 } else if (code != -1) {
Dianne Hackborn723738c2009-06-25 19:48:04 -0700897 targetSdk = code;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700898 printf("sdkVersion:'%d'\n", code);
899 }
Suchi Amalapurapu75c49842009-08-14 15:13:09 -0700900 code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
901 if (code != -1) {
902 printf("maxSdkVersion:'%d'\n", code);
903 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700904 code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
905 if (error != "") {
906 error = "";
907 String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
908 if (error != "") {
909 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
910 error.string());
911 goto bail;
912 }
Dianne Hackborn723738c2009-06-25 19:48:04 -0700913 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700914 printf("targetSdkVersion:'%s'\n", name.string());
915 } else if (code != -1) {
Dianne Hackborn723738c2009-06-25 19:48:04 -0700916 if (targetSdk < code) {
917 targetSdk = code;
918 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700919 printf("targetSdkVersion:'%d'\n", code);
920 }
921 } else if (tag == "uses-configuration") {
922 int32_t reqTouchScreen = getIntegerAttribute(tree,
923 REQ_TOUCH_SCREEN_ATTR, NULL, 0);
924 int32_t reqKeyboardType = getIntegerAttribute(tree,
925 REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
926 int32_t reqHardKeyboard = getIntegerAttribute(tree,
927 REQ_HARD_KEYBOARD_ATTR, NULL, 0);
928 int32_t reqNavigation = getIntegerAttribute(tree,
929 REQ_NAVIGATION_ATTR, NULL, 0);
930 int32_t reqFiveWayNav = getIntegerAttribute(tree,
931 REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
Dianne Hackborncb2d50d2010-01-06 11:29:54 -0800932 printf("uses-configuration:");
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700933 if (reqTouchScreen != 0) {
934 printf(" reqTouchScreen='%d'", reqTouchScreen);
935 }
936 if (reqKeyboardType != 0) {
937 printf(" reqKeyboardType='%d'", reqKeyboardType);
938 }
939 if (reqHardKeyboard != 0) {
940 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
941 }
942 if (reqNavigation != 0) {
943 printf(" reqNavigation='%d'", reqNavigation);
944 }
945 if (reqFiveWayNav != 0) {
946 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
947 }
948 printf("\n");
Michael Wrighteaeb1902013-09-05 18:15:57 -0700949 } else if (tag == "supports-input") {
950 withinSupportsInput = true;
Dianne Hackborn723738c2009-06-25 19:48:04 -0700951 } else if (tag == "supports-screens") {
952 smallScreen = getIntegerAttribute(tree,
953 SMALL_SCREEN_ATTR, NULL, 1);
954 normalScreen = getIntegerAttribute(tree,
955 NORMAL_SCREEN_ATTR, NULL, 1);
956 largeScreen = getIntegerAttribute(tree,
957 LARGE_SCREEN_ATTR, NULL, 1);
Dianne Hackbornf43489d2010-08-20 12:44:33 -0700958 xlargeScreen = getIntegerAttribute(tree,
959 XLARGE_SCREEN_ATTR, NULL, 1);
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700960 anyDensity = getIntegerAttribute(tree,
961 ANY_DENSITY_ATTR, NULL, 1);
Dianne Hackborne289bff2011-06-13 19:33:22 -0700962 requiresSmallestWidthDp = getIntegerAttribute(tree,
963 REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
964 compatibleWidthLimitDp = getIntegerAttribute(tree,
965 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
966 largestWidthLimitDp = getIntegerAttribute(tree,
967 LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
Dianne Hackborne5276a72009-08-27 16:28:44 -0700968 } else if (tag == "uses-feature") {
969 String8 name = getAttribute(tree, NAME_ATTR, &error);
Suchi Amalapurapu40b94722009-09-20 13:39:37 -0700970
971 if (name != "" && error == "") {
Dianne Hackborne5276a72009-08-27 16:28:44 -0700972 int req = getIntegerAttribute(tree,
973 REQUIRED_ATTR, NULL, 1);
Dan Morrill89d97c12010-05-03 16:13:14 -0700974
Dianne Hackborne5276a72009-08-27 16:28:44 -0700975 if (name == "android.hardware.camera") {
976 specCameraFeature = true;
Dan Morrill89d97c12010-05-03 16:13:14 -0700977 } else if (name == "android.hardware.camera.autofocus") {
978 // these have no corresponding permission to check for,
979 // but should imply the foundational camera permission
980 reqCameraAutofocusFeature = reqCameraAutofocusFeature || req;
981 specCameraAutofocusFeature = true;
982 } else if (req && (name == "android.hardware.camera.flash")) {
983 // these have no corresponding permission to check for,
984 // but should imply the foundational camera permission
985 reqCameraFlashFeature = true;
986 } else if (name == "android.hardware.location") {
987 specLocationFeature = true;
988 } else if (name == "android.hardware.location.network") {
989 specNetworkLocFeature = true;
990 reqNetworkLocFeature = reqNetworkLocFeature || req;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800991 } else if (name == "android.hardware.location.gps") {
992 specGpsFeature = true;
Dan Morrill89d97c12010-05-03 16:13:14 -0700993 reqGpsFeature = reqGpsFeature || req;
994 } else if (name == "android.hardware.bluetooth") {
995 specBluetoothFeature = true;
996 } else if (name == "android.hardware.touchscreen") {
997 specTouchscreenFeature = true;
998 } else if (name == "android.hardware.touchscreen.multitouch") {
999 specMultitouchFeature = true;
1000 } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
1001 reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req;
1002 } else if (name == "android.hardware.microphone") {
1003 specMicrophoneFeature = true;
1004 } else if (name == "android.hardware.wifi") {
1005 specWiFiFeature = true;
1006 } else if (name == "android.hardware.telephony") {
1007 specTelephonyFeature = true;
1008 } else if (req && (name == "android.hardware.telephony.gsm" ||
1009 name == "android.hardware.telephony.cdma")) {
1010 // these have no corresponding permission to check for,
1011 // but should imply the foundational telephony permission
1012 reqTelephonySubFeature = true;
Dianne Hackborne289bff2011-06-13 19:33:22 -07001013 } else if (name == "android.hardware.screen.portrait") {
1014 specScreenPortraitFeature = true;
1015 } else if (name == "android.hardware.screen.landscape") {
1016 specScreenLandscapeFeature = true;
Dianne Hackborne5276a72009-08-27 16:28:44 -07001017 }
1018 printf("uses-feature%s:'%s'\n",
1019 req ? "" : "-not-required", name.string());
1020 } else {
1021 int vers = getIntegerAttribute(tree,
1022 GL_ES_VERSION_ATTR, &error);
1023 if (error == "") {
1024 printf("uses-gl-es:'0x%x'\n", vers);
1025 }
1026 }
1027 } else if (tag == "uses-permission") {
1028 String8 name = getAttribute(tree, NAME_ATTR, &error);
Suchi Amalapurapu40b94722009-09-20 13:39:37 -07001029 if (name != "" && error == "") {
Dianne Hackborne5276a72009-08-27 16:28:44 -07001030 if (name == "android.permission.CAMERA") {
1031 hasCameraPermission = true;
Dianne Hackbornef05e072010-03-01 17:43:39 -08001032 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
1033 hasGpsPermission = true;
Dan Morrill89d97c12010-05-03 16:13:14 -07001034 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
1035 hasMockLocPermission = true;
1036 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
1037 hasCoarseLocPermission = true;
1038 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
1039 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
1040 hasGeneralLocPermission = true;
1041 } else if (name == "android.permission.BLUETOOTH" ||
1042 name == "android.permission.BLUETOOTH_ADMIN") {
1043 hasBluetoothPermission = true;
1044 } else if (name == "android.permission.RECORD_AUDIO") {
1045 hasRecordAudioPermission = true;
1046 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1047 name == "android.permission.CHANGE_WIFI_STATE" ||
1048 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
1049 hasWiFiPermission = true;
1050 } else if (name == "android.permission.CALL_PHONE" ||
1051 name == "android.permission.CALL_PRIVILEGED" ||
1052 name == "android.permission.MODIFY_PHONE_STATE" ||
1053 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1054 name == "android.permission.READ_SMS" ||
1055 name == "android.permission.RECEIVE_SMS" ||
1056 name == "android.permission.RECEIVE_MMS" ||
1057 name == "android.permission.RECEIVE_WAP_PUSH" ||
1058 name == "android.permission.SEND_SMS" ||
1059 name == "android.permission.WRITE_APN_SETTINGS" ||
1060 name == "android.permission.WRITE_SMS") {
1061 hasTelephonyPermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001062 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1063 hasWriteExternalStoragePermission = true;
Dianne Hackborn79245122012-03-12 10:51:26 -07001064 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1065 hasReadExternalStoragePermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001066 } else if (name == "android.permission.READ_PHONE_STATE") {
1067 hasReadPhoneStatePermission = true;
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001068 } else if (name == "android.permission.READ_CONTACTS") {
1069 hasReadContactsPermission = true;
1070 } else if (name == "android.permission.WRITE_CONTACTS") {
1071 hasWriteContactsPermission = true;
1072 } else if (name == "android.permission.READ_CALL_LOG") {
1073 hasReadCallLogPermission = true;
1074 } else if (name == "android.permission.WRITE_CALL_LOG") {
1075 hasWriteCallLogPermission = true;
Dianne Hackborne5276a72009-08-27 16:28:44 -07001076 }
1077 printf("uses-permission:'%s'\n", name.string());
Nick Kralevich1bcc3d62013-04-03 09:00:02 -07001078 int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
1079 if (!req) {
1080 printf("optional-permission:'%s'\n", name.string());
1081 }
Dianne Hackborne5276a72009-08-27 16:28:44 -07001082 } else {
1083 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1084 error.string());
1085 goto bail;
1086 }
Dianne Hackborn43b68032010-09-02 17:14:41 -07001087 } else if (tag == "uses-package") {
1088 String8 name = getAttribute(tree, NAME_ATTR, &error);
1089 if (name != "" && error == "") {
1090 printf("uses-package:'%s'\n", name.string());
1091 } else {
1092 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1093 error.string());
1094 goto bail;
1095 }
Jeff Hamiltone2c17f92010-02-12 13:45:16 -06001096 } else if (tag == "original-package") {
1097 String8 name = getAttribute(tree, NAME_ATTR, &error);
1098 if (name != "" && error == "") {
1099 printf("original-package:'%s'\n", name.string());
1100 } else {
1101 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1102 error.string());
1103 goto bail;
1104 }
Dan Morrill096b67f2010-12-13 16:25:54 -08001105 } else if (tag == "supports-gl-texture") {
Dan Morrill6f51fc12010-10-13 14:33:43 -07001106 String8 name = getAttribute(tree, NAME_ATTR, &error);
1107 if (name != "" && error == "") {
Dan Morrill096b67f2010-12-13 16:25:54 -08001108 printf("supports-gl-texture:'%s'\n", name.string());
Dan Morrill6f51fc12010-10-13 14:33:43 -07001109 } else {
1110 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1111 error.string());
1112 goto bail;
1113 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001114 } else if (tag == "compatible-screens") {
1115 printCompatibleScreens(tree);
1116 depth--;
Kenny Root56088a52011-09-29 13:49:45 -07001117 } else if (tag == "package-verifier") {
1118 String8 name = getAttribute(tree, NAME_ATTR, &error);
1119 if (name != "" && error == "") {
1120 String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
1121 if (publicKey != "" && error == "") {
1122 printf("package-verifier: name='%s' publicKey='%s'\n",
1123 name.string(), publicKey.string());
1124 }
1125 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001126 }
Michael Wrighteaeb1902013-09-05 18:15:57 -07001127 } else if (depth == 3) {
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001128 withinActivity = false;
1129 withinReceiver = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001130 withinService = false;
1131 hasIntentFilter = false;
Michael Wrighteaeb1902013-09-05 18:15:57 -07001132 if (withinApplication) {
1133 if(tag == "activity") {
1134 withinActivity = true;
1135 activityName = getAttribute(tree, NAME_ATTR, &error);
1136 if (error != "") {
1137 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1138 error.string());
1139 goto bail;
1140 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001141
Michael Wrighteaeb1902013-09-05 18:15:57 -07001142 activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1143 if (error != "") {
1144 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
1145 error.string());
1146 goto bail;
1147 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001148
Michael Wrighteaeb1902013-09-05 18:15:57 -07001149 activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1150 if (error != "") {
1151 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1152 error.string());
1153 goto bail;
1154 }
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001155
Michael Wrighteaeb1902013-09-05 18:15:57 -07001156 int32_t orien = getResolvedIntegerAttribute(&res, tree,
1157 SCREEN_ORIENTATION_ATTR, &error);
1158 if (error == "") {
1159 if (orien == 0 || orien == 6 || orien == 8) {
1160 // Requests landscape, sensorLandscape, or reverseLandscape.
1161 reqScreenLandscapeFeature = true;
1162 } else if (orien == 1 || orien == 7 || orien == 9) {
1163 // Requests portrait, sensorPortrait, or reversePortrait.
1164 reqScreenPortraitFeature = true;
1165 }
1166 }
1167 } else if (tag == "uses-library") {
1168 String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1169 if (error != "") {
1170 fprintf(stderr,
1171 "ERROR getting 'android:name' attribute for uses-library"
1172 " %s\n", error.string());
1173 goto bail;
1174 }
1175 int req = getIntegerAttribute(tree,
1176 REQUIRED_ATTR, NULL, 1);
1177 printf("uses-library%s:'%s'\n",
1178 req ? "" : "-not-required", libraryName.string());
1179 } else if (tag == "receiver") {
1180 withinReceiver = true;
1181 receiverName = getAttribute(tree, NAME_ATTR, &error);
1182
1183 if (error != "") {
1184 fprintf(stderr,
1185 "ERROR getting 'android:name' attribute for receiver:"
1186 " %s\n", error.string());
1187 goto bail;
1188 }
Adam Lesinskib1249b82013-09-25 17:03:50 -07001189
1190 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1191 if (error == "") {
1192 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1193 hasBindDeviceAdminPermission = true;
1194 }
1195 } else {
1196 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1197 " receiver '%s': %s\n", receiverName.string(), error.string());
1198 }
Michael Wrighteaeb1902013-09-05 18:15:57 -07001199 } else if (tag == "service") {
1200 withinService = true;
1201 serviceName = getAttribute(tree, NAME_ATTR, &error);
1202
1203 if (error != "") {
1204 fprintf(stderr, "ERROR getting 'android:name' attribute for"
1205 " service: %s\n", error.string());
1206 goto bail;
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001207 }
Adam Lesinskib1249b82013-09-25 17:03:50 -07001208
1209 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1210 if (error == "") {
1211 if (permission == "android.permission.BIND_INPUT_METHOD") {
1212 hasBindInputMethodPermission = true;
1213 } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") {
1214 hasBindAccessibilityServicePermission = true;
1215 } else if (permission == "android.permission.BIND_PRINT_SERVICE") {
1216 hasBindPrintServicePermission = true;
1217 }
1218 } else {
1219 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1220 " service '%s': %s\n", serviceName.string(), error.string());
1221 }
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001222 }
Michael Wrighteaeb1902013-09-05 18:15:57 -07001223 } else if (withinSupportsInput && tag == "input-type") {
1224 String8 name = getAttribute(tree, NAME_ATTR, &error);
1225 if (name != "" && error == "") {
1226 supportedInput.add(name);
1227 } else {
1228 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1229 error.string());
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001230 goto bail;
1231 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001232 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001233 } else if ((depth == 4) && (tag == "intent-filter")) {
1234 hasIntentFilter = true;
1235 withinIntentFilter = true;
Adam Lesinskib1249b82013-09-25 17:03:50 -07001236 actMainActivity = false;
1237 actWidgetReceivers = false;
1238 actImeService = false;
1239 actWallpaperService = false;
1240 actAccessibilityService = false;
1241 actPrintService = false;
1242 actDeviceAdminEnabled = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001243 } else if ((depth == 5) && withinIntentFilter){
1244 String8 action;
1245 if (tag == "action") {
1246 action = getAttribute(tree, NAME_ATTR, &error);
1247 if (error != "") {
1248 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1249 goto bail;
1250 }
1251 if (withinActivity) {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001252 if (action == "android.intent.action.MAIN") {
1253 isMainActivity = true;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001254 actMainActivity = true;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001255 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001256 } else if (withinReceiver) {
1257 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1258 actWidgetReceivers = true;
Adam Lesinskib1249b82013-09-25 17:03:50 -07001259 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1260 actDeviceAdminEnabled = true;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001261 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001262 } else if (withinService) {
1263 if (action == "android.view.InputMethod") {
1264 actImeService = true;
1265 } else if (action == "android.service.wallpaper.WallpaperService") {
1266 actWallpaperService = true;
Adam Lesinskib1249b82013-09-25 17:03:50 -07001267 } else if (action == "android.accessibilityservice.AccessibilityService") {
1268 actAccessibilityService = true;
1269 } else if (action == "android.printservice.PrintService") {
1270 actPrintService = true;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001271 }
1272 }
1273 if (action == "android.intent.action.SEARCH") {
1274 isSearchable = true;
1275 }
1276 }
1277
1278 if (tag == "category") {
1279 String8 category = getAttribute(tree, NAME_ATTR, &error);
1280 if (error != "") {
1281 fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string());
1282 goto bail;
1283 }
1284 if (withinActivity) {
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001285 if (category == "android.intent.category.LAUNCHER") {
1286 isLauncherActivity = true;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001287 }
1288 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001289 }
1290 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001291 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001292
Kenny Root063a44e2011-12-08 08:46:03 -08001293 // Pre-1.6 implicitly granted permission compatibility logic
1294 if (targetSdk < 4) {
1295 if (!hasWriteExternalStoragePermission) {
1296 printf("uses-permission:'android.permission.WRITE_EXTERNAL_STORAGE'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001297 printf("uses-implied-permission:'android.permission.WRITE_EXTERNAL_STORAGE'," \
1298 "'targetSdkVersion < 4'\n");
Dianne Hackborn79245122012-03-12 10:51:26 -07001299 hasWriteExternalStoragePermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001300 }
1301 if (!hasReadPhoneStatePermission) {
1302 printf("uses-permission:'android.permission.READ_PHONE_STATE'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001303 printf("uses-implied-permission:'android.permission.READ_PHONE_STATE'," \
1304 "'targetSdkVersion < 4'\n");
Kenny Root063a44e2011-12-08 08:46:03 -08001305 }
1306 }
1307
Dianne Hackborn79245122012-03-12 10:51:26 -07001308 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
Dianne Hackborn2bd8d042012-06-11 12:27:05 -07001309 // force them to always take READ_EXTERNAL_STORAGE as well. We always
1310 // do this (regardless of target API version) because we can't have
1311 // an app with write permission but not read permission.
Dianne Hackborn79245122012-03-12 10:51:26 -07001312 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
1313 printf("uses-permission:'android.permission.READ_EXTERNAL_STORAGE'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001314 printf("uses-implied-permission:'android.permission.READ_EXTERNAL_STORAGE'," \
1315 "'requested WRITE_EXTERNAL_STORAGE'\n");
Dianne Hackborn79245122012-03-12 10:51:26 -07001316 }
1317
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001318 // Pre-JellyBean call log permission compatibility.
1319 if (targetSdk < 16) {
1320 if (!hasReadCallLogPermission && hasReadContactsPermission) {
1321 printf("uses-permission:'android.permission.READ_CALL_LOG'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001322 printf("uses-implied-permission:'android.permission.READ_CALL_LOG'," \
1323 "'targetSdkVersion < 16 and requested READ_CONTACTS'\n");
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001324 }
1325 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
1326 printf("uses-permission:'android.permission.WRITE_CALL_LOG'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001327 printf("uses-implied-permission:'android.permission.WRITE_CALL_LOG'," \
1328 "'targetSdkVersion < 16 and requested WRITE_CONTACTS'\n");
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001329 }
1330 }
1331
Dan Morrill89d97c12010-05-03 16:13:14 -07001332 /* The following blocks handle printing "inferred" uses-features, based
1333 * on whether related features or permissions are used by the app.
1334 * Note that the various spec*Feature variables denote whether the
1335 * relevant tag was *present* in the AndroidManfest, not that it was
1336 * present and set to true.
1337 */
1338 // Camera-related back-compatibility logic
1339 if (!specCameraFeature) {
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001340 if (reqCameraFlashFeature) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001341 // if app requested a sub-feature (autofocus or flash) and didn't
1342 // request the base camera feature, we infer that it meant to
1343 printf("uses-feature:'android.hardware.camera'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001344 printf("uses-implied-feature:'android.hardware.camera'," \
1345 "'requested android.hardware.camera.flash feature'\n");
1346 } else if (reqCameraAutofocusFeature) {
1347 // if app requested a sub-feature (autofocus or flash) and didn't
1348 // request the base camera feature, we infer that it meant to
1349 printf("uses-feature:'android.hardware.camera'\n");
1350 printf("uses-implied-feature:'android.hardware.camera'," \
1351 "'requested android.hardware.camera.autofocus feature'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001352 } else if (hasCameraPermission) {
1353 // if app wants to use camera but didn't request the feature, we infer
1354 // that it meant to, and further that it wants autofocus
1355 // (which was the 1.0 - 1.5 behavior)
1356 printf("uses-feature:'android.hardware.camera'\n");
1357 if (!specCameraAutofocusFeature) {
1358 printf("uses-feature:'android.hardware.camera.autofocus'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001359 printf("uses-implied-feature:'android.hardware.camera.autofocus'," \
1360 "'requested android.permission.CAMERA permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001361 }
1362 }
Dianne Hackborne5276a72009-08-27 16:28:44 -07001363 }
Doug Zongkerdbe7a682009-10-09 11:24:51 -07001364
Dan Morrill89d97c12010-05-03 16:13:14 -07001365 // Location-related back-compatibility logic
1366 if (!specLocationFeature &&
1367 (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission ||
1368 hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) {
1369 // if app either takes a location-related permission or requests one of the
1370 // sub-features, we infer that it also meant to request the base location feature
1371 printf("uses-feature:'android.hardware.location'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001372 printf("uses-implied-feature:'android.hardware.location'," \
1373 "'requested a location access permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001374 }
Dianne Hackbornef05e072010-03-01 17:43:39 -08001375 if (!specGpsFeature && hasGpsPermission) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001376 // if app takes GPS (FINE location) perm but does not request the GPS
1377 // feature, we infer that it meant to
Dianne Hackbornef05e072010-03-01 17:43:39 -08001378 printf("uses-feature:'android.hardware.location.gps'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001379 printf("uses-implied-feature:'android.hardware.location.gps'," \
1380 "'requested android.permission.ACCESS_FINE_LOCATION permission'\n");
Dianne Hackbornef05e072010-03-01 17:43:39 -08001381 }
Dan Morrill89d97c12010-05-03 16:13:14 -07001382 if (!specNetworkLocFeature && hasCoarseLocPermission) {
1383 // if app takes Network location (COARSE location) perm but does not request the
1384 // network location feature, we infer that it meant to
1385 printf("uses-feature:'android.hardware.location.network'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001386 printf("uses-implied-feature:'android.hardware.location.network'," \
Dianne Hackbornec559ff2013-02-25 15:42:07 -08001387 "'requested android.permission.ACCESS_COARSE_LOCATION permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001388 }
1389
1390 // Bluetooth-related compatibility logic
Dan Morrill6b22d812010-06-15 21:41:42 -07001391 if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001392 // if app takes a Bluetooth permission but does not request the Bluetooth
1393 // feature, we infer that it meant to
1394 printf("uses-feature:'android.hardware.bluetooth'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001395 printf("uses-implied-feature:'android.hardware.bluetooth'," \
1396 "'requested android.permission.BLUETOOTH or android.permission.BLUETOOTH_ADMIN " \
1397 "permission and targetSdkVersion > 4'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001398 }
1399
1400 // Microphone-related compatibility logic
1401 if (!specMicrophoneFeature && hasRecordAudioPermission) {
1402 // if app takes the record-audio permission but does not request the microphone
1403 // feature, we infer that it meant to
1404 printf("uses-feature:'android.hardware.microphone'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001405 printf("uses-implied-feature:'android.hardware.microphone'," \
1406 "'requested android.permission.RECORD_AUDIO permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001407 }
1408
1409 // WiFi-related compatibility logic
1410 if (!specWiFiFeature && hasWiFiPermission) {
1411 // if app takes one of the WiFi permissions but does not request the WiFi
1412 // feature, we infer that it meant to
1413 printf("uses-feature:'android.hardware.wifi'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001414 printf("uses-implied-feature:'android.hardware.wifi'," \
1415 "'requested android.permission.ACCESS_WIFI_STATE, " \
1416 "android.permission.CHANGE_WIFI_STATE, or " \
1417 "android.permission.CHANGE_WIFI_MULTICAST_STATE permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001418 }
1419
1420 // Telephony-related compatibility logic
1421 if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) {
1422 // if app takes one of the telephony permissions or requests a sub-feature but
1423 // does not request the base telephony feature, we infer that it meant to
1424 printf("uses-feature:'android.hardware.telephony'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001425 printf("uses-implied-feature:'android.hardware.telephony'," \
1426 "'requested a telephony-related permission or feature'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001427 }
1428
1429 // Touchscreen-related back-compatibility logic
1430 if (!specTouchscreenFeature) { // not a typo!
1431 // all apps are presumed to require a touchscreen, unless they explicitly say
1432 // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
1433 // Note that specTouchscreenFeature is true if the tag is present, regardless
1434 // of whether its value is true or false, so this is safe
1435 printf("uses-feature:'android.hardware.touchscreen'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001436 printf("uses-implied-feature:'android.hardware.touchscreen'," \
1437 "'assumed you require a touch screen unless explicitly made optional'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001438 }
1439 if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
1440 // if app takes one of the telephony permissions or requests a sub-feature but
1441 // does not request the base telephony feature, we infer that it meant to
1442 printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001443 printf("uses-implied-feature:'android.hardware.touchscreen.multitouch'," \
1444 "'requested android.hardware.touchscreen.multitouch.distinct feature'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001445 }
Dianne Hackbornef05e072010-03-01 17:43:39 -08001446
Dianne Hackborne289bff2011-06-13 19:33:22 -07001447 // Landscape/portrait-related compatibility logic
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001448 if (!specScreenLandscapeFeature && !specScreenPortraitFeature) {
1449 // If the app has specified any activities in its manifest
1450 // that request a specific orientation, then assume that
1451 // orientation is required.
1452 if (reqScreenLandscapeFeature) {
1453 printf("uses-feature:'android.hardware.screen.landscape'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001454 printf("uses-implied-feature:'android.hardware.screen.landscape'," \
1455 "'one or more activities have specified a landscape orientation'\n");
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001456 }
1457 if (reqScreenPortraitFeature) {
1458 printf("uses-feature:'android.hardware.screen.portrait'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001459 printf("uses-implied-feature:'android.hardware.screen.portrait'," \
1460 "'one or more activities have specified a portrait orientation'\n");
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001461 }
Dianne Hackborne289bff2011-06-13 19:33:22 -07001462 }
1463
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001464 if (hasMainActivity) {
1465 printf("main\n");
1466 }
1467 if (hasWidgetReceivers) {
1468 printf("app-widget\n");
1469 }
Adam Lesinskib1249b82013-09-25 17:03:50 -07001470 if (hasDeviceAdminReceiver) {
1471 printf("device-admin\n");
1472 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001473 if (hasImeService) {
1474 printf("ime\n");
1475 }
1476 if (hasWallpaperService) {
1477 printf("wallpaper\n");
1478 }
Adam Lesinskib1249b82013-09-25 17:03:50 -07001479 if (hasAccessibilityService) {
1480 printf("accessibility\n");
1481 }
1482 if (hasPrintService) {
1483 printf("print\n");
1484 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001485 if (hasOtherActivities) {
1486 printf("other-activities\n");
1487 }
1488 if (isSearchable) {
1489 printf("search\n");
1490 }
1491 if (hasOtherReceivers) {
1492 printf("other-receivers\n");
1493 }
1494 if (hasOtherServices) {
1495 printf("other-services\n");
1496 }
1497
Dianne Hackborne289bff2011-06-13 19:33:22 -07001498 // For modern apps, if screen size buckets haven't been specified
1499 // but the new width ranges have, then infer the buckets from them.
1500 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1501 && requiresSmallestWidthDp > 0) {
1502 int compatWidth = compatibleWidthLimitDp;
1503 if (compatWidth <= 0) compatWidth = requiresSmallestWidthDp;
1504 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1505 smallScreen = -1;
1506 } else {
1507 smallScreen = 0;
1508 }
1509 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1510 normalScreen = -1;
1511 } else {
1512 normalScreen = 0;
1513 }
1514 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1515 largeScreen = -1;
1516 } else {
1517 largeScreen = 0;
1518 }
1519 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1520 xlargeScreen = -1;
1521 } else {
1522 xlargeScreen = 0;
1523 }
1524 }
1525
Dianne Hackborn723738c2009-06-25 19:48:04 -07001526 // Determine default values for any unspecified screen sizes,
1527 // based on the target SDK of the package. As of 4 (donut)
1528 // the screen size support was introduced, so all default to
1529 // enabled.
1530 if (smallScreen > 0) {
1531 smallScreen = targetSdk >= 4 ? -1 : 0;
1532 }
1533 if (normalScreen > 0) {
1534 normalScreen = -1;
1535 }
1536 if (largeScreen > 0) {
1537 largeScreen = targetSdk >= 4 ? -1 : 0;
1538 }
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001539 if (xlargeScreen > 0) {
Scott Maind58fb972010-11-04 18:32:00 -07001540 // Introduced in Gingerbread.
1541 xlargeScreen = targetSdk >= 9 ? -1 : 0;
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001542 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001543 if (anyDensity > 0) {
Dianne Hackborne289bff2011-06-13 19:33:22 -07001544 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1545 || compatibleWidthLimitDp > 0) ? -1 : 0;
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001546 }
Dianne Hackborn723738c2009-06-25 19:48:04 -07001547 printf("supports-screens:");
1548 if (smallScreen != 0) printf(" 'small'");
1549 if (normalScreen != 0) printf(" 'normal'");
1550 if (largeScreen != 0) printf(" 'large'");
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001551 if (xlargeScreen != 0) printf(" 'xlarge'");
Dianne Hackborn723738c2009-06-25 19:48:04 -07001552 printf("\n");
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001553 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
Dianne Hackborne289bff2011-06-13 19:33:22 -07001554 if (requiresSmallestWidthDp > 0) {
1555 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1556 }
1557 if (compatibleWidthLimitDp > 0) {
1558 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1559 }
1560 if (largestWidthLimitDp > 0) {
1561 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1562 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001563
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001564 printf("locales:");
Dianne Hackborne17086b2009-06-19 15:13:28 -07001565 const size_t NL = locales.size();
1566 for (size_t i=0; i<NL; i++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001567 const char* localeStr = locales[i].string();
1568 if (localeStr == NULL || strlen(localeStr) == 0) {
1569 localeStr = "--_--";
1570 }
1571 printf(" '%s'", localeStr);
1572 }
1573 printf("\n");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001574
Dianne Hackborne17086b2009-06-19 15:13:28 -07001575 printf("densities:");
1576 const size_t ND = densities.size();
1577 for (size_t i=0; i<ND; i++) {
1578 printf(" '%d'", densities[i]);
1579 }
1580 printf("\n");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001581
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001582 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1583 if (dir != NULL) {
1584 if (dir->getFileCount() > 0) {
1585 printf("native-code:");
1586 for (size_t i=0; i<dir->getFileCount(); i++) {
1587 printf(" '%s'", dir->getFileName(i).string());
1588 }
1589 printf("\n");
1590 }
1591 delete dir;
1592 }
Dan Morrille74763e2012-01-06 10:47:10 -08001593 } else if (strcmp("badger", option) == 0) {
Dianne Hackborn6c997a92012-01-31 11:27:43 -08001594 printf("%s", CONSOLE_DATA);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001595 } else if (strcmp("configurations", option) == 0) {
1596 Vector<ResTable_config> configs;
1597 res.getConfigurations(&configs);
1598 const size_t N = configs.size();
1599 for (size_t i=0; i<N; i++) {
1600 printf("%s\n", configs[i].toString().string());
1601 }
1602 } else {
1603 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
1604 goto bail;
1605 }
1606 }
1607
1608 result = NO_ERROR;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001609
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001610bail:
1611 if (asset) {
1612 delete asset;
1613 }
1614 return (result != NO_ERROR);
1615}
1616
1617
1618/*
1619 * Handle the "add" command, which wants to add files to a new or
1620 * pre-existing archive.
1621 */
1622int doAdd(Bundle* bundle)
1623{
1624 ZipFile* zip = NULL;
1625 status_t result = UNKNOWN_ERROR;
1626 const char* zipFileName;
1627
1628 if (bundle->getUpdate()) {
1629 /* avoid confusion */
1630 fprintf(stderr, "ERROR: can't use '-u' with add\n");
1631 goto bail;
1632 }
1633
1634 if (bundle->getFileSpecCount() < 1) {
1635 fprintf(stderr, "ERROR: must specify zip file name\n");
1636 goto bail;
1637 }
1638 zipFileName = bundle->getFileSpecEntry(0);
1639
1640 if (bundle->getFileSpecCount() < 2) {
1641 fprintf(stderr, "NOTE: nothing to do\n");
1642 goto bail;
1643 }
1644
1645 zip = openReadWrite(zipFileName, true);
1646 if (zip == NULL) {
1647 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
1648 goto bail;
1649 }
1650
1651 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1652 const char* fileName = bundle->getFileSpecEntry(i);
1653
1654 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
1655 printf(" '%s'... (from gzip)\n", fileName);
1656 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
1657 } else {
Doug Zongkerdbe7a682009-10-09 11:24:51 -07001658 if (bundle->getJunkPath()) {
1659 String8 storageName = String8(fileName).getPathLeaf();
1660 printf(" '%s' as '%s'...\n", fileName, storageName.string());
1661 result = zip->add(fileName, storageName.string(),
1662 bundle->getCompressionMethod(), NULL);
1663 } else {
1664 printf(" '%s'...\n", fileName);
1665 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
1666 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001667 }
1668 if (result != NO_ERROR) {
1669 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
1670 if (result == NAME_NOT_FOUND)
1671 fprintf(stderr, ": file not found\n");
1672 else if (result == ALREADY_EXISTS)
1673 fprintf(stderr, ": already exists in archive\n");
1674 else
1675 fprintf(stderr, "\n");
1676 goto bail;
1677 }
1678 }
1679
1680 result = NO_ERROR;
1681
1682bail:
1683 delete zip;
1684 return (result != NO_ERROR);
1685}
1686
1687
1688/*
1689 * Delete files from an existing archive.
1690 */
1691int doRemove(Bundle* bundle)
1692{
1693 ZipFile* zip = NULL;
1694 status_t result = UNKNOWN_ERROR;
1695 const char* zipFileName;
1696
1697 if (bundle->getFileSpecCount() < 1) {
1698 fprintf(stderr, "ERROR: must specify zip file name\n");
1699 goto bail;
1700 }
1701 zipFileName = bundle->getFileSpecEntry(0);
1702
1703 if (bundle->getFileSpecCount() < 2) {
1704 fprintf(stderr, "NOTE: nothing to do\n");
1705 goto bail;
1706 }
1707
1708 zip = openReadWrite(zipFileName, false);
1709 if (zip == NULL) {
1710 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
1711 zipFileName);
1712 goto bail;
1713 }
1714
1715 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1716 const char* fileName = bundle->getFileSpecEntry(i);
1717 ZipEntry* entry;
1718
1719 entry = zip->getEntryByName(fileName);
1720 if (entry == NULL) {
1721 printf(" '%s' NOT FOUND\n", fileName);
1722 continue;
1723 }
1724
1725 result = zip->remove(entry);
1726
1727 if (result != NO_ERROR) {
1728 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
1729 bundle->getFileSpecEntry(i), zipFileName);
1730 goto bail;
1731 }
1732 }
1733
1734 /* update the archive */
1735 zip->flush();
1736
1737bail:
1738 delete zip;
1739 return (result != NO_ERROR);
1740}
1741
1742
1743/*
1744 * Package up an asset directory and associated application files.
1745 */
1746int doPackage(Bundle* bundle)
1747{
1748 const char* outputAPKFile;
1749 int retVal = 1;
1750 status_t err;
1751 sp<AaptAssets> assets;
1752 int N;
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001753 FILE* fp;
1754 String8 dependencyFile;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001755
1756 // -c zz_ZZ means do pseudolocalization
1757 ResourceFilter filter;
1758 err = filter.parse(bundle->getConfigurations());
1759 if (err != NO_ERROR) {
1760 goto bail;
1761 }
1762 if (filter.containsPseudo()) {
1763 bundle->setPseudolocalize(true);
1764 }
1765
1766 N = bundle->getFileSpecCount();
1767 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
1768 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDir() == NULL) {
1769 fprintf(stderr, "ERROR: no input files\n");
1770 goto bail;
1771 }
1772
1773 outputAPKFile = bundle->getOutputAPKFile();
1774
1775 // Make sure the filenames provided exist and are of the appropriate type.
1776 if (outputAPKFile) {
1777 FileType type;
1778 type = getFileType(outputAPKFile);
1779 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
1780 fprintf(stderr,
1781 "ERROR: output file '%s' exists but is not regular file\n",
1782 outputAPKFile);
1783 goto bail;
1784 }
1785 }
1786
1787 // Load the assets.
1788 assets = new AaptAssets();
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001789
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001790 // Set up the resource gathering in assets if we're going to generate
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001791 // dependency files. Every time we encounter a resource while slurping
1792 // the tree, we'll add it to these stores so we have full resource paths
1793 // to write to a dependency file.
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001794 if (bundle->getGenDependencies()) {
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001795 sp<FilePathStore> resPathStore = new FilePathStore;
1796 assets->setFullResPaths(resPathStore);
1797 sp<FilePathStore> assetPathStore = new FilePathStore;
1798 assets->setFullAssetPaths(assetPathStore);
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001799 }
1800
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001801 err = assets->slurpFromArgs(bundle);
1802 if (err < 0) {
1803 goto bail;
1804 }
1805
1806 if (bundle->getVerbose()) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001807 assets->print(String8());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001808 }
1809
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001810 // If they asked for any fileAs that need to be compiled, do so.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001811 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
1812 err = buildResources(bundle, assets);
1813 if (err != 0) {
1814 goto bail;
1815 }
1816 }
1817
1818 // At this point we've read everything and processed everything. From here
1819 // on out it's just writing output files.
1820 if (SourcePos::hasErrors()) {
1821 goto bail;
1822 }
1823
Dianne Hackborn1644c6d2012-02-06 15:33:21 -08001824 // Update symbols with information about which ones are needed as Java symbols.
1825 assets->applyJavaSymbols();
1826 if (SourcePos::hasErrors()) {
1827 goto bail;
1828 }
1829
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001830 // If we've been asked to generate a dependency file, do that here
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001831 if (bundle->getGenDependencies()) {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001832 // If this is the packaging step, generate the dependency file next to
1833 // the output apk (e.g. bin/resources.ap_.d)
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001834 if (outputAPKFile) {
1835 dependencyFile = String8(outputAPKFile);
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001836 // Add the .d extension to the dependency file.
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001837 dependencyFile.append(".d");
1838 } else {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001839 // Else if this is the R.java dependency generation step,
1840 // generate the dependency file in the R.java package subdirectory
1841 // e.g. gen/com/foo/app/R.java.d
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001842 dependencyFile = String8(bundle->getRClassDir());
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001843 dependencyFile.appendPath("R.java.d");
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001844 }
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001845 // Make sure we have a clean dependency file to start with
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001846 fp = fopen(dependencyFile, "w");
1847 fclose(fp);
1848 }
1849
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001850 // Write out R.java constants
Dianne Hackborn1644c6d2012-02-06 15:33:21 -08001851 if (!assets->havePrivateSymbols()) {
Xavier Ducrohet63459ad2009-11-30 18:05:10 -08001852 if (bundle->getCustomPackage() == NULL) {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001853 // Write the R.java file into the appropriate class directory
1854 // e.g. gen/com/foo/app/R.java
Xavier Ducrohet63459ad2009-11-30 18:05:10 -08001855 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
1856 } else {
1857 const String8 customPkg(bundle->getCustomPackage());
1858 err = writeResourceSymbols(bundle, assets, customPkg, true);
1859 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001860 if (err < 0) {
1861 goto bail;
1862 }
Ying Wang002f5372012-04-25 18:53:49 -07001863 // If we have library files, we're going to write our R.java file into
1864 // the appropriate class directory for those libraries as well.
1865 // e.g. gen/com/foo/app/lib/R.java
1866 if (bundle->getExtraPackages() != NULL) {
1867 // Split on colon
1868 String8 libs(bundle->getExtraPackages());
1869 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
1870 while (packageString != NULL) {
1871 // Write the R.java file out with the correct package name
1872 err = writeResourceSymbols(bundle, assets, String8(packageString), true);
1873 if (err < 0) {
1874 goto bail;
1875 }
1876 packageString = strtok(NULL, ":");
1877 }
1878 libs.unlockBuffer();
1879 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001880 } else {
1881 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
1882 if (err < 0) {
1883 goto bail;
1884 }
1885 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
1886 if (err < 0) {
1887 goto bail;
1888 }
1889 }
1890
Joe Onorato1553c822009-08-30 13:36:22 -07001891 // Write out the ProGuard file
1892 err = writeProguardFile(bundle, assets);
1893 if (err < 0) {
1894 goto bail;
1895 }
1896
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001897 // Write the apk
1898 if (outputAPKFile) {
1899 err = writeAPK(bundle, assets, String8(outputAPKFile));
1900 if (err != NO_ERROR) {
1901 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
1902 goto bail;
1903 }
1904 }
1905
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001906 // If we've been asked to generate a dependency file, we need to finish up here.
1907 // the writeResourceSymbols and writeAPK functions have already written the target
1908 // half of the dependency file, now we need to write the prerequisites. (files that
1909 // the R.java file or .ap_ file depend on)
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001910 if (bundle->getGenDependencies()) {
1911 // Now that writeResourceSymbols or writeAPK has taken care of writing
1912 // the targets to our dependency file, we'll write the prereqs
1913 fp = fopen(dependencyFile, "a+");
1914 fprintf(fp, " : ");
1915 bool includeRaw = (outputAPKFile != NULL);
1916 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001917 // Also manually add the AndroidManifeset since it's not under res/ or assets/
1918 // and therefore was not added to our pathstores during slurping
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001919 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
1920 fclose(fp);
1921 }
1922
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001923 retVal = 0;
1924bail:
1925 if (SourcePos::hasErrors()) {
1926 SourcePos::printErrors(stderr);
1927 }
1928 return retVal;
1929}
Josiah Gaskin8a39da82011-06-06 17:00:35 -07001930
1931/*
1932 * Do PNG Crunching
1933 * PRECONDITIONS
1934 * -S flag points to a source directory containing drawable* folders
1935 * -C flag points to destination directory. The folder structure in the
1936 * source directory will be mirrored to the destination (cache) directory
1937 *
1938 * POSTCONDITIONS
1939 * Destination directory will be updated to match the PNG files in
1940 * the source directory.
1941 */
1942int doCrunch(Bundle* bundle)
1943{
1944 fprintf(stdout, "Crunching PNG Files in ");
1945 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
1946 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
1947
1948 updatePreProcessedCache(bundle);
1949
1950 return NO_ERROR;
1951}
Dan Morrille74763e2012-01-06 10:47:10 -08001952
Xavier Ducrohetb1f6ad82012-12-21 09:54:02 -08001953/*
1954 * Do PNG Crunching on a single flag
1955 * -i points to a single png file
1956 * -o points to a single png output file
1957 */
1958int doSingleCrunch(Bundle* bundle)
1959{
1960 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
1961 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
1962
1963 String8 input(bundle->getSingleCrunchInputFile());
1964 String8 output(bundle->getSingleCrunchOutputFile());
Xavier Ducrohetb7de2192013-01-15 16:41:29 -08001965
Xavier Ducrohetc75d3f52013-01-14 18:02:40 -08001966 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
1967 // we can't return the status_t as it gets truncate to the lower 8 bits.
1968 return 42;
1969 }
Xavier Ducrohetb7de2192013-01-15 16:41:29 -08001970
Xavier Ducrohetc75d3f52013-01-14 18:02:40 -08001971 return NO_ERROR;
Xavier Ducrohetb1f6ad82012-12-21 09:54:02 -08001972}
1973
Dan Morrille74763e2012-01-06 10:47:10 -08001974char CONSOLE_DATA[2925] = {
1975 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1976 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1977 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1978 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1979 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
1980 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
1981 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1982 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1983 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
1984 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
1985 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1986 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1987 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
1988 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1989 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1990 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
1991 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
1992 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1993 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1994 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
1995 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
1996 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
1997 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
1998 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
1999 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2000 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2001 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2002 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2003 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2004 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2005 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2006 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2007 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2008 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2009 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2010 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2011 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2012 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2013 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2014 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2015 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2016 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2017 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2018 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2019 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2020 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2021 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2022 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2023 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2024 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2025 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2026 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2027 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2028 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2029 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2030 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2031 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2032 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2033 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2034 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2035 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2036 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2037 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2038 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2039 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2040 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2041 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2042 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2043 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2044 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2045 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2046 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2047 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2048 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2049 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2050 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2051 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2052 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2053 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2054 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2055 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2056 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2057 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2058 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2059 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2060 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2061 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2062 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2063 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2064 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2065 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2066 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2067 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2068 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2069 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2070 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2071 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2072 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2073 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2074 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2075 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2076 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2077 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2078 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2079 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2080 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2081 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2082 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2083 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2084 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2085 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2086 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2087 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2088 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2089 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2090 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2091 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2092 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2093 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2094 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2095 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2096 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2097 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2098 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2099 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2100 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2101 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2102 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2103 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2104 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2105 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2106 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2107 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2108 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2109 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2110 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2111 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2112 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2113 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2114 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2115 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2116 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2117 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2118 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2119 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2120 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2121 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2122 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2123 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2124 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2125 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2126 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2127 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2128 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2129 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2130 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2131 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2132 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2133 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2134 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2135 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2136 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2137 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2138 };