blob: 93dc5c932afb271e649e089b3c0effd398b83b55 [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);
Steve Blockf1ff21a2010-06-14 17:34:04 +0100200#ifndef HAVE_ANDROID_OS
Dan Alberta8995ad2014-09-08 18:34:29 -0700201 printf("\nResource table:\n");
202 res.print(false);
Steve Blockf1ff21a2010-06-14 17:34:04 +0100203#endif
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700204
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800205 Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
206 Asset::ACCESS_BUFFER);
207 if (manifestAsset == NULL) {
208 printf("\nNo AndroidManifest.xml found.\n");
209 } else {
210 printf("\nAndroid manifest:\n");
211 ResXMLTree tree;
212 tree.setTo(manifestAsset->getBuffer(true),
213 manifestAsset->getLength());
214 printXMLBlock(&tree);
215 }
216 delete manifestAsset;
217 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700218
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800219 result = 0;
220
221bail:
222 delete zip;
223 return result;
224}
225
226static ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes)
227{
228 size_t N = tree.getAttributeCount();
229 for (size_t i=0; i<N; i++) {
230 if (tree.getAttributeNameResID(i) == attrRes) {
231 return (ssize_t)i;
232 }
233 }
234 return -1;
235}
236
Joe Onorato1553c822009-08-30 13:36:22 -0700237String8 getAttribute(const ResXMLTree& tree, const char* ns,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800238 const char* attr, String8* outError)
239{
240 ssize_t idx = tree.indexOfAttribute(ns, attr);
241 if (idx < 0) {
242 return String8();
243 }
244 Res_value value;
245 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
246 if (value.dataType != Res_value::TYPE_STRING) {
247 if (outError != NULL) *outError = "attribute is not a string value";
248 return String8();
249 }
250 }
251 size_t len;
252 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
253 return str ? String8(str, len) : String8();
254}
255
256static String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError)
257{
258 ssize_t idx = indexOfAttribute(tree, attrRes);
259 if (idx < 0) {
260 return String8();
261 }
262 Res_value value;
263 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
264 if (value.dataType != Res_value::TYPE_STRING) {
265 if (outError != NULL) *outError = "attribute is not a string value";
266 return String8();
267 }
268 }
269 size_t len;
270 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
271 return str ? String8(str, len) : String8();
272}
273
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700274static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes,
275 String8* outError, int32_t defValue = -1)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800276{
277 ssize_t idx = indexOfAttribute(tree, attrRes);
278 if (idx < 0) {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700279 return defValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800280 }
281 Res_value value;
282 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700283 if (value.dataType < Res_value::TYPE_FIRST_INT
284 || value.dataType > Res_value::TYPE_LAST_INT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800285 if (outError != NULL) *outError = "attribute is not an integer value";
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700286 return defValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800287 }
288 }
289 return value.data;
290}
291
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -0700292static int32_t getResolvedIntegerAttribute(const ResTable* resTable, const ResXMLTree& tree,
293 uint32_t attrRes, String8* outError, int32_t defValue = -1)
294{
295 ssize_t idx = indexOfAttribute(tree, attrRes);
296 if (idx < 0) {
297 return defValue;
298 }
299 Res_value value;
300 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
301 if (value.dataType == Res_value::TYPE_REFERENCE) {
302 resTable->resolveReference(&value, 0);
303 }
304 if (value.dataType < Res_value::TYPE_FIRST_INT
305 || value.dataType > Res_value::TYPE_LAST_INT) {
306 if (outError != NULL) *outError = "attribute is not an integer value";
307 return defValue;
308 }
309 }
310 return value.data;
311}
312
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800313static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree,
314 uint32_t attrRes, String8* outError)
315{
316 ssize_t idx = indexOfAttribute(tree, attrRes);
317 if (idx < 0) {
318 return String8();
319 }
320 Res_value value;
321 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
322 if (value.dataType == Res_value::TYPE_STRING) {
323 size_t len;
324 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
325 return str ? String8(str, len) : String8();
326 }
327 resTable->resolveReference(&value, 0);
328 if (value.dataType != Res_value::TYPE_STRING) {
329 if (outError != NULL) *outError = "attribute is not a string value";
330 return String8();
331 }
332 }
333 size_t len;
334 const Res_value* value2 = &value;
335 const char16_t* str = const_cast<ResTable*>(resTable)->valueToString(value2, 0, NULL, &len);
336 return str ? String8(str, len) : String8();
337}
338
339// These are attribute resource constants for the platform, as found
340// in android.R.attr
341enum {
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -0700342 LABEL_ATTR = 0x01010001,
343 ICON_ATTR = 0x01010002,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800344 NAME_ATTR = 0x01010003,
Adam Lesinskib1249b82013-09-25 17:03:50 -0700345 PERMISSION_ATTR = 0x01010006,
Adam Lesinskid831e802013-09-27 11:14:57 -0700346 RESOURCE_ATTR = 0x01010025,
Dan Morrillb6ec11e2012-04-03 12:44:40 -0700347 DEBUGGABLE_ATTR = 0x0101000f,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800348 VERSION_CODE_ATTR = 0x0101021b,
349 VERSION_NAME_ATTR = 0x0101021c,
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -0700350 SCREEN_ORIENTATION_ATTR = 0x0101001e,
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700351 MIN_SDK_VERSION_ATTR = 0x0101020c,
Suchi Amalapurapu75c49842009-08-14 15:13:09 -0700352 MAX_SDK_VERSION_ATTR = 0x01010271,
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700353 REQ_TOUCH_SCREEN_ATTR = 0x01010227,
354 REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
355 REQ_HARD_KEYBOARD_ATTR = 0x01010229,
356 REQ_NAVIGATION_ATTR = 0x0101022a,
357 REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
358 TARGET_SDK_VERSION_ATTR = 0x01010270,
359 TEST_ONLY_ATTR = 0x01010272,
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700360 ANY_DENSITY_ATTR = 0x0101026c,
Dianne Hackborne5276a72009-08-27 16:28:44 -0700361 GL_ES_VERSION_ATTR = 0x01010281,
Dianne Hackborn723738c2009-06-25 19:48:04 -0700362 SMALL_SCREEN_ATTR = 0x01010284,
363 NORMAL_SCREEN_ATTR = 0x01010285,
364 LARGE_SCREEN_ATTR = 0x01010286,
Dianne Hackbornf43489d2010-08-20 12:44:33 -0700365 XLARGE_SCREEN_ATTR = 0x010102bf,
Dianne Hackborne5276a72009-08-27 16:28:44 -0700366 REQUIRED_ATTR = 0x0101028e,
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700367 SCREEN_SIZE_ATTR = 0x010102ca,
368 SCREEN_DENSITY_ATTR = 0x010102cb,
Dianne Hackborne289bff2011-06-13 19:33:22 -0700369 REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
370 COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
371 LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
Kenny Root56088a52011-09-29 13:49:45 -0700372 PUBLIC_KEY_ATTR = 0x010103a6,
Adam Lesinskid831e802013-09-27 11:14:57 -0700373 CATEGORY_ATTR = 0x010103e8,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800374};
375
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700376const char *getComponentName(String8 &pkgName, String8 &componentName) {
377 ssize_t idx = componentName.find(".");
378 String8 retStr(pkgName);
379 if (idx == 0) {
380 retStr += componentName;
381 } else if (idx < 0) {
382 retStr += ".";
383 retStr += componentName;
384 } else {
385 return componentName.string();
386 }
387 return retStr.string();
388}
389
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700390static void printCompatibleScreens(ResXMLTree& tree) {
391 size_t len;
392 ResXMLTree::event_code_t code;
393 int depth = 0;
394 bool first = true;
395 printf("compatible-screens:");
396 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
397 if (code == ResXMLTree::END_TAG) {
398 depth--;
399 if (depth < 0) {
400 break;
401 }
402 continue;
403 }
404 if (code != ResXMLTree::START_TAG) {
405 continue;
406 }
407 depth++;
408 String8 tag(tree.getElementName(&len));
409 if (tag == "screen") {
410 int32_t screenSize = getIntegerAttribute(tree,
411 SCREEN_SIZE_ATTR, NULL, -1);
412 int32_t screenDensity = getIntegerAttribute(tree,
413 SCREEN_DENSITY_ATTR, NULL, -1);
414 if (screenSize > 0 && screenDensity > 0) {
415 if (!first) {
416 printf(",");
417 }
418 first = false;
419 printf("'%d/%d'", screenSize, screenDensity);
420 }
421 }
422 }
423 printf("\n");
424}
425
Adam Lesinskid831e802013-09-27 11:14:57 -0700426Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool offHost,
427 String8 *outError = NULL)
428{
429 Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER);
430 if (aidAsset == NULL) {
431 if (outError != NULL) *outError = "xml resource does not exist";
432 return Vector<String8>();
433 }
434
435 const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service");
436
437 bool withinApduService = false;
438 Vector<String8> categories;
439
440 String8 error;
441 ResXMLTree tree;
442 tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength());
443
444 size_t len;
445 int depth = 0;
446 ResXMLTree::event_code_t code;
447 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
448 if (code == ResXMLTree::END_TAG) {
449 depth--;
450 String8 tag(tree.getElementName(&len));
451
452 if (depth == 0 && tag == serviceTagName) {
453 withinApduService = false;
454 }
455
456 } else if (code == ResXMLTree::START_TAG) {
457 depth++;
458 String8 tag(tree.getElementName(&len));
459
460 if (depth == 1) {
461 if (tag == serviceTagName) {
462 withinApduService = true;
463 }
464 } else if (depth == 2 && withinApduService) {
465 if (tag == "aid-group") {
466 String8 category = getAttribute(tree, CATEGORY_ATTR, &error);
467 if (error != "") {
468 if (outError != NULL) *outError = error;
469 return Vector<String8>();
470 }
471
472 categories.add(category);
473 }
474 }
475 }
476 }
477 aidAsset->close();
478 return categories;
479}
480
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800481/*
482 * Handle the "dump" command, to extract select data from an archive.
483 */
Dan Morrille74763e2012-01-06 10:47:10 -0800484extern char CONSOLE_DATA[2925]; // see EOF
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800485int doDump(Bundle* bundle)
486{
487 status_t result = UNKNOWN_ERROR;
488 Asset* asset = NULL;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700489
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800490 if (bundle->getFileSpecCount() < 1) {
491 fprintf(stderr, "ERROR: no dump option specified\n");
492 return 1;
493 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700494
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800495 if (bundle->getFileSpecCount() < 2) {
496 fprintf(stderr, "ERROR: no dump file specified\n");
497 return 1;
498 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700499
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800500 const char* option = bundle->getFileSpecEntry(0);
501 const char* filename = bundle->getFileSpecEntry(1);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700502
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800503 AssetManager assets;
Narayan Kamath745d4ef2014-01-27 11:17:22 +0000504 int32_t assetsCookie;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700505 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800506 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
507 return 1;
508 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700509
Dianne Hackborne289bff2011-06-13 19:33:22 -0700510 // Make a dummy config for retrieving resources... we need to supply
511 // non-default values for some configs so that we can retrieve resources
512 // in the app that don't have a default. The most important of these is
513 // the API version because key resources like icons will have an implicit
514 // version if they are using newer config types like density.
515 ResTable_config config;
Narayan Kamath788fa412014-01-21 15:32:36 +0000516 memset(&config, 0, sizeof(ResTable_config));
Dianne Hackborne289bff2011-06-13 19:33:22 -0700517 config.language[0] = 'e';
518 config.language[1] = 'n';
519 config.country[0] = 'U';
520 config.country[1] = 'S';
521 config.orientation = ResTable_config::ORIENTATION_PORT;
522 config.density = ResTable_config::DENSITY_MEDIUM;
523 config.sdkVersion = 10000; // Very high.
524 config.screenWidthDp = 320;
525 config.screenHeightDp = 480;
526 config.smallestScreenWidthDp = 320;
527 assets.setConfiguration(config);
528
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800529 const ResTable& res = assets.getResources(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800530 if (strcmp("resources", option) == 0) {
Steve Blockf1ff21a2010-06-14 17:34:04 +0100531#ifndef HAVE_ANDROID_OS
Dianne Hackborne17086b2009-06-19 15:13:28 -0700532 res.print(bundle->getValues());
Steve Blockf1ff21a2010-06-14 17:34:04 +0100533#endif
Dianne Hackborn6c997a92012-01-31 11:27:43 -0800534
535 } else if (strcmp("strings", option) == 0) {
536 const ResStringPool* pool = res.getTableStringBlock(0);
537 printStringPool(pool);
538
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800539 } else if (strcmp("xmltree", option) == 0) {
540 if (bundle->getFileSpecCount() < 3) {
541 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
542 goto bail;
543 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700544
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800545 for (int i=2; i<bundle->getFileSpecCount(); i++) {
546 const char* resname = bundle->getFileSpecEntry(i);
547 ResXMLTree tree;
548 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
549 if (asset == NULL) {
Kenny Root44b283d2009-09-01 19:03:11 -0500550 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800551 goto bail;
552 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700553
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800554 if (tree.setTo(asset->getBuffer(true),
555 asset->getLength()) != NO_ERROR) {
556 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
557 goto bail;
558 }
559 tree.restart();
560 printXMLBlock(&tree);
Kenny Root19138462009-12-04 09:38:48 -0800561 tree.uninit();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800562 delete asset;
563 asset = NULL;
564 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700565
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800566 } else if (strcmp("xmlstrings", option) == 0) {
567 if (bundle->getFileSpecCount() < 3) {
568 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
569 goto bail;
570 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700571
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800572 for (int i=2; i<bundle->getFileSpecCount(); i++) {
573 const char* resname = bundle->getFileSpecEntry(i);
574 ResXMLTree tree;
575 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
576 if (asset == NULL) {
Kenny Root44b283d2009-09-01 19:03:11 -0500577 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800578 goto bail;
579 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700580
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800581 if (tree.setTo(asset->getBuffer(true),
582 asset->getLength()) != NO_ERROR) {
583 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
584 goto bail;
585 }
586 printStringPool(&tree.getStrings());
587 delete asset;
588 asset = NULL;
589 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700590
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800591 } else {
592 ResXMLTree tree;
593 asset = assets.openNonAsset("AndroidManifest.xml",
594 Asset::ACCESS_BUFFER);
595 if (asset == NULL) {
596 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
597 goto bail;
598 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700599
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800600 if (tree.setTo(asset->getBuffer(true),
601 asset->getLength()) != NO_ERROR) {
602 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
603 goto bail;
604 }
605 tree.restart();
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700606
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800607 if (strcmp("permissions", option) == 0) {
608 size_t len;
609 ResXMLTree::event_code_t code;
610 int depth = 0;
611 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
612 if (code == ResXMLTree::END_TAG) {
613 depth--;
614 continue;
615 }
616 if (code != ResXMLTree::START_TAG) {
617 continue;
618 }
619 depth++;
620 String8 tag(tree.getElementName(&len));
621 //printf("Depth %d tag %s\n", depth, tag.string());
622 if (depth == 1) {
623 if (tag != "manifest") {
624 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
625 goto bail;
626 }
627 String8 pkg = getAttribute(tree, NULL, "package", NULL);
628 printf("package: %s\n", pkg.string());
629 } else if (depth == 2 && tag == "permission") {
630 String8 error;
631 String8 name = getAttribute(tree, NAME_ATTR, &error);
632 if (error != "") {
633 fprintf(stderr, "ERROR: %s\n", error.string());
634 goto bail;
635 }
636 printf("permission: %s\n", name.string());
637 } else if (depth == 2 && tag == "uses-permission") {
638 String8 error;
639 String8 name = getAttribute(tree, NAME_ATTR, &error);
640 if (error != "") {
641 fprintf(stderr, "ERROR: %s\n", error.string());
642 goto bail;
643 }
644 printf("uses-permission: %s\n", name.string());
Nick Kralevich1bcc3d62013-04-03 09:00:02 -0700645 int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
646 if (!req) {
647 printf("optional-permission: %s\n", name.string());
648 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800649 }
650 }
651 } else if (strcmp("badging", option) == 0) {
Dianne Hackborne289bff2011-06-13 19:33:22 -0700652 Vector<String8> locales;
653 res.getLocales(&locales);
654
655 Vector<ResTable_config> configs;
656 res.getConfigurations(&configs);
657 SortedVector<int> densities;
658 const size_t NC = configs.size();
659 for (size_t i=0; i<NC; i++) {
660 int dens = configs[i].density;
661 if (dens == 0) dens = 160;
662 densities.add(dens);
663 }
664
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800665 size_t len;
666 ResXMLTree::event_code_t code;
667 int depth = 0;
668 String8 error;
669 bool withinActivity = false;
670 bool isMainActivity = false;
671 bool isLauncherActivity = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700672 bool isSearchable = false;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700673 bool withinApplication = false;
Michael Wrighteaeb1902013-09-05 18:15:57 -0700674 bool withinSupportsInput = false;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700675 bool withinReceiver = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700676 bool withinService = false;
677 bool withinIntentFilter = false;
678 bool hasMainActivity = false;
679 bool hasOtherActivities = false;
680 bool hasOtherReceivers = false;
681 bool hasOtherServices = false;
682 bool hasWallpaperService = false;
683 bool hasImeService = false;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700684 bool hasAccessibilityService = false;
685 bool hasPrintService = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700686 bool hasWidgetReceivers = false;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700687 bool hasDeviceAdminReceiver = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700688 bool hasIntentFilter = false;
Adam Lesinskid831e802013-09-27 11:14:57 -0700689 bool hasPaymentService = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700690 bool actMainActivity = false;
691 bool actWidgetReceivers = false;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700692 bool actDeviceAdminEnabled = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700693 bool actImeService = false;
694 bool actWallpaperService = false;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700695 bool actAccessibilityService = false;
696 bool actPrintService = false;
Adam Lesinskid831e802013-09-27 11:14:57 -0700697 bool actHostApduService = false;
698 bool actOffHostApduService = false;
699 bool hasMetaHostPaymentCategory = false;
700 bool hasMetaOffHostPaymentCategory = false;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700701
702 // These permissions are required by services implementing services
703 // the system binds to (IME, Accessibility, PrintServices, etc.)
704 bool hasBindDeviceAdminPermission = false;
705 bool hasBindInputMethodPermission = false;
706 bool hasBindAccessibilityServicePermission = false;
707 bool hasBindPrintServicePermission = false;
Adam Lesinskid831e802013-09-27 11:14:57 -0700708 bool hasBindNfcServicePermission = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700709
Kenny Root063a44e2011-12-08 08:46:03 -0800710 // These two implement the implicit permissions that are granted
711 // to pre-1.6 applications.
712 bool hasWriteExternalStoragePermission = false;
713 bool hasReadPhoneStatePermission = false;
714
Dianne Hackborn79245122012-03-12 10:51:26 -0700715 // If an app requests write storage, they will also get read storage.
716 bool hasReadExternalStoragePermission = false;
717
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -0700718 // Implement transition to read and write call log.
719 bool hasReadContactsPermission = false;
720 bool hasWriteContactsPermission = false;
721 bool hasReadCallLogPermission = false;
722 bool hasWriteCallLogPermission = false;
723
Dan Morrill89d97c12010-05-03 16:13:14 -0700724 // This next group of variables is used to implement a group of
725 // backward-compatibility heuristics necessitated by the addition of
726 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
727 // heuristic is "if an app requests a permission but doesn't explicitly
728 // request the corresponding <uses-feature>, presume it's there anyway".
729 bool specCameraFeature = false; // camera-related
730 bool specCameraAutofocusFeature = false;
731 bool reqCameraAutofocusFeature = false;
732 bool reqCameraFlashFeature = false;
Dianne Hackborne5276a72009-08-27 16:28:44 -0700733 bool hasCameraPermission = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700734 bool specLocationFeature = false; // location-related
735 bool specNetworkLocFeature = false;
736 bool reqNetworkLocFeature = false;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800737 bool specGpsFeature = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700738 bool reqGpsFeature = false;
739 bool hasMockLocPermission = false;
740 bool hasCoarseLocPermission = false;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800741 bool hasGpsPermission = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700742 bool hasGeneralLocPermission = false;
743 bool specBluetoothFeature = false; // Bluetooth API-related
744 bool hasBluetoothPermission = false;
745 bool specMicrophoneFeature = false; // microphone-related
746 bool hasRecordAudioPermission = false;
747 bool specWiFiFeature = false;
748 bool hasWiFiPermission = false;
749 bool specTelephonyFeature = false; // telephony-related
750 bool reqTelephonySubFeature = false;
751 bool hasTelephonyPermission = false;
752 bool specTouchscreenFeature = false; // touchscreen-related
753 bool specMultitouchFeature = false;
754 bool reqDistinctMultitouchFeature = false;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700755 bool specScreenPortraitFeature = false;
756 bool specScreenLandscapeFeature = false;
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -0700757 bool reqScreenPortraitFeature = false;
758 bool reqScreenLandscapeFeature = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700759 // 2.2 also added some other features that apps can request, but that
760 // have no corresponding permission, so we cannot implement any
761 // back-compatibility heuristic for them. The below are thus unnecessary
762 // (but are retained here for documentary purposes.)
763 //bool specCompassFeature = false;
764 //bool specAccelerometerFeature = false;
765 //bool specProximityFeature = false;
766 //bool specAmbientLightFeature = false;
767 //bool specLiveWallpaperFeature = false;
768
Dianne Hackborn723738c2009-06-25 19:48:04 -0700769 int targetSdk = 0;
770 int smallScreen = 1;
771 int normalScreen = 1;
772 int largeScreen = 1;
Dianne Hackbornf43489d2010-08-20 12:44:33 -0700773 int xlargeScreen = 1;
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700774 int anyDensity = 1;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700775 int requiresSmallestWidthDp = 0;
776 int compatibleWidthLimitDp = 0;
777 int largestWidthLimitDp = 0;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700778 String8 pkg;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800779 String8 activityName;
780 String8 activityLabel;
781 String8 activityIcon;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700782 String8 receiverName;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700783 String8 serviceName;
Michael Wrighteaeb1902013-09-05 18:15:57 -0700784 Vector<String8> supportedInput;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800785 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
786 if (code == ResXMLTree::END_TAG) {
787 depth--;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700788 if (depth < 2) {
Michael Wrighteaeb1902013-09-05 18:15:57 -0700789 if (withinSupportsInput && !supportedInput.isEmpty()) {
790 printf("supports-input: '");
791 const size_t N = supportedInput.size();
792 for (size_t i=0; i<N; i++) {
793 printf("%s", supportedInput[i].string());
794 if (i != N - 1) {
795 printf("' '");
796 } else {
797 printf("'\n");
798 }
799 }
800 supportedInput.clear();
801 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700802 withinApplication = false;
Michael Wrighteaeb1902013-09-05 18:15:57 -0700803 withinSupportsInput = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700804 } else if (depth < 3) {
805 if (withinActivity && isMainActivity && isLauncherActivity) {
806 const char *aName = getComponentName(pkg, activityName);
Dianne Hackborne289bff2011-06-13 19:33:22 -0700807 printf("launchable-activity:");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700808 if (aName != NULL) {
Dianne Hackborne289bff2011-06-13 19:33:22 -0700809 printf(" name='%s' ", aName);
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700810 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700811 printf(" label='%s' icon='%s'\n",
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700812 activityLabel.string(),
813 activityIcon.string());
814 }
815 if (!hasIntentFilter) {
816 hasOtherActivities |= withinActivity;
817 hasOtherReceivers |= withinReceiver;
818 hasOtherServices |= withinService;
Adam Lesinskid831e802013-09-27 11:14:57 -0700819 } else {
820 if (withinService) {
821 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
822 hasBindNfcServicePermission);
823 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
824 hasBindNfcServicePermission);
825 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700826 }
827 withinActivity = false;
828 withinService = false;
829 withinReceiver = false;
830 hasIntentFilter = false;
831 isMainActivity = isLauncherActivity = false;
832 } else if (depth < 4) {
833 if (withinIntentFilter) {
834 if (withinActivity) {
835 hasMainActivity |= actMainActivity;
836 hasOtherActivities |= !actMainActivity;
837 } else if (withinReceiver) {
838 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700839 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
840 hasBindDeviceAdminPermission);
841 hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled);
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700842 } else if (withinService) {
843 hasImeService |= actImeService;
844 hasWallpaperService |= actWallpaperService;
Adam Lesinskib1249b82013-09-25 17:03:50 -0700845 hasAccessibilityService |= (actAccessibilityService &&
846 hasBindAccessibilityServicePermission);
847 hasPrintService |= (actPrintService && hasBindPrintServicePermission);
848 hasOtherServices |= (!actImeService && !actWallpaperService &&
Adam Lesinskid831e802013-09-27 11:14:57 -0700849 !actAccessibilityService && !actPrintService &&
850 !actHostApduService && !actOffHostApduService);
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700851 }
852 }
853 withinIntentFilter = false;
854 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800855 continue;
856 }
857 if (code != ResXMLTree::START_TAG) {
858 continue;
859 }
860 depth++;
861 String8 tag(tree.getElementName(&len));
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700862 //printf("Depth %d, %s\n", depth, tag.string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800863 if (depth == 1) {
864 if (tag != "manifest") {
865 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
866 goto bail;
867 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700868 pkg = getAttribute(tree, NULL, "package", NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800869 printf("package: name='%s' ", pkg.string());
870 int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
871 if (error != "") {
872 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
873 goto bail;
874 }
875 if (versionCode > 0) {
876 printf("versionCode='%d' ", versionCode);
877 } else {
878 printf("versionCode='' ");
879 }
Dianne Hackborncf244ad2010-03-09 15:00:30 -0800880 String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800881 if (error != "") {
882 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
883 goto bail;
884 }
885 printf("versionName='%s'\n", versionName.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700886 } else if (depth == 2) {
887 withinApplication = false;
888 if (tag == "application") {
889 withinApplication = true;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700890
891 String8 label;
892 const size_t NL = locales.size();
893 for (size_t i=0; i<NL; i++) {
894 const char* localeStr = locales[i].string();
895 assets.setLocale(localeStr != NULL ? localeStr : "");
896 String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
897 if (llabel != "") {
898 if (localeStr == NULL || strlen(localeStr) == 0) {
899 label = llabel;
900 printf("application-label:'%s'\n", llabel.string());
901 } else {
902 if (label == "") {
903 label = llabel;
904 }
905 printf("application-label-%s:'%s'\n", localeStr,
906 llabel.string());
907 }
908 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700909 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700910
911 ResTable_config tmpConfig = config;
912 const size_t ND = densities.size();
913 for (size_t i=0; i<ND; i++) {
914 tmpConfig.density = densities[i];
915 assets.setConfiguration(tmpConfig);
916 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
917 if (icon != "") {
918 printf("application-icon-%d:'%s'\n", densities[i], icon.string());
919 }
920 }
921 assets.setConfiguration(config);
922
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700923 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
924 if (error != "") {
925 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
926 goto bail;
927 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700928 int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700929 if (error != "") {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700930 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700931 goto bail;
932 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700933 printf("application: label='%s' ", label.string());
934 printf("icon='%s'\n", icon.string());
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700935 if (testOnly != 0) {
936 printf("testOnly='%d'\n", testOnly);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700937 }
Dan Morrillb6ec11e2012-04-03 12:44:40 -0700938
939 int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0);
940 if (error != "") {
941 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string());
942 goto bail;
943 }
944 if (debuggable != 0) {
945 printf("application-debuggable\n");
946 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700947 } else if (tag == "uses-sdk") {
948 int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
949 if (error != "") {
950 error = "";
951 String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
952 if (error != "") {
953 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
954 error.string());
955 goto bail;
956 }
Dianne Hackborn723738c2009-06-25 19:48:04 -0700957 if (name == "Donut") targetSdk = 4;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700958 printf("sdkVersion:'%s'\n", name.string());
959 } else if (code != -1) {
Dianne Hackborn723738c2009-06-25 19:48:04 -0700960 targetSdk = code;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700961 printf("sdkVersion:'%d'\n", code);
962 }
Suchi Amalapurapu75c49842009-08-14 15:13:09 -0700963 code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
964 if (code != -1) {
965 printf("maxSdkVersion:'%d'\n", code);
966 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700967 code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
968 if (error != "") {
969 error = "";
970 String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
971 if (error != "") {
972 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
973 error.string());
974 goto bail;
975 }
Dianne Hackborn723738c2009-06-25 19:48:04 -0700976 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700977 printf("targetSdkVersion:'%s'\n", name.string());
978 } else if (code != -1) {
Dianne Hackborn723738c2009-06-25 19:48:04 -0700979 if (targetSdk < code) {
980 targetSdk = code;
981 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700982 printf("targetSdkVersion:'%d'\n", code);
983 }
984 } else if (tag == "uses-configuration") {
985 int32_t reqTouchScreen = getIntegerAttribute(tree,
986 REQ_TOUCH_SCREEN_ATTR, NULL, 0);
987 int32_t reqKeyboardType = getIntegerAttribute(tree,
988 REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
989 int32_t reqHardKeyboard = getIntegerAttribute(tree,
990 REQ_HARD_KEYBOARD_ATTR, NULL, 0);
991 int32_t reqNavigation = getIntegerAttribute(tree,
992 REQ_NAVIGATION_ATTR, NULL, 0);
993 int32_t reqFiveWayNav = getIntegerAttribute(tree,
994 REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
Dianne Hackborncb2d50d2010-01-06 11:29:54 -0800995 printf("uses-configuration:");
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700996 if (reqTouchScreen != 0) {
997 printf(" reqTouchScreen='%d'", reqTouchScreen);
998 }
999 if (reqKeyboardType != 0) {
1000 printf(" reqKeyboardType='%d'", reqKeyboardType);
1001 }
1002 if (reqHardKeyboard != 0) {
1003 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1004 }
1005 if (reqNavigation != 0) {
1006 printf(" reqNavigation='%d'", reqNavigation);
1007 }
1008 if (reqFiveWayNav != 0) {
1009 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1010 }
1011 printf("\n");
Michael Wrighteaeb1902013-09-05 18:15:57 -07001012 } else if (tag == "supports-input") {
1013 withinSupportsInput = true;
Dianne Hackborn723738c2009-06-25 19:48:04 -07001014 } else if (tag == "supports-screens") {
1015 smallScreen = getIntegerAttribute(tree,
1016 SMALL_SCREEN_ATTR, NULL, 1);
1017 normalScreen = getIntegerAttribute(tree,
1018 NORMAL_SCREEN_ATTR, NULL, 1);
1019 largeScreen = getIntegerAttribute(tree,
1020 LARGE_SCREEN_ATTR, NULL, 1);
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001021 xlargeScreen = getIntegerAttribute(tree,
1022 XLARGE_SCREEN_ATTR, NULL, 1);
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001023 anyDensity = getIntegerAttribute(tree,
1024 ANY_DENSITY_ATTR, NULL, 1);
Dianne Hackborne289bff2011-06-13 19:33:22 -07001025 requiresSmallestWidthDp = getIntegerAttribute(tree,
1026 REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
1027 compatibleWidthLimitDp = getIntegerAttribute(tree,
1028 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
1029 largestWidthLimitDp = getIntegerAttribute(tree,
1030 LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
Dianne Hackborne5276a72009-08-27 16:28:44 -07001031 } else if (tag == "uses-feature") {
1032 String8 name = getAttribute(tree, NAME_ATTR, &error);
Suchi Amalapurapu40b94722009-09-20 13:39:37 -07001033
1034 if (name != "" && error == "") {
Dianne Hackborne5276a72009-08-27 16:28:44 -07001035 int req = getIntegerAttribute(tree,
1036 REQUIRED_ATTR, NULL, 1);
Dan Morrill89d97c12010-05-03 16:13:14 -07001037
Dianne Hackborne5276a72009-08-27 16:28:44 -07001038 if (name == "android.hardware.camera") {
1039 specCameraFeature = true;
Dan Morrill89d97c12010-05-03 16:13:14 -07001040 } else if (name == "android.hardware.camera.autofocus") {
1041 // these have no corresponding permission to check for,
1042 // but should imply the foundational camera permission
1043 reqCameraAutofocusFeature = reqCameraAutofocusFeature || req;
1044 specCameraAutofocusFeature = true;
1045 } else if (req && (name == "android.hardware.camera.flash")) {
1046 // these have no corresponding permission to check for,
1047 // but should imply the foundational camera permission
1048 reqCameraFlashFeature = true;
1049 } else if (name == "android.hardware.location") {
1050 specLocationFeature = true;
1051 } else if (name == "android.hardware.location.network") {
1052 specNetworkLocFeature = true;
1053 reqNetworkLocFeature = reqNetworkLocFeature || req;
Dianne Hackbornef05e072010-03-01 17:43:39 -08001054 } else if (name == "android.hardware.location.gps") {
1055 specGpsFeature = true;
Dan Morrill89d97c12010-05-03 16:13:14 -07001056 reqGpsFeature = reqGpsFeature || req;
1057 } else if (name == "android.hardware.bluetooth") {
1058 specBluetoothFeature = true;
1059 } else if (name == "android.hardware.touchscreen") {
1060 specTouchscreenFeature = true;
1061 } else if (name == "android.hardware.touchscreen.multitouch") {
1062 specMultitouchFeature = true;
1063 } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
1064 reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req;
1065 } else if (name == "android.hardware.microphone") {
1066 specMicrophoneFeature = true;
1067 } else if (name == "android.hardware.wifi") {
1068 specWiFiFeature = true;
1069 } else if (name == "android.hardware.telephony") {
1070 specTelephonyFeature = true;
1071 } else if (req && (name == "android.hardware.telephony.gsm" ||
1072 name == "android.hardware.telephony.cdma")) {
1073 // these have no corresponding permission to check for,
1074 // but should imply the foundational telephony permission
1075 reqTelephonySubFeature = true;
Dianne Hackborne289bff2011-06-13 19:33:22 -07001076 } else if (name == "android.hardware.screen.portrait") {
1077 specScreenPortraitFeature = true;
1078 } else if (name == "android.hardware.screen.landscape") {
1079 specScreenLandscapeFeature = true;
Dianne Hackborne5276a72009-08-27 16:28:44 -07001080 }
1081 printf("uses-feature%s:'%s'\n",
1082 req ? "" : "-not-required", name.string());
1083 } else {
1084 int vers = getIntegerAttribute(tree,
1085 GL_ES_VERSION_ATTR, &error);
1086 if (error == "") {
1087 printf("uses-gl-es:'0x%x'\n", vers);
1088 }
1089 }
1090 } else if (tag == "uses-permission") {
1091 String8 name = getAttribute(tree, NAME_ATTR, &error);
Suchi Amalapurapu40b94722009-09-20 13:39:37 -07001092 if (name != "" && error == "") {
Dianne Hackborne5276a72009-08-27 16:28:44 -07001093 if (name == "android.permission.CAMERA") {
1094 hasCameraPermission = true;
Dianne Hackbornef05e072010-03-01 17:43:39 -08001095 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
1096 hasGpsPermission = true;
Dan Morrill89d97c12010-05-03 16:13:14 -07001097 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
1098 hasMockLocPermission = true;
1099 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
1100 hasCoarseLocPermission = true;
1101 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
1102 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
1103 hasGeneralLocPermission = true;
1104 } else if (name == "android.permission.BLUETOOTH" ||
1105 name == "android.permission.BLUETOOTH_ADMIN") {
1106 hasBluetoothPermission = true;
1107 } else if (name == "android.permission.RECORD_AUDIO") {
1108 hasRecordAudioPermission = true;
1109 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1110 name == "android.permission.CHANGE_WIFI_STATE" ||
1111 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
1112 hasWiFiPermission = true;
1113 } else if (name == "android.permission.CALL_PHONE" ||
1114 name == "android.permission.CALL_PRIVILEGED" ||
1115 name == "android.permission.MODIFY_PHONE_STATE" ||
1116 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1117 name == "android.permission.READ_SMS" ||
1118 name == "android.permission.RECEIVE_SMS" ||
1119 name == "android.permission.RECEIVE_MMS" ||
1120 name == "android.permission.RECEIVE_WAP_PUSH" ||
1121 name == "android.permission.SEND_SMS" ||
1122 name == "android.permission.WRITE_APN_SETTINGS" ||
1123 name == "android.permission.WRITE_SMS") {
1124 hasTelephonyPermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001125 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1126 hasWriteExternalStoragePermission = true;
Dianne Hackborn79245122012-03-12 10:51:26 -07001127 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1128 hasReadExternalStoragePermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001129 } else if (name == "android.permission.READ_PHONE_STATE") {
1130 hasReadPhoneStatePermission = true;
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001131 } else if (name == "android.permission.READ_CONTACTS") {
1132 hasReadContactsPermission = true;
1133 } else if (name == "android.permission.WRITE_CONTACTS") {
1134 hasWriteContactsPermission = true;
1135 } else if (name == "android.permission.READ_CALL_LOG") {
1136 hasReadCallLogPermission = true;
1137 } else if (name == "android.permission.WRITE_CALL_LOG") {
1138 hasWriteCallLogPermission = true;
Dianne Hackborne5276a72009-08-27 16:28:44 -07001139 }
1140 printf("uses-permission:'%s'\n", name.string());
Nick Kralevich1bcc3d62013-04-03 09:00:02 -07001141 int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
1142 if (!req) {
1143 printf("optional-permission:'%s'\n", name.string());
1144 }
Dianne Hackborne5276a72009-08-27 16:28:44 -07001145 } else {
1146 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1147 error.string());
1148 goto bail;
1149 }
Dianne Hackborn43b68032010-09-02 17:14:41 -07001150 } else if (tag == "uses-package") {
1151 String8 name = getAttribute(tree, NAME_ATTR, &error);
1152 if (name != "" && error == "") {
1153 printf("uses-package:'%s'\n", name.string());
1154 } else {
1155 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1156 error.string());
1157 goto bail;
1158 }
Jeff Hamiltone2c17f92010-02-12 13:45:16 -06001159 } else if (tag == "original-package") {
1160 String8 name = getAttribute(tree, NAME_ATTR, &error);
1161 if (name != "" && error == "") {
1162 printf("original-package:'%s'\n", name.string());
1163 } else {
1164 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1165 error.string());
1166 goto bail;
1167 }
Dan Morrill096b67f2010-12-13 16:25:54 -08001168 } else if (tag == "supports-gl-texture") {
Dan Morrill6f51fc12010-10-13 14:33:43 -07001169 String8 name = getAttribute(tree, NAME_ATTR, &error);
1170 if (name != "" && error == "") {
Dan Morrill096b67f2010-12-13 16:25:54 -08001171 printf("supports-gl-texture:'%s'\n", name.string());
Dan Morrill6f51fc12010-10-13 14:33:43 -07001172 } else {
1173 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1174 error.string());
1175 goto bail;
1176 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001177 } else if (tag == "compatible-screens") {
1178 printCompatibleScreens(tree);
1179 depth--;
Kenny Root56088a52011-09-29 13:49:45 -07001180 } else if (tag == "package-verifier") {
1181 String8 name = getAttribute(tree, NAME_ATTR, &error);
1182 if (name != "" && error == "") {
1183 String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
1184 if (publicKey != "" && error == "") {
1185 printf("package-verifier: name='%s' publicKey='%s'\n",
1186 name.string(), publicKey.string());
1187 }
1188 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001189 }
Michael Wrighteaeb1902013-09-05 18:15:57 -07001190 } else if (depth == 3) {
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001191 withinActivity = false;
1192 withinReceiver = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001193 withinService = false;
1194 hasIntentFilter = false;
Adam Lesinskid831e802013-09-27 11:14:57 -07001195 hasMetaHostPaymentCategory = false;
1196 hasMetaOffHostPaymentCategory = false;
1197 hasBindDeviceAdminPermission = false;
1198 hasBindInputMethodPermission = false;
1199 hasBindAccessibilityServicePermission = false;
1200 hasBindPrintServicePermission = false;
1201 hasBindNfcServicePermission = false;
Michael Wrighteaeb1902013-09-05 18:15:57 -07001202 if (withinApplication) {
1203 if(tag == "activity") {
1204 withinActivity = true;
1205 activityName = getAttribute(tree, NAME_ATTR, &error);
1206 if (error != "") {
1207 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1208 error.string());
1209 goto bail;
1210 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001211
Michael Wrighteaeb1902013-09-05 18:15:57 -07001212 activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1213 if (error != "") {
1214 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
1215 error.string());
1216 goto bail;
1217 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001218
Michael Wrighteaeb1902013-09-05 18:15:57 -07001219 activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1220 if (error != "") {
1221 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1222 error.string());
1223 goto bail;
1224 }
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001225
Michael Wrighteaeb1902013-09-05 18:15:57 -07001226 int32_t orien = getResolvedIntegerAttribute(&res, tree,
1227 SCREEN_ORIENTATION_ATTR, &error);
1228 if (error == "") {
1229 if (orien == 0 || orien == 6 || orien == 8) {
1230 // Requests landscape, sensorLandscape, or reverseLandscape.
1231 reqScreenLandscapeFeature = true;
1232 } else if (orien == 1 || orien == 7 || orien == 9) {
1233 // Requests portrait, sensorPortrait, or reversePortrait.
1234 reqScreenPortraitFeature = true;
1235 }
1236 }
1237 } else if (tag == "uses-library") {
1238 String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1239 if (error != "") {
1240 fprintf(stderr,
1241 "ERROR getting 'android:name' attribute for uses-library"
1242 " %s\n", error.string());
1243 goto bail;
1244 }
1245 int req = getIntegerAttribute(tree,
1246 REQUIRED_ATTR, NULL, 1);
1247 printf("uses-library%s:'%s'\n",
1248 req ? "" : "-not-required", libraryName.string());
1249 } else if (tag == "receiver") {
1250 withinReceiver = true;
1251 receiverName = getAttribute(tree, NAME_ATTR, &error);
1252
1253 if (error != "") {
1254 fprintf(stderr,
1255 "ERROR getting 'android:name' attribute for receiver:"
1256 " %s\n", error.string());
1257 goto bail;
1258 }
Adam Lesinskib1249b82013-09-25 17:03:50 -07001259
1260 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1261 if (error == "") {
1262 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1263 hasBindDeviceAdminPermission = true;
1264 }
1265 } else {
1266 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1267 " receiver '%s': %s\n", receiverName.string(), error.string());
1268 }
Michael Wrighteaeb1902013-09-05 18:15:57 -07001269 } else if (tag == "service") {
1270 withinService = true;
1271 serviceName = getAttribute(tree, NAME_ATTR, &error);
1272
1273 if (error != "") {
1274 fprintf(stderr, "ERROR getting 'android:name' attribute for"
1275 " service: %s\n", error.string());
1276 goto bail;
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001277 }
Adam Lesinskib1249b82013-09-25 17:03:50 -07001278
1279 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1280 if (error == "") {
1281 if (permission == "android.permission.BIND_INPUT_METHOD") {
1282 hasBindInputMethodPermission = true;
1283 } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") {
1284 hasBindAccessibilityServicePermission = true;
1285 } else if (permission == "android.permission.BIND_PRINT_SERVICE") {
1286 hasBindPrintServicePermission = true;
Adam Lesinskid831e802013-09-27 11:14:57 -07001287 } else if (permission == "android.permission.BIND_NFC_SERVICE") {
1288 hasBindNfcServicePermission = true;
Adam Lesinskib1249b82013-09-25 17:03:50 -07001289 }
1290 } else {
1291 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1292 " service '%s': %s\n", serviceName.string(), error.string());
1293 }
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001294 }
Michael Wrighteaeb1902013-09-05 18:15:57 -07001295 } else if (withinSupportsInput && tag == "input-type") {
1296 String8 name = getAttribute(tree, NAME_ATTR, &error);
1297 if (name != "" && error == "") {
1298 supportedInput.add(name);
1299 } else {
1300 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1301 error.string());
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001302 goto bail;
1303 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001304 }
Adam Lesinskid831e802013-09-27 11:14:57 -07001305 } else if (depth == 4) {
1306 if (tag == "intent-filter") {
1307 hasIntentFilter = true;
1308 withinIntentFilter = true;
1309 actMainActivity = false;
1310 actWidgetReceivers = false;
1311 actImeService = false;
1312 actWallpaperService = false;
1313 actAccessibilityService = false;
1314 actPrintService = false;
1315 actDeviceAdminEnabled = false;
1316 actHostApduService = false;
1317 actOffHostApduService = false;
1318 } else if (withinService && tag == "meta-data") {
1319 String8 name = getAttribute(tree, NAME_ATTR, &error);
1320 if (error != "") {
1321 fprintf(stderr, "ERROR getting 'android:name' attribute for"
1322 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1323 goto bail;
1324 }
1325
1326 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1327 name == "android.nfc.cardemulation.off_host_apdu_service") {
1328 bool offHost = true;
1329 if (name == "android.nfc.cardemulation.host_apdu_service") {
1330 offHost = false;
1331 }
1332
1333 String8 xmlPath = getResolvedAttribute(&res, tree, RESOURCE_ATTR, &error);
1334 if (error != "") {
1335 fprintf(stderr, "ERROR getting 'android:resource' attribute for"
1336 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1337 goto bail;
1338 }
1339
1340 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1341 offHost, &error);
1342 if (error != "") {
1343 fprintf(stderr, "ERROR getting AID category for service '%s'\n",
1344 serviceName.string());
1345 goto bail;
1346 }
1347
1348 const size_t catLen = categories.size();
1349 for (size_t i = 0; i < catLen; i++) {
1350 bool paymentCategory = (categories[i] == "payment");
1351 if (offHost) {
1352 hasMetaOffHostPaymentCategory |= paymentCategory;
1353 } else {
1354 hasMetaHostPaymentCategory |= paymentCategory;
1355 }
1356 }
1357 }
1358 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001359 } else if ((depth == 5) && withinIntentFilter){
1360 String8 action;
1361 if (tag == "action") {
1362 action = getAttribute(tree, NAME_ATTR, &error);
1363 if (error != "") {
1364 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1365 goto bail;
1366 }
1367 if (withinActivity) {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001368 if (action == "android.intent.action.MAIN") {
1369 isMainActivity = true;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001370 actMainActivity = true;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001371 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001372 } else if (withinReceiver) {
1373 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1374 actWidgetReceivers = true;
Adam Lesinskib1249b82013-09-25 17:03:50 -07001375 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1376 actDeviceAdminEnabled = true;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001377 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001378 } else if (withinService) {
1379 if (action == "android.view.InputMethod") {
1380 actImeService = true;
1381 } else if (action == "android.service.wallpaper.WallpaperService") {
1382 actWallpaperService = true;
Adam Lesinskib1249b82013-09-25 17:03:50 -07001383 } else if (action == "android.accessibilityservice.AccessibilityService") {
1384 actAccessibilityService = true;
1385 } else if (action == "android.printservice.PrintService") {
1386 actPrintService = true;
Adam Lesinskid831e802013-09-27 11:14:57 -07001387 } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
1388 actHostApduService = true;
1389 } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
1390 actOffHostApduService = true;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001391 }
1392 }
1393 if (action == "android.intent.action.SEARCH") {
1394 isSearchable = true;
1395 }
1396 }
1397
1398 if (tag == "category") {
1399 String8 category = getAttribute(tree, NAME_ATTR, &error);
1400 if (error != "") {
1401 fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string());
1402 goto bail;
1403 }
1404 if (withinActivity) {
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001405 if (category == "android.intent.category.LAUNCHER") {
1406 isLauncherActivity = true;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001407 }
1408 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001409 }
1410 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001411 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001412
Kenny Root063a44e2011-12-08 08:46:03 -08001413 // Pre-1.6 implicitly granted permission compatibility logic
1414 if (targetSdk < 4) {
1415 if (!hasWriteExternalStoragePermission) {
1416 printf("uses-permission:'android.permission.WRITE_EXTERNAL_STORAGE'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001417 printf("uses-implied-permission:'android.permission.WRITE_EXTERNAL_STORAGE'," \
1418 "'targetSdkVersion < 4'\n");
Dianne Hackborn79245122012-03-12 10:51:26 -07001419 hasWriteExternalStoragePermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001420 }
1421 if (!hasReadPhoneStatePermission) {
1422 printf("uses-permission:'android.permission.READ_PHONE_STATE'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001423 printf("uses-implied-permission:'android.permission.READ_PHONE_STATE'," \
1424 "'targetSdkVersion < 4'\n");
Kenny Root063a44e2011-12-08 08:46:03 -08001425 }
1426 }
1427
Dianne Hackborn79245122012-03-12 10:51:26 -07001428 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
Dianne Hackborn2bd8d042012-06-11 12:27:05 -07001429 // force them to always take READ_EXTERNAL_STORAGE as well. We always
1430 // do this (regardless of target API version) because we can't have
1431 // an app with write permission but not read permission.
Dianne Hackborn79245122012-03-12 10:51:26 -07001432 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
1433 printf("uses-permission:'android.permission.READ_EXTERNAL_STORAGE'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001434 printf("uses-implied-permission:'android.permission.READ_EXTERNAL_STORAGE'," \
1435 "'requested WRITE_EXTERNAL_STORAGE'\n");
Dianne Hackborn79245122012-03-12 10:51:26 -07001436 }
1437
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001438 // Pre-JellyBean call log permission compatibility.
1439 if (targetSdk < 16) {
1440 if (!hasReadCallLogPermission && hasReadContactsPermission) {
1441 printf("uses-permission:'android.permission.READ_CALL_LOG'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001442 printf("uses-implied-permission:'android.permission.READ_CALL_LOG'," \
1443 "'targetSdkVersion < 16 and requested READ_CONTACTS'\n");
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001444 }
1445 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
1446 printf("uses-permission:'android.permission.WRITE_CALL_LOG'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001447 printf("uses-implied-permission:'android.permission.WRITE_CALL_LOG'," \
1448 "'targetSdkVersion < 16 and requested WRITE_CONTACTS'\n");
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001449 }
1450 }
1451
Dan Morrill89d97c12010-05-03 16:13:14 -07001452 /* The following blocks handle printing "inferred" uses-features, based
1453 * on whether related features or permissions are used by the app.
1454 * Note that the various spec*Feature variables denote whether the
1455 * relevant tag was *present* in the AndroidManfest, not that it was
1456 * present and set to true.
1457 */
1458 // Camera-related back-compatibility logic
1459 if (!specCameraFeature) {
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001460 if (reqCameraFlashFeature) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001461 // if app requested a sub-feature (autofocus or flash) and didn't
1462 // request the base camera feature, we infer that it meant to
1463 printf("uses-feature:'android.hardware.camera'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001464 printf("uses-implied-feature:'android.hardware.camera'," \
1465 "'requested android.hardware.camera.flash feature'\n");
1466 } else if (reqCameraAutofocusFeature) {
1467 // if app requested a sub-feature (autofocus or flash) and didn't
1468 // request the base camera feature, we infer that it meant to
1469 printf("uses-feature:'android.hardware.camera'\n");
1470 printf("uses-implied-feature:'android.hardware.camera'," \
1471 "'requested android.hardware.camera.autofocus feature'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001472 } else if (hasCameraPermission) {
1473 // if app wants to use camera but didn't request the feature, we infer
1474 // that it meant to, and further that it wants autofocus
1475 // (which was the 1.0 - 1.5 behavior)
1476 printf("uses-feature:'android.hardware.camera'\n");
1477 if (!specCameraAutofocusFeature) {
1478 printf("uses-feature:'android.hardware.camera.autofocus'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001479 printf("uses-implied-feature:'android.hardware.camera.autofocus'," \
1480 "'requested android.permission.CAMERA permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001481 }
1482 }
Dianne Hackborne5276a72009-08-27 16:28:44 -07001483 }
Doug Zongkerdbe7a682009-10-09 11:24:51 -07001484
Dan Morrill89d97c12010-05-03 16:13:14 -07001485 // Location-related back-compatibility logic
1486 if (!specLocationFeature &&
1487 (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission ||
1488 hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) {
1489 // if app either takes a location-related permission or requests one of the
1490 // sub-features, we infer that it also meant to request the base location feature
1491 printf("uses-feature:'android.hardware.location'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001492 printf("uses-implied-feature:'android.hardware.location'," \
1493 "'requested a location access permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001494 }
Dianne Hackbornef05e072010-03-01 17:43:39 -08001495 if (!specGpsFeature && hasGpsPermission) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001496 // if app takes GPS (FINE location) perm but does not request the GPS
1497 // feature, we infer that it meant to
Dianne Hackbornef05e072010-03-01 17:43:39 -08001498 printf("uses-feature:'android.hardware.location.gps'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001499 printf("uses-implied-feature:'android.hardware.location.gps'," \
1500 "'requested android.permission.ACCESS_FINE_LOCATION permission'\n");
Dianne Hackbornef05e072010-03-01 17:43:39 -08001501 }
Dan Morrill89d97c12010-05-03 16:13:14 -07001502 if (!specNetworkLocFeature && hasCoarseLocPermission) {
1503 // if app takes Network location (COARSE location) perm but does not request the
1504 // network location feature, we infer that it meant to
1505 printf("uses-feature:'android.hardware.location.network'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001506 printf("uses-implied-feature:'android.hardware.location.network'," \
Dianne Hackbornec559ff2013-02-25 15:42:07 -08001507 "'requested android.permission.ACCESS_COARSE_LOCATION permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001508 }
1509
1510 // Bluetooth-related compatibility logic
Dan Morrill6b22d812010-06-15 21:41:42 -07001511 if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001512 // if app takes a Bluetooth permission but does not request the Bluetooth
1513 // feature, we infer that it meant to
1514 printf("uses-feature:'android.hardware.bluetooth'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001515 printf("uses-implied-feature:'android.hardware.bluetooth'," \
1516 "'requested android.permission.BLUETOOTH or android.permission.BLUETOOTH_ADMIN " \
1517 "permission and targetSdkVersion > 4'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001518 }
1519
1520 // Microphone-related compatibility logic
1521 if (!specMicrophoneFeature && hasRecordAudioPermission) {
1522 // if app takes the record-audio permission but does not request the microphone
1523 // feature, we infer that it meant to
1524 printf("uses-feature:'android.hardware.microphone'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001525 printf("uses-implied-feature:'android.hardware.microphone'," \
1526 "'requested android.permission.RECORD_AUDIO permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001527 }
1528
1529 // WiFi-related compatibility logic
1530 if (!specWiFiFeature && hasWiFiPermission) {
1531 // if app takes one of the WiFi permissions but does not request the WiFi
1532 // feature, we infer that it meant to
1533 printf("uses-feature:'android.hardware.wifi'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001534 printf("uses-implied-feature:'android.hardware.wifi'," \
1535 "'requested android.permission.ACCESS_WIFI_STATE, " \
1536 "android.permission.CHANGE_WIFI_STATE, or " \
1537 "android.permission.CHANGE_WIFI_MULTICAST_STATE permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001538 }
1539
1540 // Telephony-related compatibility logic
1541 if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) {
1542 // if app takes one of the telephony permissions or requests a sub-feature but
1543 // does not request the base telephony feature, we infer that it meant to
1544 printf("uses-feature:'android.hardware.telephony'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001545 printf("uses-implied-feature:'android.hardware.telephony'," \
1546 "'requested a telephony-related permission or feature'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001547 }
1548
1549 // Touchscreen-related back-compatibility logic
1550 if (!specTouchscreenFeature) { // not a typo!
1551 // all apps are presumed to require a touchscreen, unless they explicitly say
1552 // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
1553 // Note that specTouchscreenFeature is true if the tag is present, regardless
1554 // of whether its value is true or false, so this is safe
1555 printf("uses-feature:'android.hardware.touchscreen'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001556 printf("uses-implied-feature:'android.hardware.touchscreen'," \
1557 "'assumed you require a touch screen unless explicitly made optional'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001558 }
1559 if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
1560 // if app takes one of the telephony permissions or requests a sub-feature but
1561 // does not request the base telephony feature, we infer that it meant to
1562 printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001563 printf("uses-implied-feature:'android.hardware.touchscreen.multitouch'," \
1564 "'requested android.hardware.touchscreen.multitouch.distinct feature'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001565 }
Dianne Hackbornef05e072010-03-01 17:43:39 -08001566
Dianne Hackborne289bff2011-06-13 19:33:22 -07001567 // Landscape/portrait-related compatibility logic
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001568 if (!specScreenLandscapeFeature && !specScreenPortraitFeature) {
1569 // If the app has specified any activities in its manifest
1570 // that request a specific orientation, then assume that
1571 // orientation is required.
1572 if (reqScreenLandscapeFeature) {
1573 printf("uses-feature:'android.hardware.screen.landscape'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001574 printf("uses-implied-feature:'android.hardware.screen.landscape'," \
1575 "'one or more activities have specified a landscape orientation'\n");
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001576 }
1577 if (reqScreenPortraitFeature) {
1578 printf("uses-feature:'android.hardware.screen.portrait'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001579 printf("uses-implied-feature:'android.hardware.screen.portrait'," \
1580 "'one or more activities have specified a portrait orientation'\n");
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001581 }
Dianne Hackborne289bff2011-06-13 19:33:22 -07001582 }
1583
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001584 if (hasMainActivity) {
1585 printf("main\n");
1586 }
1587 if (hasWidgetReceivers) {
1588 printf("app-widget\n");
1589 }
Adam Lesinskib1249b82013-09-25 17:03:50 -07001590 if (hasDeviceAdminReceiver) {
1591 printf("device-admin\n");
1592 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001593 if (hasImeService) {
1594 printf("ime\n");
1595 }
1596 if (hasWallpaperService) {
1597 printf("wallpaper\n");
1598 }
Adam Lesinskib1249b82013-09-25 17:03:50 -07001599 if (hasAccessibilityService) {
1600 printf("accessibility\n");
1601 }
1602 if (hasPrintService) {
1603 printf("print\n");
1604 }
Adam Lesinskid831e802013-09-27 11:14:57 -07001605 if (hasPaymentService) {
1606 printf("payment\n");
1607 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001608 if (hasOtherActivities) {
1609 printf("other-activities\n");
1610 }
1611 if (isSearchable) {
1612 printf("search\n");
1613 }
1614 if (hasOtherReceivers) {
1615 printf("other-receivers\n");
1616 }
1617 if (hasOtherServices) {
1618 printf("other-services\n");
1619 }
1620
Dianne Hackborne289bff2011-06-13 19:33:22 -07001621 // For modern apps, if screen size buckets haven't been specified
1622 // but the new width ranges have, then infer the buckets from them.
1623 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1624 && requiresSmallestWidthDp > 0) {
1625 int compatWidth = compatibleWidthLimitDp;
1626 if (compatWidth <= 0) compatWidth = requiresSmallestWidthDp;
1627 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1628 smallScreen = -1;
1629 } else {
1630 smallScreen = 0;
1631 }
1632 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1633 normalScreen = -1;
1634 } else {
1635 normalScreen = 0;
1636 }
1637 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1638 largeScreen = -1;
1639 } else {
1640 largeScreen = 0;
1641 }
1642 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1643 xlargeScreen = -1;
1644 } else {
1645 xlargeScreen = 0;
1646 }
1647 }
1648
Dianne Hackborn723738c2009-06-25 19:48:04 -07001649 // Determine default values for any unspecified screen sizes,
1650 // based on the target SDK of the package. As of 4 (donut)
1651 // the screen size support was introduced, so all default to
1652 // enabled.
1653 if (smallScreen > 0) {
1654 smallScreen = targetSdk >= 4 ? -1 : 0;
1655 }
1656 if (normalScreen > 0) {
1657 normalScreen = -1;
1658 }
1659 if (largeScreen > 0) {
1660 largeScreen = targetSdk >= 4 ? -1 : 0;
1661 }
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001662 if (xlargeScreen > 0) {
Scott Maind58fb972010-11-04 18:32:00 -07001663 // Introduced in Gingerbread.
1664 xlargeScreen = targetSdk >= 9 ? -1 : 0;
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001665 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001666 if (anyDensity > 0) {
Dianne Hackborne289bff2011-06-13 19:33:22 -07001667 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1668 || compatibleWidthLimitDp > 0) ? -1 : 0;
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001669 }
Dianne Hackborn723738c2009-06-25 19:48:04 -07001670 printf("supports-screens:");
1671 if (smallScreen != 0) printf(" 'small'");
1672 if (normalScreen != 0) printf(" 'normal'");
1673 if (largeScreen != 0) printf(" 'large'");
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001674 if (xlargeScreen != 0) printf(" 'xlarge'");
Dianne Hackborn723738c2009-06-25 19:48:04 -07001675 printf("\n");
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001676 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
Dianne Hackborne289bff2011-06-13 19:33:22 -07001677 if (requiresSmallestWidthDp > 0) {
1678 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1679 }
1680 if (compatibleWidthLimitDp > 0) {
1681 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1682 }
1683 if (largestWidthLimitDp > 0) {
1684 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1685 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001686
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001687 printf("locales:");
Dianne Hackborne17086b2009-06-19 15:13:28 -07001688 const size_t NL = locales.size();
1689 for (size_t i=0; i<NL; i++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001690 const char* localeStr = locales[i].string();
1691 if (localeStr == NULL || strlen(localeStr) == 0) {
1692 localeStr = "--_--";
1693 }
1694 printf(" '%s'", localeStr);
1695 }
1696 printf("\n");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001697
Dianne Hackborne17086b2009-06-19 15:13:28 -07001698 printf("densities:");
1699 const size_t ND = densities.size();
1700 for (size_t i=0; i<ND; i++) {
1701 printf(" '%d'", densities[i]);
1702 }
1703 printf("\n");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001704
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001705 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1706 if (dir != NULL) {
1707 if (dir->getFileCount() > 0) {
1708 printf("native-code:");
1709 for (size_t i=0; i<dir->getFileCount(); i++) {
1710 printf(" '%s'", dir->getFileName(i).string());
1711 }
1712 printf("\n");
1713 }
1714 delete dir;
1715 }
Dan Morrille74763e2012-01-06 10:47:10 -08001716 } else if (strcmp("badger", option) == 0) {
Dianne Hackborn6c997a92012-01-31 11:27:43 -08001717 printf("%s", CONSOLE_DATA);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001718 } else if (strcmp("configurations", option) == 0) {
1719 Vector<ResTable_config> configs;
1720 res.getConfigurations(&configs);
1721 const size_t N = configs.size();
1722 for (size_t i=0; i<N; i++) {
1723 printf("%s\n", configs[i].toString().string());
1724 }
1725 } else {
1726 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
1727 goto bail;
1728 }
1729 }
1730
1731 result = NO_ERROR;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001732
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001733bail:
1734 if (asset) {
1735 delete asset;
1736 }
1737 return (result != NO_ERROR);
1738}
1739
1740
1741/*
1742 * Handle the "add" command, which wants to add files to a new or
1743 * pre-existing archive.
1744 */
1745int doAdd(Bundle* bundle)
1746{
1747 ZipFile* zip = NULL;
1748 status_t result = UNKNOWN_ERROR;
1749 const char* zipFileName;
1750
1751 if (bundle->getUpdate()) {
1752 /* avoid confusion */
1753 fprintf(stderr, "ERROR: can't use '-u' with add\n");
1754 goto bail;
1755 }
1756
1757 if (bundle->getFileSpecCount() < 1) {
1758 fprintf(stderr, "ERROR: must specify zip file name\n");
1759 goto bail;
1760 }
1761 zipFileName = bundle->getFileSpecEntry(0);
1762
1763 if (bundle->getFileSpecCount() < 2) {
1764 fprintf(stderr, "NOTE: nothing to do\n");
1765 goto bail;
1766 }
1767
1768 zip = openReadWrite(zipFileName, true);
1769 if (zip == NULL) {
1770 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
1771 goto bail;
1772 }
1773
1774 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1775 const char* fileName = bundle->getFileSpecEntry(i);
1776
1777 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
1778 printf(" '%s'... (from gzip)\n", fileName);
1779 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
1780 } else {
Doug Zongkerdbe7a682009-10-09 11:24:51 -07001781 if (bundle->getJunkPath()) {
1782 String8 storageName = String8(fileName).getPathLeaf();
1783 printf(" '%s' as '%s'...\n", fileName, storageName.string());
1784 result = zip->add(fileName, storageName.string(),
1785 bundle->getCompressionMethod(), NULL);
1786 } else {
1787 printf(" '%s'...\n", fileName);
1788 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
1789 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001790 }
1791 if (result != NO_ERROR) {
1792 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
1793 if (result == NAME_NOT_FOUND)
1794 fprintf(stderr, ": file not found\n");
1795 else if (result == ALREADY_EXISTS)
1796 fprintf(stderr, ": already exists in archive\n");
1797 else
1798 fprintf(stderr, "\n");
1799 goto bail;
1800 }
1801 }
1802
1803 result = NO_ERROR;
1804
1805bail:
1806 delete zip;
1807 return (result != NO_ERROR);
1808}
1809
1810
1811/*
1812 * Delete files from an existing archive.
1813 */
1814int doRemove(Bundle* bundle)
1815{
1816 ZipFile* zip = NULL;
1817 status_t result = UNKNOWN_ERROR;
1818 const char* zipFileName;
1819
1820 if (bundle->getFileSpecCount() < 1) {
1821 fprintf(stderr, "ERROR: must specify zip file name\n");
1822 goto bail;
1823 }
1824 zipFileName = bundle->getFileSpecEntry(0);
1825
1826 if (bundle->getFileSpecCount() < 2) {
1827 fprintf(stderr, "NOTE: nothing to do\n");
1828 goto bail;
1829 }
1830
1831 zip = openReadWrite(zipFileName, false);
1832 if (zip == NULL) {
1833 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
1834 zipFileName);
1835 goto bail;
1836 }
1837
1838 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1839 const char* fileName = bundle->getFileSpecEntry(i);
1840 ZipEntry* entry;
1841
1842 entry = zip->getEntryByName(fileName);
1843 if (entry == NULL) {
1844 printf(" '%s' NOT FOUND\n", fileName);
1845 continue;
1846 }
1847
1848 result = zip->remove(entry);
1849
1850 if (result != NO_ERROR) {
1851 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
1852 bundle->getFileSpecEntry(i), zipFileName);
1853 goto bail;
1854 }
1855 }
1856
1857 /* update the archive */
1858 zip->flush();
1859
1860bail:
1861 delete zip;
1862 return (result != NO_ERROR);
1863}
1864
1865
1866/*
1867 * Package up an asset directory and associated application files.
1868 */
1869int doPackage(Bundle* bundle)
1870{
1871 const char* outputAPKFile;
1872 int retVal = 1;
1873 status_t err;
1874 sp<AaptAssets> assets;
1875 int N;
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001876 FILE* fp;
1877 String8 dependencyFile;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001878
Anton Krumina2ef5c02014-03-12 14:46:44 -07001879 // -c en_XA or/and ar_XB means do pseudolocalization
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001880 ResourceFilter filter;
1881 err = filter.parse(bundle->getConfigurations());
1882 if (err != NO_ERROR) {
1883 goto bail;
1884 }
1885 if (filter.containsPseudo()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07001886 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
1887 }
1888 if (filter.containsPseudoBidi()) {
1889 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001890 }
1891
1892 N = bundle->getFileSpecCount();
1893 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
1894 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDir() == NULL) {
1895 fprintf(stderr, "ERROR: no input files\n");
1896 goto bail;
1897 }
1898
1899 outputAPKFile = bundle->getOutputAPKFile();
1900
1901 // Make sure the filenames provided exist and are of the appropriate type.
1902 if (outputAPKFile) {
1903 FileType type;
1904 type = getFileType(outputAPKFile);
1905 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
1906 fprintf(stderr,
1907 "ERROR: output file '%s' exists but is not regular file\n",
1908 outputAPKFile);
1909 goto bail;
1910 }
1911 }
1912
1913 // Load the assets.
1914 assets = new AaptAssets();
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001915
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001916 // Set up the resource gathering in assets if we're going to generate
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001917 // dependency files. Every time we encounter a resource while slurping
1918 // the tree, we'll add it to these stores so we have full resource paths
1919 // to write to a dependency file.
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001920 if (bundle->getGenDependencies()) {
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001921 sp<FilePathStore> resPathStore = new FilePathStore;
1922 assets->setFullResPaths(resPathStore);
1923 sp<FilePathStore> assetPathStore = new FilePathStore;
1924 assets->setFullAssetPaths(assetPathStore);
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001925 }
1926
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001927 err = assets->slurpFromArgs(bundle);
1928 if (err < 0) {
1929 goto bail;
1930 }
1931
1932 if (bundle->getVerbose()) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001933 assets->print(String8());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001934 }
1935
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001936 // If they asked for any fileAs that need to be compiled, do so.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001937 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
1938 err = buildResources(bundle, assets);
1939 if (err != 0) {
1940 goto bail;
1941 }
1942 }
1943
1944 // At this point we've read everything and processed everything. From here
1945 // on out it's just writing output files.
1946 if (SourcePos::hasErrors()) {
1947 goto bail;
1948 }
1949
Dianne Hackborn1644c6d72012-02-06 15:33:21 -08001950 // Update symbols with information about which ones are needed as Java symbols.
1951 assets->applyJavaSymbols();
1952 if (SourcePos::hasErrors()) {
1953 goto bail;
1954 }
1955
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001956 // If we've been asked to generate a dependency file, do that here
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001957 if (bundle->getGenDependencies()) {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001958 // If this is the packaging step, generate the dependency file next to
1959 // the output apk (e.g. bin/resources.ap_.d)
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001960 if (outputAPKFile) {
1961 dependencyFile = String8(outputAPKFile);
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001962 // Add the .d extension to the dependency file.
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001963 dependencyFile.append(".d");
1964 } else {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001965 // Else if this is the R.java dependency generation step,
1966 // generate the dependency file in the R.java package subdirectory
1967 // e.g. gen/com/foo/app/R.java.d
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001968 dependencyFile = String8(bundle->getRClassDir());
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001969 dependencyFile.appendPath("R.java.d");
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001970 }
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001971 // Make sure we have a clean dependency file to start with
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001972 fp = fopen(dependencyFile, "w");
1973 fclose(fp);
1974 }
1975
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001976 // Write out R.java constants
Dianne Hackborn1644c6d72012-02-06 15:33:21 -08001977 if (!assets->havePrivateSymbols()) {
Xavier Ducrohet63459ad2009-11-30 18:05:10 -08001978 if (bundle->getCustomPackage() == NULL) {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001979 // Write the R.java file into the appropriate class directory
1980 // e.g. gen/com/foo/app/R.java
Xavier Ducrohet63459ad2009-11-30 18:05:10 -08001981 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
1982 } else {
1983 const String8 customPkg(bundle->getCustomPackage());
1984 err = writeResourceSymbols(bundle, assets, customPkg, true);
1985 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001986 if (err < 0) {
1987 goto bail;
1988 }
Ying Wang002f5372012-04-25 18:53:49 -07001989 // If we have library files, we're going to write our R.java file into
1990 // the appropriate class directory for those libraries as well.
1991 // e.g. gen/com/foo/app/lib/R.java
1992 if (bundle->getExtraPackages() != NULL) {
1993 // Split on colon
1994 String8 libs(bundle->getExtraPackages());
1995 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
1996 while (packageString != NULL) {
1997 // Write the R.java file out with the correct package name
1998 err = writeResourceSymbols(bundle, assets, String8(packageString), true);
1999 if (err < 0) {
2000 goto bail;
2001 }
2002 packageString = strtok(NULL, ":");
2003 }
2004 libs.unlockBuffer();
2005 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002006 } else {
2007 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
2008 if (err < 0) {
2009 goto bail;
2010 }
2011 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
2012 if (err < 0) {
2013 goto bail;
2014 }
2015 }
2016
Joe Onorato1553c822009-08-30 13:36:22 -07002017 // Write out the ProGuard file
2018 err = writeProguardFile(bundle, assets);
2019 if (err < 0) {
2020 goto bail;
2021 }
2022
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002023 // Write the apk
2024 if (outputAPKFile) {
2025 err = writeAPK(bundle, assets, String8(outputAPKFile));
2026 if (err != NO_ERROR) {
2027 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
2028 goto bail;
2029 }
2030 }
2031
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07002032 // If we've been asked to generate a dependency file, we need to finish up here.
2033 // the writeResourceSymbols and writeAPK functions have already written the target
2034 // half of the dependency file, now we need to write the prerequisites. (files that
2035 // the R.java file or .ap_ file depend on)
Josiah Gaskin03589cc2011-06-27 16:26:02 -07002036 if (bundle->getGenDependencies()) {
2037 // Now that writeResourceSymbols or writeAPK has taken care of writing
2038 // the targets to our dependency file, we'll write the prereqs
2039 fp = fopen(dependencyFile, "a+");
2040 fprintf(fp, " : ");
2041 bool includeRaw = (outputAPKFile != NULL);
2042 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07002043 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2044 // and therefore was not added to our pathstores during slurping
Josiah Gaskin03589cc2011-06-27 16:26:02 -07002045 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2046 fclose(fp);
2047 }
2048
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002049 retVal = 0;
2050bail:
2051 if (SourcePos::hasErrors()) {
2052 SourcePos::printErrors(stderr);
2053 }
2054 return retVal;
2055}
Josiah Gaskin8a39da82011-06-06 17:00:35 -07002056
2057/*
2058 * Do PNG Crunching
2059 * PRECONDITIONS
2060 * -S flag points to a source directory containing drawable* folders
2061 * -C flag points to destination directory. The folder structure in the
2062 * source directory will be mirrored to the destination (cache) directory
2063 *
2064 * POSTCONDITIONS
2065 * Destination directory will be updated to match the PNG files in
2066 * the source directory.
2067 */
2068int doCrunch(Bundle* bundle)
2069{
2070 fprintf(stdout, "Crunching PNG Files in ");
2071 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2072 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2073
2074 updatePreProcessedCache(bundle);
2075
2076 return NO_ERROR;
2077}
Dan Morrille74763e2012-01-06 10:47:10 -08002078
Xavier Ducrohetb1f6ad82012-12-21 09:54:02 -08002079/*
2080 * Do PNG Crunching on a single flag
2081 * -i points to a single png file
2082 * -o points to a single png output file
2083 */
2084int doSingleCrunch(Bundle* bundle)
2085{
2086 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2087 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2088
2089 String8 input(bundle->getSingleCrunchInputFile());
2090 String8 output(bundle->getSingleCrunchOutputFile());
Xavier Ducrohetb7de2192013-01-15 16:41:29 -08002091
Xavier Ducrohetc75d3f52013-01-14 18:02:40 -08002092 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2093 // we can't return the status_t as it gets truncate to the lower 8 bits.
2094 return 42;
2095 }
Xavier Ducrohetb7de2192013-01-15 16:41:29 -08002096
Xavier Ducrohetc75d3f52013-01-14 18:02:40 -08002097 return NO_ERROR;
Xavier Ducrohetb1f6ad82012-12-21 09:54:02 -08002098}
2099
Dan Morrille74763e2012-01-06 10:47:10 -08002100char CONSOLE_DATA[2925] = {
2101 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2102 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2103 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2104 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2105 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2106 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2107 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2108 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2109 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2110 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2111 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2112 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2113 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2114 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2115 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2116 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2117 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2118 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2119 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2120 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2121 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2122 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2123 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2124 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2125 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2126 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2127 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2128 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2129 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2130 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2131 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2132 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2133 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2134 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2135 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2136 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2137 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2138 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2139 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2140 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2141 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2142 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2143 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2144 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2145 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2146 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2147 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2148 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2149 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2150 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2151 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2152 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2153 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2154 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2155 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2156 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2157 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2158 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2159 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2160 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2161 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2162 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2163 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2164 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2165 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2166 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2167 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2168 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2169 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2170 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2171 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2172 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2173 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2174 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2175 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2176 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2177 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2178 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2179 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2180 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2181 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2182 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2183 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2184 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2185 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2186 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2187 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2188 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2189 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2190 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2191 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2192 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2193 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2194 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2195 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2196 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2197 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2198 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2199 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2200 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2201 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2202 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2203 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2204 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2205 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2206 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2207 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2208 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2209 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2210 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2211 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2212 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2213 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2214 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2215 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2216 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2217 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2218 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2219 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2220 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2221 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2222 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2223 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2224 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2225 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2226 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2227 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2228 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2229 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2230 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2231 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2232 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2233 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2234 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2235 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2236 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2237 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2238 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2239 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2240 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2241 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2242 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2243 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2244 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2245 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2246 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2247 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2248 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2249 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2250 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2251 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2252 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2253 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2254 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2255 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2256 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2257 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2258 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2259 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2260 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2261 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2262 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2263 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2264 };