blob: 1e3b05899d08b9606761441fa090ba8989c6febd [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001//
2// Copyright 2006 The Android Open Source Project
3//
4// Android Asset Packaging Tool main entry point.
5//
6#include "Main.h"
7#include "Bundle.h"
Dianne Hackborne6b68032011-10-13 16:26:02 -07008#include "ResourceFilter.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08009#include "ResourceTable.h"
Xavier Ducrohetb1f6ad82012-12-21 09:54:02 -080010#include "Images.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080011#include "XMLNode.h"
12
Mathias Agopian3b4062e2009-05-31 19:13:00 -070013#include <utils/Log.h>
14#include <utils/threads.h>
15#include <utils/List.h>
16#include <utils/Errors.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080017
18#include <fcntl.h>
19#include <errno.h>
20
21using namespace android;
22
23/*
24 * Show version info. All the cool kids do it.
25 */
26int doVersion(Bundle* bundle)
27{
28 if (bundle->getFileSpecCount() != 0)
29 printf("(ignoring extra arguments)\n");
30 printf("Android Asset Packaging Tool, v0.2\n");
31
32 return 0;
33}
34
35
36/*
37 * Open the file read only. The call fails if the file doesn't exist.
38 *
39 * Returns NULL on failure.
40 */
41ZipFile* openReadOnly(const char* fileName)
42{
43 ZipFile* zip;
44 status_t result;
45
46 zip = new ZipFile;
47 result = zip->open(fileName, ZipFile::kOpenReadOnly);
48 if (result != NO_ERROR) {
49 if (result == NAME_NOT_FOUND)
50 fprintf(stderr, "ERROR: '%s' not found\n", fileName);
51 else if (result == PERMISSION_DENIED)
52 fprintf(stderr, "ERROR: '%s' access denied\n", fileName);
53 else
54 fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n",
55 fileName);
56 delete zip;
57 return NULL;
58 }
59
60 return zip;
61}
62
63/*
64 * Open the file read-write. The file will be created if it doesn't
65 * already exist and "okayToCreate" is set.
66 *
67 * Returns NULL on failure.
68 */
69ZipFile* openReadWrite(const char* fileName, bool okayToCreate)
70{
71 ZipFile* zip = NULL;
72 status_t result;
73 int flags;
74
75 flags = ZipFile::kOpenReadWrite;
76 if (okayToCreate)
77 flags |= ZipFile::kOpenCreate;
78
79 zip = new ZipFile;
80 result = zip->open(fileName, flags);
81 if (result != NO_ERROR) {
82 delete zip;
83 zip = NULL;
84 goto bail;
85 }
86
87bail:
88 return zip;
89}
90
91
92/*
93 * Return a short string describing the compression method.
94 */
95const char* compressionName(int method)
96{
97 if (method == ZipEntry::kCompressStored)
98 return "Stored";
99 else if (method == ZipEntry::kCompressDeflated)
100 return "Deflated";
101 else
102 return "Unknown";
103}
104
105/*
106 * Return the percent reduction in size (0% == no compression).
107 */
108int calcPercent(long uncompressedLen, long compressedLen)
109{
110 if (!uncompressedLen)
111 return 0;
112 else
113 return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5);
114}
115
116/*
117 * Handle the "list" command, which can be a simple file dump or
118 * a verbose listing.
119 *
120 * The verbose listing closely matches the output of the Info-ZIP "unzip"
121 * command.
122 */
123int doList(Bundle* bundle)
124{
125 int result = 1;
126 ZipFile* zip = NULL;
127 const ZipEntry* entry;
128 long totalUncLen, totalCompLen;
129 const char* zipFileName;
130
131 if (bundle->getFileSpecCount() != 1) {
132 fprintf(stderr, "ERROR: specify zip file name (only)\n");
133 goto bail;
134 }
135 zipFileName = bundle->getFileSpecEntry(0);
136
137 zip = openReadOnly(zipFileName);
138 if (zip == NULL)
139 goto bail;
140
141 int count, i;
142
143 if (bundle->getVerbose()) {
144 printf("Archive: %s\n", zipFileName);
145 printf(
Kenny Rootfb2a9462010-08-25 07:36:31 -0700146 " Length Method Size Ratio Offset Date Time CRC-32 Name\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800147 printf(
Kenny Rootfb2a9462010-08-25 07:36:31 -0700148 "-------- ------ ------- ----- ------- ---- ---- ------ ----\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800149 }
150
151 totalUncLen = totalCompLen = 0;
152
153 count = zip->getNumEntries();
154 for (i = 0; i < count; i++) {
155 entry = zip->getEntryByIndex(i);
156 if (bundle->getVerbose()) {
157 char dateBuf[32];
158 time_t when;
159
160 when = entry->getModWhen();
161 strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M",
162 localtime(&when));
163
Kenny Rootfb2a9462010-08-25 07:36:31 -0700164 printf("%8ld %-7.7s %7ld %3d%% %8zd %s %08lx %s\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800165 (long) entry->getUncompressedLen(),
166 compressionName(entry->getCompressionMethod()),
167 (long) entry->getCompressedLen(),
168 calcPercent(entry->getUncompressedLen(),
169 entry->getCompressedLen()),
Kenny Rootfb2a9462010-08-25 07:36:31 -0700170 (size_t) entry->getLFHOffset(),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800171 dateBuf,
172 entry->getCRC32(),
173 entry->getFileName());
174 } else {
175 printf("%s\n", entry->getFileName());
176 }
177
178 totalUncLen += entry->getUncompressedLen();
179 totalCompLen += entry->getCompressedLen();
180 }
181
182 if (bundle->getVerbose()) {
183 printf(
184 "-------- ------- --- -------\n");
185 printf("%8ld %7ld %2d%% %d files\n",
186 totalUncLen,
187 totalCompLen,
188 calcPercent(totalUncLen, totalCompLen),
189 zip->getNumEntries());
190 }
191
192 if (bundle->getAndroidList()) {
193 AssetManager assets;
194 if (!assets.addAssetPath(String8(zipFileName), NULL)) {
195 fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n");
196 goto bail;
197 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700198
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800199 const ResTable& res = assets.getResources(false);
200 if (&res == NULL) {
201 printf("\nNo resource table found.\n");
202 } else {
Steve Blockf1ff21a2010-06-14 17:34:04 +0100203#ifndef HAVE_ANDROID_OS
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204 printf("\nResource table:\n");
Dianne Hackborne17086b2009-06-19 15:13:28 -0700205 res.print(false);
Steve Blockf1ff21a2010-06-14 17:34:04 +0100206#endif
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800207 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700208
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800209 Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
210 Asset::ACCESS_BUFFER);
211 if (manifestAsset == NULL) {
212 printf("\nNo AndroidManifest.xml found.\n");
213 } else {
214 printf("\nAndroid manifest:\n");
215 ResXMLTree tree;
216 tree.setTo(manifestAsset->getBuffer(true),
217 manifestAsset->getLength());
218 printXMLBlock(&tree);
219 }
220 delete manifestAsset;
221 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700222
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800223 result = 0;
224
225bail:
226 delete zip;
227 return result;
228}
229
230static ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes)
231{
232 size_t N = tree.getAttributeCount();
233 for (size_t i=0; i<N; i++) {
234 if (tree.getAttributeNameResID(i) == attrRes) {
235 return (ssize_t)i;
236 }
237 }
238 return -1;
239}
240
Joe Onorato1553c822009-08-30 13:36:22 -0700241String8 getAttribute(const ResXMLTree& tree, const char* ns,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800242 const char* attr, String8* outError)
243{
244 ssize_t idx = tree.indexOfAttribute(ns, attr);
245 if (idx < 0) {
246 return String8();
247 }
248 Res_value value;
249 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
250 if (value.dataType != Res_value::TYPE_STRING) {
251 if (outError != NULL) *outError = "attribute is not a string value";
252 return String8();
253 }
254 }
255 size_t len;
256 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
257 return str ? String8(str, len) : String8();
258}
259
260static String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError)
261{
262 ssize_t idx = indexOfAttribute(tree, attrRes);
263 if (idx < 0) {
264 return String8();
265 }
266 Res_value value;
267 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
268 if (value.dataType != Res_value::TYPE_STRING) {
269 if (outError != NULL) *outError = "attribute is not a string value";
270 return String8();
271 }
272 }
273 size_t len;
274 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
275 return str ? String8(str, len) : String8();
276}
277
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700278static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes,
279 String8* outError, int32_t defValue = -1)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800280{
281 ssize_t idx = indexOfAttribute(tree, attrRes);
282 if (idx < 0) {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700283 return defValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800284 }
285 Res_value value;
286 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700287 if (value.dataType < Res_value::TYPE_FIRST_INT
288 || value.dataType > Res_value::TYPE_LAST_INT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800289 if (outError != NULL) *outError = "attribute is not an integer value";
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700290 return defValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800291 }
292 }
293 return value.data;
294}
295
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -0700296static int32_t getResolvedIntegerAttribute(const ResTable* resTable, const ResXMLTree& tree,
297 uint32_t attrRes, String8* outError, int32_t defValue = -1)
298{
299 ssize_t idx = indexOfAttribute(tree, attrRes);
300 if (idx < 0) {
301 return defValue;
302 }
303 Res_value value;
304 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
305 if (value.dataType == Res_value::TYPE_REFERENCE) {
306 resTable->resolveReference(&value, 0);
307 }
308 if (value.dataType < Res_value::TYPE_FIRST_INT
309 || value.dataType > Res_value::TYPE_LAST_INT) {
310 if (outError != NULL) *outError = "attribute is not an integer value";
311 return defValue;
312 }
313 }
314 return value.data;
315}
316
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800317static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree,
318 uint32_t attrRes, String8* outError)
319{
320 ssize_t idx = indexOfAttribute(tree, attrRes);
321 if (idx < 0) {
322 return String8();
323 }
324 Res_value value;
325 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
326 if (value.dataType == Res_value::TYPE_STRING) {
327 size_t len;
328 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
329 return str ? String8(str, len) : String8();
330 }
331 resTable->resolveReference(&value, 0);
332 if (value.dataType != Res_value::TYPE_STRING) {
333 if (outError != NULL) *outError = "attribute is not a string value";
334 return String8();
335 }
336 }
337 size_t len;
338 const Res_value* value2 = &value;
339 const char16_t* str = const_cast<ResTable*>(resTable)->valueToString(value2, 0, NULL, &len);
340 return str ? String8(str, len) : String8();
341}
342
343// These are attribute resource constants for the platform, as found
344// in android.R.attr
345enum {
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -0700346 LABEL_ATTR = 0x01010001,
347 ICON_ATTR = 0x01010002,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800348 NAME_ATTR = 0x01010003,
Dan Morrillb6ec11e2012-04-03 12:44:40 -0700349 DEBUGGABLE_ATTR = 0x0101000f,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800350 VERSION_CODE_ATTR = 0x0101021b,
351 VERSION_NAME_ATTR = 0x0101021c,
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -0700352 SCREEN_ORIENTATION_ATTR = 0x0101001e,
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700353 MIN_SDK_VERSION_ATTR = 0x0101020c,
Suchi Amalapurapu75c49842009-08-14 15:13:09 -0700354 MAX_SDK_VERSION_ATTR = 0x01010271,
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700355 REQ_TOUCH_SCREEN_ATTR = 0x01010227,
356 REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
357 REQ_HARD_KEYBOARD_ATTR = 0x01010229,
358 REQ_NAVIGATION_ATTR = 0x0101022a,
359 REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
360 TARGET_SDK_VERSION_ATTR = 0x01010270,
361 TEST_ONLY_ATTR = 0x01010272,
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700362 ANY_DENSITY_ATTR = 0x0101026c,
Dianne Hackborne5276a72009-08-27 16:28:44 -0700363 GL_ES_VERSION_ATTR = 0x01010281,
Dianne Hackborn723738c2009-06-25 19:48:04 -0700364 SMALL_SCREEN_ATTR = 0x01010284,
365 NORMAL_SCREEN_ATTR = 0x01010285,
366 LARGE_SCREEN_ATTR = 0x01010286,
Dianne Hackbornf43489d2010-08-20 12:44:33 -0700367 XLARGE_SCREEN_ATTR = 0x010102bf,
Dianne Hackborne5276a72009-08-27 16:28:44 -0700368 REQUIRED_ATTR = 0x0101028e,
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700369 SCREEN_SIZE_ATTR = 0x010102ca,
370 SCREEN_DENSITY_ATTR = 0x010102cb,
Dianne Hackborne289bff2011-06-13 19:33:22 -0700371 REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
372 COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
373 LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
Kenny Root56088a52011-09-29 13:49:45 -0700374 PUBLIC_KEY_ATTR = 0x010103a6,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800375};
376
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700377const char *getComponentName(String8 &pkgName, String8 &componentName) {
378 ssize_t idx = componentName.find(".");
379 String8 retStr(pkgName);
380 if (idx == 0) {
381 retStr += componentName;
382 } else if (idx < 0) {
383 retStr += ".";
384 retStr += componentName;
385 } else {
386 return componentName.string();
387 }
388 return retStr.string();
389}
390
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700391static void printCompatibleScreens(ResXMLTree& tree) {
392 size_t len;
393 ResXMLTree::event_code_t code;
394 int depth = 0;
395 bool first = true;
396 printf("compatible-screens:");
397 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
398 if (code == ResXMLTree::END_TAG) {
399 depth--;
400 if (depth < 0) {
401 break;
402 }
403 continue;
404 }
405 if (code != ResXMLTree::START_TAG) {
406 continue;
407 }
408 depth++;
409 String8 tag(tree.getElementName(&len));
410 if (tag == "screen") {
411 int32_t screenSize = getIntegerAttribute(tree,
412 SCREEN_SIZE_ATTR, NULL, -1);
413 int32_t screenDensity = getIntegerAttribute(tree,
414 SCREEN_DENSITY_ATTR, NULL, -1);
415 if (screenSize > 0 && screenDensity > 0) {
416 if (!first) {
417 printf(",");
418 }
419 first = false;
420 printf("'%d/%d'", screenSize, screenDensity);
421 }
422 }
423 }
424 printf("\n");
425}
426
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800427/*
428 * Handle the "dump" command, to extract select data from an archive.
429 */
Dan Morrille74763e2012-01-06 10:47:10 -0800430extern char CONSOLE_DATA[2925]; // see EOF
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800431int doDump(Bundle* bundle)
432{
433 status_t result = UNKNOWN_ERROR;
434 Asset* asset = NULL;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700435
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800436 if (bundle->getFileSpecCount() < 1) {
437 fprintf(stderr, "ERROR: no dump option specified\n");
438 return 1;
439 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700440
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800441 if (bundle->getFileSpecCount() < 2) {
442 fprintf(stderr, "ERROR: no dump file specified\n");
443 return 1;
444 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700445
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800446 const char* option = bundle->getFileSpecEntry(0);
447 const char* filename = bundle->getFileSpecEntry(1);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700448
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800449 AssetManager assets;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700450 void* assetsCookie;
451 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800452 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
453 return 1;
454 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700455
Dianne Hackborne289bff2011-06-13 19:33:22 -0700456 // Make a dummy config for retrieving resources... we need to supply
457 // non-default values for some configs so that we can retrieve resources
458 // in the app that don't have a default. The most important of these is
459 // the API version because key resources like icons will have an implicit
460 // version if they are using newer config types like density.
461 ResTable_config config;
462 config.language[0] = 'e';
463 config.language[1] = 'n';
464 config.country[0] = 'U';
465 config.country[1] = 'S';
466 config.orientation = ResTable_config::ORIENTATION_PORT;
467 config.density = ResTable_config::DENSITY_MEDIUM;
468 config.sdkVersion = 10000; // Very high.
469 config.screenWidthDp = 320;
470 config.screenHeightDp = 480;
471 config.smallestScreenWidthDp = 320;
472 assets.setConfiguration(config);
473
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800474 const ResTable& res = assets.getResources(false);
475 if (&res == NULL) {
476 fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
477 goto bail;
478 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700479
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800480 if (strcmp("resources", option) == 0) {
Steve Blockf1ff21a2010-06-14 17:34:04 +0100481#ifndef HAVE_ANDROID_OS
Dianne Hackborne17086b2009-06-19 15:13:28 -0700482 res.print(bundle->getValues());
Steve Blockf1ff21a2010-06-14 17:34:04 +0100483#endif
Dianne Hackborn6c997a92012-01-31 11:27:43 -0800484
485 } else if (strcmp("strings", option) == 0) {
486 const ResStringPool* pool = res.getTableStringBlock(0);
487 printStringPool(pool);
488
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800489 } else if (strcmp("xmltree", option) == 0) {
490 if (bundle->getFileSpecCount() < 3) {
491 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
492 goto bail;
493 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700494
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800495 for (int i=2; i<bundle->getFileSpecCount(); i++) {
496 const char* resname = bundle->getFileSpecEntry(i);
497 ResXMLTree tree;
498 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
499 if (asset == NULL) {
Kenny Root44b283d2009-09-01 19:03:11 -0500500 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800501 goto bail;
502 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700503
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800504 if (tree.setTo(asset->getBuffer(true),
505 asset->getLength()) != NO_ERROR) {
506 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
507 goto bail;
508 }
509 tree.restart();
510 printXMLBlock(&tree);
Kenny Root19138462009-12-04 09:38:48 -0800511 tree.uninit();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800512 delete asset;
513 asset = NULL;
514 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700515
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800516 } else if (strcmp("xmlstrings", option) == 0) {
517 if (bundle->getFileSpecCount() < 3) {
518 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
519 goto bail;
520 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700521
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800522 for (int i=2; i<bundle->getFileSpecCount(); i++) {
523 const char* resname = bundle->getFileSpecEntry(i);
524 ResXMLTree tree;
525 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
526 if (asset == NULL) {
Kenny Root44b283d2009-09-01 19:03:11 -0500527 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800528 goto bail;
529 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700530
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800531 if (tree.setTo(asset->getBuffer(true),
532 asset->getLength()) != NO_ERROR) {
533 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
534 goto bail;
535 }
536 printStringPool(&tree.getStrings());
537 delete asset;
538 asset = NULL;
539 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700540
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800541 } else {
542 ResXMLTree tree;
543 asset = assets.openNonAsset("AndroidManifest.xml",
544 Asset::ACCESS_BUFFER);
545 if (asset == NULL) {
546 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
547 goto bail;
548 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700549
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800550 if (tree.setTo(asset->getBuffer(true),
551 asset->getLength()) != NO_ERROR) {
552 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
553 goto bail;
554 }
555 tree.restart();
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700556
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800557 if (strcmp("permissions", option) == 0) {
558 size_t len;
559 ResXMLTree::event_code_t code;
560 int depth = 0;
561 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
562 if (code == ResXMLTree::END_TAG) {
563 depth--;
564 continue;
565 }
566 if (code != ResXMLTree::START_TAG) {
567 continue;
568 }
569 depth++;
570 String8 tag(tree.getElementName(&len));
571 //printf("Depth %d tag %s\n", depth, tag.string());
572 if (depth == 1) {
573 if (tag != "manifest") {
574 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
575 goto bail;
576 }
577 String8 pkg = getAttribute(tree, NULL, "package", NULL);
578 printf("package: %s\n", pkg.string());
579 } else if (depth == 2 && tag == "permission") {
580 String8 error;
581 String8 name = getAttribute(tree, NAME_ATTR, &error);
582 if (error != "") {
583 fprintf(stderr, "ERROR: %s\n", error.string());
584 goto bail;
585 }
586 printf("permission: %s\n", name.string());
587 } else if (depth == 2 && tag == "uses-permission") {
588 String8 error;
589 String8 name = getAttribute(tree, NAME_ATTR, &error);
590 if (error != "") {
591 fprintf(stderr, "ERROR: %s\n", error.string());
592 goto bail;
593 }
594 printf("uses-permission: %s\n", name.string());
Nick Kralevich1bcc3d62013-04-03 09:00:02 -0700595 int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
596 if (!req) {
597 printf("optional-permission: %s\n", name.string());
598 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800599 }
600 }
601 } else if (strcmp("badging", option) == 0) {
Dianne Hackborne289bff2011-06-13 19:33:22 -0700602 Vector<String8> locales;
603 res.getLocales(&locales);
604
605 Vector<ResTable_config> configs;
606 res.getConfigurations(&configs);
607 SortedVector<int> densities;
608 const size_t NC = configs.size();
609 for (size_t i=0; i<NC; i++) {
610 int dens = configs[i].density;
611 if (dens == 0) dens = 160;
612 densities.add(dens);
613 }
614
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800615 size_t len;
616 ResXMLTree::event_code_t code;
617 int depth = 0;
618 String8 error;
619 bool withinActivity = false;
620 bool isMainActivity = false;
621 bool isLauncherActivity = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700622 bool isSearchable = false;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700623 bool withinApplication = false;
Michael Wrighteaeb1902013-09-05 18:15:57 -0700624 bool withinSupportsInput = false;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700625 bool withinReceiver = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700626 bool withinService = false;
627 bool withinIntentFilter = false;
628 bool hasMainActivity = false;
629 bool hasOtherActivities = false;
630 bool hasOtherReceivers = false;
631 bool hasOtherServices = false;
632 bool hasWallpaperService = false;
633 bool hasImeService = false;
634 bool hasWidgetReceivers = false;
635 bool hasIntentFilter = false;
636 bool actMainActivity = false;
637 bool actWidgetReceivers = false;
638 bool actImeService = false;
639 bool actWallpaperService = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700640
Kenny Root063a44e2011-12-08 08:46:03 -0800641 // These two implement the implicit permissions that are granted
642 // to pre-1.6 applications.
643 bool hasWriteExternalStoragePermission = false;
644 bool hasReadPhoneStatePermission = false;
645
Dianne Hackborn79245122012-03-12 10:51:26 -0700646 // If an app requests write storage, they will also get read storage.
647 bool hasReadExternalStoragePermission = false;
648
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -0700649 // Implement transition to read and write call log.
650 bool hasReadContactsPermission = false;
651 bool hasWriteContactsPermission = false;
652 bool hasReadCallLogPermission = false;
653 bool hasWriteCallLogPermission = false;
654
Dan Morrill89d97c12010-05-03 16:13:14 -0700655 // This next group of variables is used to implement a group of
656 // backward-compatibility heuristics necessitated by the addition of
657 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
658 // heuristic is "if an app requests a permission but doesn't explicitly
659 // request the corresponding <uses-feature>, presume it's there anyway".
660 bool specCameraFeature = false; // camera-related
661 bool specCameraAutofocusFeature = false;
662 bool reqCameraAutofocusFeature = false;
663 bool reqCameraFlashFeature = false;
Dianne Hackborne5276a72009-08-27 16:28:44 -0700664 bool hasCameraPermission = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700665 bool specLocationFeature = false; // location-related
666 bool specNetworkLocFeature = false;
667 bool reqNetworkLocFeature = false;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800668 bool specGpsFeature = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700669 bool reqGpsFeature = false;
670 bool hasMockLocPermission = false;
671 bool hasCoarseLocPermission = false;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800672 bool hasGpsPermission = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700673 bool hasGeneralLocPermission = false;
674 bool specBluetoothFeature = false; // Bluetooth API-related
675 bool hasBluetoothPermission = false;
676 bool specMicrophoneFeature = false; // microphone-related
677 bool hasRecordAudioPermission = false;
678 bool specWiFiFeature = false;
679 bool hasWiFiPermission = false;
680 bool specTelephonyFeature = false; // telephony-related
681 bool reqTelephonySubFeature = false;
682 bool hasTelephonyPermission = false;
683 bool specTouchscreenFeature = false; // touchscreen-related
684 bool specMultitouchFeature = false;
685 bool reqDistinctMultitouchFeature = false;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700686 bool specScreenPortraitFeature = false;
687 bool specScreenLandscapeFeature = false;
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -0700688 bool reqScreenPortraitFeature = false;
689 bool reqScreenLandscapeFeature = false;
Dan Morrill89d97c12010-05-03 16:13:14 -0700690 // 2.2 also added some other features that apps can request, but that
691 // have no corresponding permission, so we cannot implement any
692 // back-compatibility heuristic for them. The below are thus unnecessary
693 // (but are retained here for documentary purposes.)
694 //bool specCompassFeature = false;
695 //bool specAccelerometerFeature = false;
696 //bool specProximityFeature = false;
697 //bool specAmbientLightFeature = false;
698 //bool specLiveWallpaperFeature = false;
699
Dianne Hackborn723738c2009-06-25 19:48:04 -0700700 int targetSdk = 0;
701 int smallScreen = 1;
702 int normalScreen = 1;
703 int largeScreen = 1;
Dianne Hackbornf43489d2010-08-20 12:44:33 -0700704 int xlargeScreen = 1;
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700705 int anyDensity = 1;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700706 int requiresSmallestWidthDp = 0;
707 int compatibleWidthLimitDp = 0;
708 int largestWidthLimitDp = 0;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700709 String8 pkg;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800710 String8 activityName;
711 String8 activityLabel;
712 String8 activityIcon;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700713 String8 receiverName;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700714 String8 serviceName;
Michael Wrighteaeb1902013-09-05 18:15:57 -0700715 Vector<String8> supportedInput;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800716 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
717 if (code == ResXMLTree::END_TAG) {
718 depth--;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700719 if (depth < 2) {
Michael Wrighteaeb1902013-09-05 18:15:57 -0700720 if (withinSupportsInput && !supportedInput.isEmpty()) {
721 printf("supports-input: '");
722 const size_t N = supportedInput.size();
723 for (size_t i=0; i<N; i++) {
724 printf("%s", supportedInput[i].string());
725 if (i != N - 1) {
726 printf("' '");
727 } else {
728 printf("'\n");
729 }
730 }
731 supportedInput.clear();
732 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700733 withinApplication = false;
Michael Wrighteaeb1902013-09-05 18:15:57 -0700734 withinSupportsInput = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700735 } else if (depth < 3) {
736 if (withinActivity && isMainActivity && isLauncherActivity) {
737 const char *aName = getComponentName(pkg, activityName);
Dianne Hackborne289bff2011-06-13 19:33:22 -0700738 printf("launchable-activity:");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700739 if (aName != NULL) {
Dianne Hackborne289bff2011-06-13 19:33:22 -0700740 printf(" name='%s' ", aName);
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700741 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700742 printf(" label='%s' icon='%s'\n",
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700743 activityLabel.string(),
744 activityIcon.string());
745 }
746 if (!hasIntentFilter) {
747 hasOtherActivities |= withinActivity;
748 hasOtherReceivers |= withinReceiver;
749 hasOtherServices |= withinService;
750 }
751 withinActivity = false;
752 withinService = false;
753 withinReceiver = false;
754 hasIntentFilter = false;
755 isMainActivity = isLauncherActivity = false;
756 } else if (depth < 4) {
757 if (withinIntentFilter) {
758 if (withinActivity) {
759 hasMainActivity |= actMainActivity;
760 hasOtherActivities |= !actMainActivity;
761 } else if (withinReceiver) {
762 hasWidgetReceivers |= actWidgetReceivers;
763 hasOtherReceivers |= !actWidgetReceivers;
764 } else if (withinService) {
765 hasImeService |= actImeService;
766 hasWallpaperService |= actWallpaperService;
767 hasOtherServices |= (!actImeService && !actWallpaperService);
768 }
769 }
770 withinIntentFilter = false;
771 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800772 continue;
773 }
774 if (code != ResXMLTree::START_TAG) {
775 continue;
776 }
777 depth++;
778 String8 tag(tree.getElementName(&len));
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700779 //printf("Depth %d, %s\n", depth, tag.string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800780 if (depth == 1) {
781 if (tag != "manifest") {
782 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
783 goto bail;
784 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700785 pkg = getAttribute(tree, NULL, "package", NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800786 printf("package: name='%s' ", pkg.string());
787 int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
788 if (error != "") {
789 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
790 goto bail;
791 }
792 if (versionCode > 0) {
793 printf("versionCode='%d' ", versionCode);
794 } else {
795 printf("versionCode='' ");
796 }
Dianne Hackborncf244ad2010-03-09 15:00:30 -0800797 String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800798 if (error != "") {
799 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
800 goto bail;
801 }
802 printf("versionName='%s'\n", versionName.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700803 } else if (depth == 2) {
804 withinApplication = false;
805 if (tag == "application") {
806 withinApplication = true;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700807
808 String8 label;
809 const size_t NL = locales.size();
810 for (size_t i=0; i<NL; i++) {
811 const char* localeStr = locales[i].string();
812 assets.setLocale(localeStr != NULL ? localeStr : "");
813 String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
814 if (llabel != "") {
815 if (localeStr == NULL || strlen(localeStr) == 0) {
816 label = llabel;
817 printf("application-label:'%s'\n", llabel.string());
818 } else {
819 if (label == "") {
820 label = llabel;
821 }
822 printf("application-label-%s:'%s'\n", localeStr,
823 llabel.string());
824 }
825 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700826 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700827
828 ResTable_config tmpConfig = config;
829 const size_t ND = densities.size();
830 for (size_t i=0; i<ND; i++) {
831 tmpConfig.density = densities[i];
832 assets.setConfiguration(tmpConfig);
833 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
834 if (icon != "") {
835 printf("application-icon-%d:'%s'\n", densities[i], icon.string());
836 }
837 }
838 assets.setConfiguration(config);
839
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700840 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
841 if (error != "") {
842 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
843 goto bail;
844 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700845 int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700846 if (error != "") {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700847 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700848 goto bail;
849 }
Dianne Hackborne289bff2011-06-13 19:33:22 -0700850 printf("application: label='%s' ", label.string());
851 printf("icon='%s'\n", icon.string());
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700852 if (testOnly != 0) {
853 printf("testOnly='%d'\n", testOnly);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700854 }
Dan Morrillb6ec11e2012-04-03 12:44:40 -0700855
856 int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0);
857 if (error != "") {
858 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string());
859 goto bail;
860 }
861 if (debuggable != 0) {
862 printf("application-debuggable\n");
863 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700864 } else if (tag == "uses-sdk") {
865 int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
866 if (error != "") {
867 error = "";
868 String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
869 if (error != "") {
870 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
871 error.string());
872 goto bail;
873 }
Dianne Hackborn723738c2009-06-25 19:48:04 -0700874 if (name == "Donut") targetSdk = 4;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700875 printf("sdkVersion:'%s'\n", name.string());
876 } else if (code != -1) {
Dianne Hackborn723738c2009-06-25 19:48:04 -0700877 targetSdk = code;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700878 printf("sdkVersion:'%d'\n", code);
879 }
Suchi Amalapurapu75c49842009-08-14 15:13:09 -0700880 code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
881 if (code != -1) {
882 printf("maxSdkVersion:'%d'\n", code);
883 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700884 code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
885 if (error != "") {
886 error = "";
887 String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
888 if (error != "") {
889 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
890 error.string());
891 goto bail;
892 }
Dianne Hackborn723738c2009-06-25 19:48:04 -0700893 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700894 printf("targetSdkVersion:'%s'\n", name.string());
895 } else if (code != -1) {
Dianne Hackborn723738c2009-06-25 19:48:04 -0700896 if (targetSdk < code) {
897 targetSdk = code;
898 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700899 printf("targetSdkVersion:'%d'\n", code);
900 }
901 } else if (tag == "uses-configuration") {
902 int32_t reqTouchScreen = getIntegerAttribute(tree,
903 REQ_TOUCH_SCREEN_ATTR, NULL, 0);
904 int32_t reqKeyboardType = getIntegerAttribute(tree,
905 REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
906 int32_t reqHardKeyboard = getIntegerAttribute(tree,
907 REQ_HARD_KEYBOARD_ATTR, NULL, 0);
908 int32_t reqNavigation = getIntegerAttribute(tree,
909 REQ_NAVIGATION_ATTR, NULL, 0);
910 int32_t reqFiveWayNav = getIntegerAttribute(tree,
911 REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
Dianne Hackborncb2d50d2010-01-06 11:29:54 -0800912 printf("uses-configuration:");
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700913 if (reqTouchScreen != 0) {
914 printf(" reqTouchScreen='%d'", reqTouchScreen);
915 }
916 if (reqKeyboardType != 0) {
917 printf(" reqKeyboardType='%d'", reqKeyboardType);
918 }
919 if (reqHardKeyboard != 0) {
920 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
921 }
922 if (reqNavigation != 0) {
923 printf(" reqNavigation='%d'", reqNavigation);
924 }
925 if (reqFiveWayNav != 0) {
926 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
927 }
928 printf("\n");
Michael Wrighteaeb1902013-09-05 18:15:57 -0700929 } else if (tag == "supports-input") {
930 withinSupportsInput = true;
Dianne Hackborn723738c2009-06-25 19:48:04 -0700931 } else if (tag == "supports-screens") {
932 smallScreen = getIntegerAttribute(tree,
933 SMALL_SCREEN_ATTR, NULL, 1);
934 normalScreen = getIntegerAttribute(tree,
935 NORMAL_SCREEN_ATTR, NULL, 1);
936 largeScreen = getIntegerAttribute(tree,
937 LARGE_SCREEN_ATTR, NULL, 1);
Dianne Hackbornf43489d2010-08-20 12:44:33 -0700938 xlargeScreen = getIntegerAttribute(tree,
939 XLARGE_SCREEN_ATTR, NULL, 1);
Dianne Hackborna0b46c92010-10-21 15:32:06 -0700940 anyDensity = getIntegerAttribute(tree,
941 ANY_DENSITY_ATTR, NULL, 1);
Dianne Hackborne289bff2011-06-13 19:33:22 -0700942 requiresSmallestWidthDp = getIntegerAttribute(tree,
943 REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
944 compatibleWidthLimitDp = getIntegerAttribute(tree,
945 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
946 largestWidthLimitDp = getIntegerAttribute(tree,
947 LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
Dianne Hackborne5276a72009-08-27 16:28:44 -0700948 } else if (tag == "uses-feature") {
949 String8 name = getAttribute(tree, NAME_ATTR, &error);
Suchi Amalapurapu40b94722009-09-20 13:39:37 -0700950
951 if (name != "" && error == "") {
Dianne Hackborne5276a72009-08-27 16:28:44 -0700952 int req = getIntegerAttribute(tree,
953 REQUIRED_ATTR, NULL, 1);
Dan Morrill89d97c12010-05-03 16:13:14 -0700954
Dianne Hackborne5276a72009-08-27 16:28:44 -0700955 if (name == "android.hardware.camera") {
956 specCameraFeature = true;
Dan Morrill89d97c12010-05-03 16:13:14 -0700957 } else if (name == "android.hardware.camera.autofocus") {
958 // these have no corresponding permission to check for,
959 // but should imply the foundational camera permission
960 reqCameraAutofocusFeature = reqCameraAutofocusFeature || req;
961 specCameraAutofocusFeature = true;
962 } else if (req && (name == "android.hardware.camera.flash")) {
963 // these have no corresponding permission to check for,
964 // but should imply the foundational camera permission
965 reqCameraFlashFeature = true;
966 } else if (name == "android.hardware.location") {
967 specLocationFeature = true;
968 } else if (name == "android.hardware.location.network") {
969 specNetworkLocFeature = true;
970 reqNetworkLocFeature = reqNetworkLocFeature || req;
Dianne Hackbornef05e072010-03-01 17:43:39 -0800971 } else if (name == "android.hardware.location.gps") {
972 specGpsFeature = true;
Dan Morrill89d97c12010-05-03 16:13:14 -0700973 reqGpsFeature = reqGpsFeature || req;
974 } else if (name == "android.hardware.bluetooth") {
975 specBluetoothFeature = true;
976 } else if (name == "android.hardware.touchscreen") {
977 specTouchscreenFeature = true;
978 } else if (name == "android.hardware.touchscreen.multitouch") {
979 specMultitouchFeature = true;
980 } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
981 reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req;
982 } else if (name == "android.hardware.microphone") {
983 specMicrophoneFeature = true;
984 } else if (name == "android.hardware.wifi") {
985 specWiFiFeature = true;
986 } else if (name == "android.hardware.telephony") {
987 specTelephonyFeature = true;
988 } else if (req && (name == "android.hardware.telephony.gsm" ||
989 name == "android.hardware.telephony.cdma")) {
990 // these have no corresponding permission to check for,
991 // but should imply the foundational telephony permission
992 reqTelephonySubFeature = true;
Dianne Hackborne289bff2011-06-13 19:33:22 -0700993 } else if (name == "android.hardware.screen.portrait") {
994 specScreenPortraitFeature = true;
995 } else if (name == "android.hardware.screen.landscape") {
996 specScreenLandscapeFeature = true;
Dianne Hackborne5276a72009-08-27 16:28:44 -0700997 }
998 printf("uses-feature%s:'%s'\n",
999 req ? "" : "-not-required", name.string());
1000 } else {
1001 int vers = getIntegerAttribute(tree,
1002 GL_ES_VERSION_ATTR, &error);
1003 if (error == "") {
1004 printf("uses-gl-es:'0x%x'\n", vers);
1005 }
1006 }
1007 } else if (tag == "uses-permission") {
1008 String8 name = getAttribute(tree, NAME_ATTR, &error);
Suchi Amalapurapu40b94722009-09-20 13:39:37 -07001009 if (name != "" && error == "") {
Dianne Hackborne5276a72009-08-27 16:28:44 -07001010 if (name == "android.permission.CAMERA") {
1011 hasCameraPermission = true;
Dianne Hackbornef05e072010-03-01 17:43:39 -08001012 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
1013 hasGpsPermission = true;
Dan Morrill89d97c12010-05-03 16:13:14 -07001014 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
1015 hasMockLocPermission = true;
1016 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
1017 hasCoarseLocPermission = true;
1018 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
1019 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
1020 hasGeneralLocPermission = true;
1021 } else if (name == "android.permission.BLUETOOTH" ||
1022 name == "android.permission.BLUETOOTH_ADMIN") {
1023 hasBluetoothPermission = true;
1024 } else if (name == "android.permission.RECORD_AUDIO") {
1025 hasRecordAudioPermission = true;
1026 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1027 name == "android.permission.CHANGE_WIFI_STATE" ||
1028 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
1029 hasWiFiPermission = true;
1030 } else if (name == "android.permission.CALL_PHONE" ||
1031 name == "android.permission.CALL_PRIVILEGED" ||
1032 name == "android.permission.MODIFY_PHONE_STATE" ||
1033 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1034 name == "android.permission.READ_SMS" ||
1035 name == "android.permission.RECEIVE_SMS" ||
1036 name == "android.permission.RECEIVE_MMS" ||
1037 name == "android.permission.RECEIVE_WAP_PUSH" ||
1038 name == "android.permission.SEND_SMS" ||
1039 name == "android.permission.WRITE_APN_SETTINGS" ||
1040 name == "android.permission.WRITE_SMS") {
1041 hasTelephonyPermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001042 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1043 hasWriteExternalStoragePermission = true;
Dianne Hackborn79245122012-03-12 10:51:26 -07001044 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1045 hasReadExternalStoragePermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001046 } else if (name == "android.permission.READ_PHONE_STATE") {
1047 hasReadPhoneStatePermission = true;
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001048 } else if (name == "android.permission.READ_CONTACTS") {
1049 hasReadContactsPermission = true;
1050 } else if (name == "android.permission.WRITE_CONTACTS") {
1051 hasWriteContactsPermission = true;
1052 } else if (name == "android.permission.READ_CALL_LOG") {
1053 hasReadCallLogPermission = true;
1054 } else if (name == "android.permission.WRITE_CALL_LOG") {
1055 hasWriteCallLogPermission = true;
Dianne Hackborne5276a72009-08-27 16:28:44 -07001056 }
1057 printf("uses-permission:'%s'\n", name.string());
Nick Kralevich1bcc3d62013-04-03 09:00:02 -07001058 int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
1059 if (!req) {
1060 printf("optional-permission:'%s'\n", name.string());
1061 }
Dianne Hackborne5276a72009-08-27 16:28:44 -07001062 } else {
1063 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1064 error.string());
1065 goto bail;
1066 }
Dianne Hackborn43b68032010-09-02 17:14:41 -07001067 } else if (tag == "uses-package") {
1068 String8 name = getAttribute(tree, NAME_ATTR, &error);
1069 if (name != "" && error == "") {
1070 printf("uses-package:'%s'\n", name.string());
1071 } else {
1072 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1073 error.string());
1074 goto bail;
1075 }
Jeff Hamiltone2c17f92010-02-12 13:45:16 -06001076 } else if (tag == "original-package") {
1077 String8 name = getAttribute(tree, NAME_ATTR, &error);
1078 if (name != "" && error == "") {
1079 printf("original-package:'%s'\n", name.string());
1080 } else {
1081 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1082 error.string());
1083 goto bail;
1084 }
Dan Morrill096b67f2010-12-13 16:25:54 -08001085 } else if (tag == "supports-gl-texture") {
Dan Morrill6f51fc12010-10-13 14:33:43 -07001086 String8 name = getAttribute(tree, NAME_ATTR, &error);
1087 if (name != "" && error == "") {
Dan Morrill096b67f2010-12-13 16:25:54 -08001088 printf("supports-gl-texture:'%s'\n", name.string());
Dan Morrill6f51fc12010-10-13 14:33:43 -07001089 } else {
1090 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1091 error.string());
1092 goto bail;
1093 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001094 } else if (tag == "compatible-screens") {
1095 printCompatibleScreens(tree);
1096 depth--;
Kenny Root56088a52011-09-29 13:49:45 -07001097 } else if (tag == "package-verifier") {
1098 String8 name = getAttribute(tree, NAME_ATTR, &error);
1099 if (name != "" && error == "") {
1100 String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
1101 if (publicKey != "" && error == "") {
1102 printf("package-verifier: name='%s' publicKey='%s'\n",
1103 name.string(), publicKey.string());
1104 }
1105 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001106 }
Michael Wrighteaeb1902013-09-05 18:15:57 -07001107 } else if (depth == 3) {
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001108 withinActivity = false;
1109 withinReceiver = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001110 withinService = false;
1111 hasIntentFilter = false;
Michael Wrighteaeb1902013-09-05 18:15:57 -07001112 if (withinApplication) {
1113 if(tag == "activity") {
1114 withinActivity = true;
1115 activityName = getAttribute(tree, NAME_ATTR, &error);
1116 if (error != "") {
1117 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1118 error.string());
1119 goto bail;
1120 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001121
Michael Wrighteaeb1902013-09-05 18:15:57 -07001122 activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1123 if (error != "") {
1124 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
1125 error.string());
1126 goto bail;
1127 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001128
Michael Wrighteaeb1902013-09-05 18:15:57 -07001129 activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1130 if (error != "") {
1131 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1132 error.string());
1133 goto bail;
1134 }
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001135
Michael Wrighteaeb1902013-09-05 18:15:57 -07001136 int32_t orien = getResolvedIntegerAttribute(&res, tree,
1137 SCREEN_ORIENTATION_ATTR, &error);
1138 if (error == "") {
1139 if (orien == 0 || orien == 6 || orien == 8) {
1140 // Requests landscape, sensorLandscape, or reverseLandscape.
1141 reqScreenLandscapeFeature = true;
1142 } else if (orien == 1 || orien == 7 || orien == 9) {
1143 // Requests portrait, sensorPortrait, or reversePortrait.
1144 reqScreenPortraitFeature = true;
1145 }
1146 }
1147 } else if (tag == "uses-library") {
1148 String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1149 if (error != "") {
1150 fprintf(stderr,
1151 "ERROR getting 'android:name' attribute for uses-library"
1152 " %s\n", error.string());
1153 goto bail;
1154 }
1155 int req = getIntegerAttribute(tree,
1156 REQUIRED_ATTR, NULL, 1);
1157 printf("uses-library%s:'%s'\n",
1158 req ? "" : "-not-required", libraryName.string());
1159 } else if (tag == "receiver") {
1160 withinReceiver = true;
1161 receiverName = getAttribute(tree, NAME_ATTR, &error);
1162
1163 if (error != "") {
1164 fprintf(stderr,
1165 "ERROR getting 'android:name' attribute for receiver:"
1166 " %s\n", error.string());
1167 goto bail;
1168 }
1169 } else if (tag == "service") {
1170 withinService = true;
1171 serviceName = getAttribute(tree, NAME_ATTR, &error);
1172
1173 if (error != "") {
1174 fprintf(stderr, "ERROR getting 'android:name' attribute for"
1175 " service: %s\n", error.string());
1176 goto bail;
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001177 }
1178 }
Michael Wrighteaeb1902013-09-05 18:15:57 -07001179 } else if (withinSupportsInput && tag == "input-type") {
1180 String8 name = getAttribute(tree, NAME_ATTR, &error);
1181 if (name != "" && error == "") {
1182 supportedInput.add(name);
1183 } else {
1184 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1185 error.string());
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001186 goto bail;
1187 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001188 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001189 } else if ((depth == 4) && (tag == "intent-filter")) {
1190 hasIntentFilter = true;
1191 withinIntentFilter = true;
1192 actMainActivity = actWidgetReceivers = actImeService = actWallpaperService = false;
1193 } else if ((depth == 5) && withinIntentFilter){
1194 String8 action;
1195 if (tag == "action") {
1196 action = getAttribute(tree, NAME_ATTR, &error);
1197 if (error != "") {
1198 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1199 goto bail;
1200 }
1201 if (withinActivity) {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001202 if (action == "android.intent.action.MAIN") {
1203 isMainActivity = true;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001204 actMainActivity = true;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001205 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001206 } else if (withinReceiver) {
1207 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1208 actWidgetReceivers = true;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001209 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001210 } else if (withinService) {
1211 if (action == "android.view.InputMethod") {
1212 actImeService = true;
1213 } else if (action == "android.service.wallpaper.WallpaperService") {
1214 actWallpaperService = true;
1215 }
1216 }
1217 if (action == "android.intent.action.SEARCH") {
1218 isSearchable = true;
1219 }
1220 }
1221
1222 if (tag == "category") {
1223 String8 category = getAttribute(tree, NAME_ATTR, &error);
1224 if (error != "") {
1225 fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string());
1226 goto bail;
1227 }
1228 if (withinActivity) {
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001229 if (category == "android.intent.category.LAUNCHER") {
1230 isLauncherActivity = true;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001231 }
1232 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001233 }
1234 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001235 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001236
Kenny Root063a44e2011-12-08 08:46:03 -08001237 // Pre-1.6 implicitly granted permission compatibility logic
1238 if (targetSdk < 4) {
1239 if (!hasWriteExternalStoragePermission) {
1240 printf("uses-permission:'android.permission.WRITE_EXTERNAL_STORAGE'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001241 printf("uses-implied-permission:'android.permission.WRITE_EXTERNAL_STORAGE'," \
1242 "'targetSdkVersion < 4'\n");
Dianne Hackborn79245122012-03-12 10:51:26 -07001243 hasWriteExternalStoragePermission = true;
Kenny Root063a44e2011-12-08 08:46:03 -08001244 }
1245 if (!hasReadPhoneStatePermission) {
1246 printf("uses-permission:'android.permission.READ_PHONE_STATE'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001247 printf("uses-implied-permission:'android.permission.READ_PHONE_STATE'," \
1248 "'targetSdkVersion < 4'\n");
Kenny Root063a44e2011-12-08 08:46:03 -08001249 }
1250 }
1251
Dianne Hackborn79245122012-03-12 10:51:26 -07001252 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
Dianne Hackborn2bd8d042012-06-11 12:27:05 -07001253 // force them to always take READ_EXTERNAL_STORAGE as well. We always
1254 // do this (regardless of target API version) because we can't have
1255 // an app with write permission but not read permission.
Dianne Hackborn79245122012-03-12 10:51:26 -07001256 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
1257 printf("uses-permission:'android.permission.READ_EXTERNAL_STORAGE'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001258 printf("uses-implied-permission:'android.permission.READ_EXTERNAL_STORAGE'," \
1259 "'requested WRITE_EXTERNAL_STORAGE'\n");
Dianne Hackborn79245122012-03-12 10:51:26 -07001260 }
1261
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001262 // Pre-JellyBean call log permission compatibility.
1263 if (targetSdk < 16) {
1264 if (!hasReadCallLogPermission && hasReadContactsPermission) {
1265 printf("uses-permission:'android.permission.READ_CALL_LOG'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001266 printf("uses-implied-permission:'android.permission.READ_CALL_LOG'," \
1267 "'targetSdkVersion < 16 and requested READ_CONTACTS'\n");
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001268 }
1269 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
1270 printf("uses-permission:'android.permission.WRITE_CALL_LOG'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001271 printf("uses-implied-permission:'android.permission.WRITE_CALL_LOG'," \
1272 "'targetSdkVersion < 16 and requested WRITE_CONTACTS'\n");
Dianne Hackborn31b0e0e2012-04-05 19:33:30 -07001273 }
1274 }
1275
Dan Morrill89d97c12010-05-03 16:13:14 -07001276 /* The following blocks handle printing "inferred" uses-features, based
1277 * on whether related features or permissions are used by the app.
1278 * Note that the various spec*Feature variables denote whether the
1279 * relevant tag was *present* in the AndroidManfest, not that it was
1280 * present and set to true.
1281 */
1282 // Camera-related back-compatibility logic
1283 if (!specCameraFeature) {
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001284 if (reqCameraFlashFeature) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001285 // if app requested a sub-feature (autofocus or flash) and didn't
1286 // request the base camera feature, we infer that it meant to
1287 printf("uses-feature:'android.hardware.camera'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001288 printf("uses-implied-feature:'android.hardware.camera'," \
1289 "'requested android.hardware.camera.flash feature'\n");
1290 } else if (reqCameraAutofocusFeature) {
1291 // if app requested a sub-feature (autofocus or flash) and didn't
1292 // request the base camera feature, we infer that it meant to
1293 printf("uses-feature:'android.hardware.camera'\n");
1294 printf("uses-implied-feature:'android.hardware.camera'," \
1295 "'requested android.hardware.camera.autofocus feature'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001296 } else if (hasCameraPermission) {
1297 // if app wants to use camera but didn't request the feature, we infer
1298 // that it meant to, and further that it wants autofocus
1299 // (which was the 1.0 - 1.5 behavior)
1300 printf("uses-feature:'android.hardware.camera'\n");
1301 if (!specCameraAutofocusFeature) {
1302 printf("uses-feature:'android.hardware.camera.autofocus'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001303 printf("uses-implied-feature:'android.hardware.camera.autofocus'," \
1304 "'requested android.permission.CAMERA permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001305 }
1306 }
Dianne Hackborne5276a72009-08-27 16:28:44 -07001307 }
Doug Zongkerdbe7a682009-10-09 11:24:51 -07001308
Dan Morrill89d97c12010-05-03 16:13:14 -07001309 // Location-related back-compatibility logic
1310 if (!specLocationFeature &&
1311 (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission ||
1312 hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) {
1313 // if app either takes a location-related permission or requests one of the
1314 // sub-features, we infer that it also meant to request the base location feature
1315 printf("uses-feature:'android.hardware.location'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001316 printf("uses-implied-feature:'android.hardware.location'," \
1317 "'requested a location access permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001318 }
Dianne Hackbornef05e072010-03-01 17:43:39 -08001319 if (!specGpsFeature && hasGpsPermission) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001320 // if app takes GPS (FINE location) perm but does not request the GPS
1321 // feature, we infer that it meant to
Dianne Hackbornef05e072010-03-01 17:43:39 -08001322 printf("uses-feature:'android.hardware.location.gps'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001323 printf("uses-implied-feature:'android.hardware.location.gps'," \
1324 "'requested android.permission.ACCESS_FINE_LOCATION permission'\n");
Dianne Hackbornef05e072010-03-01 17:43:39 -08001325 }
Dan Morrill89d97c12010-05-03 16:13:14 -07001326 if (!specNetworkLocFeature && hasCoarseLocPermission) {
1327 // if app takes Network location (COARSE location) perm but does not request the
1328 // network location feature, we infer that it meant to
1329 printf("uses-feature:'android.hardware.location.network'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001330 printf("uses-implied-feature:'android.hardware.location.network'," \
Dianne Hackbornec559ff2013-02-25 15:42:07 -08001331 "'requested android.permission.ACCESS_COARSE_LOCATION permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001332 }
1333
1334 // Bluetooth-related compatibility logic
Dan Morrill6b22d812010-06-15 21:41:42 -07001335 if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
Dan Morrill89d97c12010-05-03 16:13:14 -07001336 // if app takes a Bluetooth permission but does not request the Bluetooth
1337 // feature, we infer that it meant to
1338 printf("uses-feature:'android.hardware.bluetooth'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001339 printf("uses-implied-feature:'android.hardware.bluetooth'," \
1340 "'requested android.permission.BLUETOOTH or android.permission.BLUETOOTH_ADMIN " \
1341 "permission and targetSdkVersion > 4'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001342 }
1343
1344 // Microphone-related compatibility logic
1345 if (!specMicrophoneFeature && hasRecordAudioPermission) {
1346 // if app takes the record-audio permission but does not request the microphone
1347 // feature, we infer that it meant to
1348 printf("uses-feature:'android.hardware.microphone'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001349 printf("uses-implied-feature:'android.hardware.microphone'," \
1350 "'requested android.permission.RECORD_AUDIO permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001351 }
1352
1353 // WiFi-related compatibility logic
1354 if (!specWiFiFeature && hasWiFiPermission) {
1355 // if app takes one of the WiFi permissions but does not request the WiFi
1356 // feature, we infer that it meant to
1357 printf("uses-feature:'android.hardware.wifi'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001358 printf("uses-implied-feature:'android.hardware.wifi'," \
1359 "'requested android.permission.ACCESS_WIFI_STATE, " \
1360 "android.permission.CHANGE_WIFI_STATE, or " \
1361 "android.permission.CHANGE_WIFI_MULTICAST_STATE permission'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001362 }
1363
1364 // Telephony-related compatibility logic
1365 if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) {
1366 // if app takes one of the telephony permissions or requests a sub-feature but
1367 // does not request the base telephony feature, we infer that it meant to
1368 printf("uses-feature:'android.hardware.telephony'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001369 printf("uses-implied-feature:'android.hardware.telephony'," \
1370 "'requested a telephony-related permission or feature'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001371 }
1372
1373 // Touchscreen-related back-compatibility logic
1374 if (!specTouchscreenFeature) { // not a typo!
1375 // all apps are presumed to require a touchscreen, unless they explicitly say
1376 // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
1377 // Note that specTouchscreenFeature is true if the tag is present, regardless
1378 // of whether its value is true or false, so this is safe
1379 printf("uses-feature:'android.hardware.touchscreen'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001380 printf("uses-implied-feature:'android.hardware.touchscreen'," \
1381 "'assumed you require a touch screen unless explicitly made optional'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001382 }
1383 if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
1384 // if app takes one of the telephony permissions or requests a sub-feature but
1385 // does not request the base telephony feature, we infer that it meant to
1386 printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001387 printf("uses-implied-feature:'android.hardware.touchscreen.multitouch'," \
1388 "'requested android.hardware.touchscreen.multitouch.distinct feature'\n");
Dan Morrill89d97c12010-05-03 16:13:14 -07001389 }
Dianne Hackbornef05e072010-03-01 17:43:39 -08001390
Dianne Hackborne289bff2011-06-13 19:33:22 -07001391 // Landscape/portrait-related compatibility logic
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001392 if (!specScreenLandscapeFeature && !specScreenPortraitFeature) {
1393 // If the app has specified any activities in its manifest
1394 // that request a specific orientation, then assume that
1395 // orientation is required.
1396 if (reqScreenLandscapeFeature) {
1397 printf("uses-feature:'android.hardware.screen.landscape'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001398 printf("uses-implied-feature:'android.hardware.screen.landscape'," \
1399 "'one or more activities have specified a landscape orientation'\n");
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001400 }
1401 if (reqScreenPortraitFeature) {
1402 printf("uses-feature:'android.hardware.screen.portrait'\n");
Dianne Hackborn4aa14b92012-04-13 19:18:41 -07001403 printf("uses-implied-feature:'android.hardware.screen.portrait'," \
1404 "'one or more activities have specified a portrait orientation'\n");
Dianne Hackbornf77ae6e2011-06-16 11:11:23 -07001405 }
Dianne Hackborne289bff2011-06-13 19:33:22 -07001406 }
1407
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001408 if (hasMainActivity) {
1409 printf("main\n");
1410 }
1411 if (hasWidgetReceivers) {
1412 printf("app-widget\n");
1413 }
1414 if (hasImeService) {
1415 printf("ime\n");
1416 }
1417 if (hasWallpaperService) {
1418 printf("wallpaper\n");
1419 }
1420 if (hasOtherActivities) {
1421 printf("other-activities\n");
1422 }
1423 if (isSearchable) {
1424 printf("search\n");
1425 }
1426 if (hasOtherReceivers) {
1427 printf("other-receivers\n");
1428 }
1429 if (hasOtherServices) {
1430 printf("other-services\n");
1431 }
1432
Dianne Hackborne289bff2011-06-13 19:33:22 -07001433 // For modern apps, if screen size buckets haven't been specified
1434 // but the new width ranges have, then infer the buckets from them.
1435 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1436 && requiresSmallestWidthDp > 0) {
1437 int compatWidth = compatibleWidthLimitDp;
1438 if (compatWidth <= 0) compatWidth = requiresSmallestWidthDp;
1439 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1440 smallScreen = -1;
1441 } else {
1442 smallScreen = 0;
1443 }
1444 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1445 normalScreen = -1;
1446 } else {
1447 normalScreen = 0;
1448 }
1449 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1450 largeScreen = -1;
1451 } else {
1452 largeScreen = 0;
1453 }
1454 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1455 xlargeScreen = -1;
1456 } else {
1457 xlargeScreen = 0;
1458 }
1459 }
1460
Dianne Hackborn723738c2009-06-25 19:48:04 -07001461 // Determine default values for any unspecified screen sizes,
1462 // based on the target SDK of the package. As of 4 (donut)
1463 // the screen size support was introduced, so all default to
1464 // enabled.
1465 if (smallScreen > 0) {
1466 smallScreen = targetSdk >= 4 ? -1 : 0;
1467 }
1468 if (normalScreen > 0) {
1469 normalScreen = -1;
1470 }
1471 if (largeScreen > 0) {
1472 largeScreen = targetSdk >= 4 ? -1 : 0;
1473 }
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001474 if (xlargeScreen > 0) {
Scott Maind58fb972010-11-04 18:32:00 -07001475 // Introduced in Gingerbread.
1476 xlargeScreen = targetSdk >= 9 ? -1 : 0;
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001477 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001478 if (anyDensity > 0) {
Dianne Hackborne289bff2011-06-13 19:33:22 -07001479 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1480 || compatibleWidthLimitDp > 0) ? -1 : 0;
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001481 }
Dianne Hackborn723738c2009-06-25 19:48:04 -07001482 printf("supports-screens:");
1483 if (smallScreen != 0) printf(" 'small'");
1484 if (normalScreen != 0) printf(" 'normal'");
1485 if (largeScreen != 0) printf(" 'large'");
Dianne Hackbornf43489d2010-08-20 12:44:33 -07001486 if (xlargeScreen != 0) printf(" 'xlarge'");
Dianne Hackborn723738c2009-06-25 19:48:04 -07001487 printf("\n");
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001488 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
Dianne Hackborne289bff2011-06-13 19:33:22 -07001489 if (requiresSmallestWidthDp > 0) {
1490 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1491 }
1492 if (compatibleWidthLimitDp > 0) {
1493 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1494 }
1495 if (largestWidthLimitDp > 0) {
1496 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1497 }
Dianne Hackborna0b46c92010-10-21 15:32:06 -07001498
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001499 printf("locales:");
Dianne Hackborne17086b2009-06-19 15:13:28 -07001500 const size_t NL = locales.size();
1501 for (size_t i=0; i<NL; i++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001502 const char* localeStr = locales[i].string();
1503 if (localeStr == NULL || strlen(localeStr) == 0) {
1504 localeStr = "--_--";
1505 }
1506 printf(" '%s'", localeStr);
1507 }
1508 printf("\n");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001509
Dianne Hackborne17086b2009-06-19 15:13:28 -07001510 printf("densities:");
1511 const size_t ND = densities.size();
1512 for (size_t i=0; i<ND; i++) {
1513 printf(" '%d'", densities[i]);
1514 }
1515 printf("\n");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -07001516
Dianne Hackbornbb9ea302009-05-18 15:22:00 -07001517 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1518 if (dir != NULL) {
1519 if (dir->getFileCount() > 0) {
1520 printf("native-code:");
1521 for (size_t i=0; i<dir->getFileCount(); i++) {
1522 printf(" '%s'", dir->getFileName(i).string());
1523 }
1524 printf("\n");
1525 }
1526 delete dir;
1527 }
Dan Morrille74763e2012-01-06 10:47:10 -08001528 } else if (strcmp("badger", option) == 0) {
Dianne Hackborn6c997a92012-01-31 11:27:43 -08001529 printf("%s", CONSOLE_DATA);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001530 } else if (strcmp("configurations", option) == 0) {
1531 Vector<ResTable_config> configs;
1532 res.getConfigurations(&configs);
1533 const size_t N = configs.size();
1534 for (size_t i=0; i<N; i++) {
1535 printf("%s\n", configs[i].toString().string());
1536 }
1537 } else {
1538 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
1539 goto bail;
1540 }
1541 }
1542
1543 result = NO_ERROR;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -07001544
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001545bail:
1546 if (asset) {
1547 delete asset;
1548 }
1549 return (result != NO_ERROR);
1550}
1551
1552
1553/*
1554 * Handle the "add" command, which wants to add files to a new or
1555 * pre-existing archive.
1556 */
1557int doAdd(Bundle* bundle)
1558{
1559 ZipFile* zip = NULL;
1560 status_t result = UNKNOWN_ERROR;
1561 const char* zipFileName;
1562
1563 if (bundle->getUpdate()) {
1564 /* avoid confusion */
1565 fprintf(stderr, "ERROR: can't use '-u' with add\n");
1566 goto bail;
1567 }
1568
1569 if (bundle->getFileSpecCount() < 1) {
1570 fprintf(stderr, "ERROR: must specify zip file name\n");
1571 goto bail;
1572 }
1573 zipFileName = bundle->getFileSpecEntry(0);
1574
1575 if (bundle->getFileSpecCount() < 2) {
1576 fprintf(stderr, "NOTE: nothing to do\n");
1577 goto bail;
1578 }
1579
1580 zip = openReadWrite(zipFileName, true);
1581 if (zip == NULL) {
1582 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
1583 goto bail;
1584 }
1585
1586 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1587 const char* fileName = bundle->getFileSpecEntry(i);
1588
1589 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
1590 printf(" '%s'... (from gzip)\n", fileName);
1591 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
1592 } else {
Doug Zongkerdbe7a682009-10-09 11:24:51 -07001593 if (bundle->getJunkPath()) {
1594 String8 storageName = String8(fileName).getPathLeaf();
1595 printf(" '%s' as '%s'...\n", fileName, storageName.string());
1596 result = zip->add(fileName, storageName.string(),
1597 bundle->getCompressionMethod(), NULL);
1598 } else {
1599 printf(" '%s'...\n", fileName);
1600 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
1601 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001602 }
1603 if (result != NO_ERROR) {
1604 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
1605 if (result == NAME_NOT_FOUND)
1606 fprintf(stderr, ": file not found\n");
1607 else if (result == ALREADY_EXISTS)
1608 fprintf(stderr, ": already exists in archive\n");
1609 else
1610 fprintf(stderr, "\n");
1611 goto bail;
1612 }
1613 }
1614
1615 result = NO_ERROR;
1616
1617bail:
1618 delete zip;
1619 return (result != NO_ERROR);
1620}
1621
1622
1623/*
1624 * Delete files from an existing archive.
1625 */
1626int doRemove(Bundle* bundle)
1627{
1628 ZipFile* zip = NULL;
1629 status_t result = UNKNOWN_ERROR;
1630 const char* zipFileName;
1631
1632 if (bundle->getFileSpecCount() < 1) {
1633 fprintf(stderr, "ERROR: must specify zip file name\n");
1634 goto bail;
1635 }
1636 zipFileName = bundle->getFileSpecEntry(0);
1637
1638 if (bundle->getFileSpecCount() < 2) {
1639 fprintf(stderr, "NOTE: nothing to do\n");
1640 goto bail;
1641 }
1642
1643 zip = openReadWrite(zipFileName, false);
1644 if (zip == NULL) {
1645 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
1646 zipFileName);
1647 goto bail;
1648 }
1649
1650 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1651 const char* fileName = bundle->getFileSpecEntry(i);
1652 ZipEntry* entry;
1653
1654 entry = zip->getEntryByName(fileName);
1655 if (entry == NULL) {
1656 printf(" '%s' NOT FOUND\n", fileName);
1657 continue;
1658 }
1659
1660 result = zip->remove(entry);
1661
1662 if (result != NO_ERROR) {
1663 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
1664 bundle->getFileSpecEntry(i), zipFileName);
1665 goto bail;
1666 }
1667 }
1668
1669 /* update the archive */
1670 zip->flush();
1671
1672bail:
1673 delete zip;
1674 return (result != NO_ERROR);
1675}
1676
1677
1678/*
1679 * Package up an asset directory and associated application files.
1680 */
1681int doPackage(Bundle* bundle)
1682{
1683 const char* outputAPKFile;
1684 int retVal = 1;
1685 status_t err;
1686 sp<AaptAssets> assets;
1687 int N;
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001688 FILE* fp;
1689 String8 dependencyFile;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001690
1691 // -c zz_ZZ means do pseudolocalization
1692 ResourceFilter filter;
1693 err = filter.parse(bundle->getConfigurations());
1694 if (err != NO_ERROR) {
1695 goto bail;
1696 }
1697 if (filter.containsPseudo()) {
1698 bundle->setPseudolocalize(true);
1699 }
1700
1701 N = bundle->getFileSpecCount();
1702 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
1703 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDir() == NULL) {
1704 fprintf(stderr, "ERROR: no input files\n");
1705 goto bail;
1706 }
1707
1708 outputAPKFile = bundle->getOutputAPKFile();
1709
1710 // Make sure the filenames provided exist and are of the appropriate type.
1711 if (outputAPKFile) {
1712 FileType type;
1713 type = getFileType(outputAPKFile);
1714 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
1715 fprintf(stderr,
1716 "ERROR: output file '%s' exists but is not regular file\n",
1717 outputAPKFile);
1718 goto bail;
1719 }
1720 }
1721
1722 // Load the assets.
1723 assets = new AaptAssets();
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001724
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001725 // Set up the resource gathering in assets if we're going to generate
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001726 // dependency files. Every time we encounter a resource while slurping
1727 // the tree, we'll add it to these stores so we have full resource paths
1728 // to write to a dependency file.
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001729 if (bundle->getGenDependencies()) {
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001730 sp<FilePathStore> resPathStore = new FilePathStore;
1731 assets->setFullResPaths(resPathStore);
1732 sp<FilePathStore> assetPathStore = new FilePathStore;
1733 assets->setFullAssetPaths(assetPathStore);
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001734 }
1735
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001736 err = assets->slurpFromArgs(bundle);
1737 if (err < 0) {
1738 goto bail;
1739 }
1740
1741 if (bundle->getVerbose()) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001742 assets->print(String8());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001743 }
1744
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001745 // If they asked for any fileAs that need to be compiled, do so.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001746 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
1747 err = buildResources(bundle, assets);
1748 if (err != 0) {
1749 goto bail;
1750 }
1751 }
1752
1753 // At this point we've read everything and processed everything. From here
1754 // on out it's just writing output files.
1755 if (SourcePos::hasErrors()) {
1756 goto bail;
1757 }
1758
Dianne Hackborn1644c6d72012-02-06 15:33:21 -08001759 // Update symbols with information about which ones are needed as Java symbols.
1760 assets->applyJavaSymbols();
1761 if (SourcePos::hasErrors()) {
1762 goto bail;
1763 }
1764
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001765 // If we've been asked to generate a dependency file, do that here
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001766 if (bundle->getGenDependencies()) {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001767 // If this is the packaging step, generate the dependency file next to
1768 // the output apk (e.g. bin/resources.ap_.d)
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001769 if (outputAPKFile) {
1770 dependencyFile = String8(outputAPKFile);
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001771 // Add the .d extension to the dependency file.
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001772 dependencyFile.append(".d");
1773 } else {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001774 // Else if this is the R.java dependency generation step,
1775 // generate the dependency file in the R.java package subdirectory
1776 // e.g. gen/com/foo/app/R.java.d
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001777 dependencyFile = String8(bundle->getRClassDir());
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001778 dependencyFile.appendPath("R.java.d");
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001779 }
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001780 // Make sure we have a clean dependency file to start with
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001781 fp = fopen(dependencyFile, "w");
1782 fclose(fp);
1783 }
1784
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001785 // Write out R.java constants
Dianne Hackborn1644c6d72012-02-06 15:33:21 -08001786 if (!assets->havePrivateSymbols()) {
Xavier Ducrohet63459ad2009-11-30 18:05:10 -08001787 if (bundle->getCustomPackage() == NULL) {
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001788 // Write the R.java file into the appropriate class directory
1789 // e.g. gen/com/foo/app/R.java
Xavier Ducrohet63459ad2009-11-30 18:05:10 -08001790 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
1791 } else {
1792 const String8 customPkg(bundle->getCustomPackage());
1793 err = writeResourceSymbols(bundle, assets, customPkg, true);
1794 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001795 if (err < 0) {
1796 goto bail;
1797 }
Ying Wang002f5372012-04-25 18:53:49 -07001798 // If we have library files, we're going to write our R.java file into
1799 // the appropriate class directory for those libraries as well.
1800 // e.g. gen/com/foo/app/lib/R.java
1801 if (bundle->getExtraPackages() != NULL) {
1802 // Split on colon
1803 String8 libs(bundle->getExtraPackages());
1804 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
1805 while (packageString != NULL) {
1806 // Write the R.java file out with the correct package name
1807 err = writeResourceSymbols(bundle, assets, String8(packageString), true);
1808 if (err < 0) {
1809 goto bail;
1810 }
1811 packageString = strtok(NULL, ":");
1812 }
1813 libs.unlockBuffer();
1814 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001815 } else {
1816 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
1817 if (err < 0) {
1818 goto bail;
1819 }
1820 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
1821 if (err < 0) {
1822 goto bail;
1823 }
1824 }
1825
Joe Onorato1553c822009-08-30 13:36:22 -07001826 // Write out the ProGuard file
1827 err = writeProguardFile(bundle, assets);
1828 if (err < 0) {
1829 goto bail;
1830 }
1831
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001832 // Write the apk
1833 if (outputAPKFile) {
1834 err = writeAPK(bundle, assets, String8(outputAPKFile));
1835 if (err != NO_ERROR) {
1836 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
1837 goto bail;
1838 }
1839 }
1840
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001841 // If we've been asked to generate a dependency file, we need to finish up here.
1842 // the writeResourceSymbols and writeAPK functions have already written the target
1843 // half of the dependency file, now we need to write the prerequisites. (files that
1844 // the R.java file or .ap_ file depend on)
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001845 if (bundle->getGenDependencies()) {
1846 // Now that writeResourceSymbols or writeAPK has taken care of writing
1847 // the targets to our dependency file, we'll write the prereqs
1848 fp = fopen(dependencyFile, "a+");
1849 fprintf(fp, " : ");
1850 bool includeRaw = (outputAPKFile != NULL);
1851 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07001852 // Also manually add the AndroidManifeset since it's not under res/ or assets/
1853 // and therefore was not added to our pathstores during slurping
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001854 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
1855 fclose(fp);
1856 }
1857
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001858 retVal = 0;
1859bail:
1860 if (SourcePos::hasErrors()) {
1861 SourcePos::printErrors(stderr);
1862 }
1863 return retVal;
1864}
Josiah Gaskin8a39da82011-06-06 17:00:35 -07001865
1866/*
1867 * Do PNG Crunching
1868 * PRECONDITIONS
1869 * -S flag points to a source directory containing drawable* folders
1870 * -C flag points to destination directory. The folder structure in the
1871 * source directory will be mirrored to the destination (cache) directory
1872 *
1873 * POSTCONDITIONS
1874 * Destination directory will be updated to match the PNG files in
1875 * the source directory.
1876 */
1877int doCrunch(Bundle* bundle)
1878{
1879 fprintf(stdout, "Crunching PNG Files in ");
1880 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
1881 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
1882
1883 updatePreProcessedCache(bundle);
1884
1885 return NO_ERROR;
1886}
Dan Morrille74763e2012-01-06 10:47:10 -08001887
Xavier Ducrohetb1f6ad82012-12-21 09:54:02 -08001888/*
1889 * Do PNG Crunching on a single flag
1890 * -i points to a single png file
1891 * -o points to a single png output file
1892 */
1893int doSingleCrunch(Bundle* bundle)
1894{
1895 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
1896 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
1897
1898 String8 input(bundle->getSingleCrunchInputFile());
1899 String8 output(bundle->getSingleCrunchOutputFile());
Xavier Ducrohetb7de2192013-01-15 16:41:29 -08001900
Xavier Ducrohetc75d3f52013-01-14 18:02:40 -08001901 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
1902 // we can't return the status_t as it gets truncate to the lower 8 bits.
1903 return 42;
1904 }
Xavier Ducrohetb7de2192013-01-15 16:41:29 -08001905
Xavier Ducrohetc75d3f52013-01-14 18:02:40 -08001906 return NO_ERROR;
Xavier Ducrohetb1f6ad82012-12-21 09:54:02 -08001907}
1908
Dan Morrille74763e2012-01-06 10:47:10 -08001909char CONSOLE_DATA[2925] = {
1910 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1911 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1912 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1913 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1914 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
1915 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
1916 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1917 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1918 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
1919 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
1920 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1921 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1922 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
1923 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1924 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1925 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
1926 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
1927 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1928 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1929 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
1930 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
1931 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
1932 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
1933 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
1934 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1935 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1936 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
1937 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
1938 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
1939 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1940 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
1941 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1942 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1943 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
1944 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
1945 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1946 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1947 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
1948 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
1949 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
1950 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1951 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
1952 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1953 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1954 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
1955 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
1956 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
1957 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
1958 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
1959 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1960 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
1961 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
1962 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
1963 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1964 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
1965 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
1966 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
1967 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
1968 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
1969 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
1970 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
1971 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
1972 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
1973 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
1974 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
1975 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
1976 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
1977 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
1978 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
1979 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
1980 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
1981 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
1982 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1983 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
1984 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
1985 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
1986 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1987 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
1988 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
1989 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1990 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
1991 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
1992 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
1993 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1994 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
1995 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
1996 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1997 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
1998 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
1999 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2000 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2001 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2002 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2003 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2004 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2005 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2006 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2007 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2008 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2009 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2010 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2011 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2012 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2013 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2014 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2015 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2016 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2017 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2018 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2019 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2020 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2021 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2022 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2023 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2024 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2025 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2026 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2027 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2028 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2029 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2030 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2031 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2032 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2033 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2034 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2035 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2036 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2037 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2038 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2039 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2040 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2041 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2042 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2043 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2044 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2045 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2046 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2047 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2048 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2049 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2050 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2051 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2052 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2053 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2054 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2055 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2056 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2057 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2058 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2059 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2060 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2061 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2062 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2063 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2064 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2065 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2066 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2067 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2068 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2069 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2070 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2071 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2072 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2073 };