blob: d8215e77ae889c3a781d54c5a7a1645be591123a [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"
8#include "ResourceTable.h"
9#include "XMLNode.h"
10
Mathias Agopian3b4062e2009-05-31 19:13:00 -070011#include <utils/Log.h>
12#include <utils/threads.h>
13#include <utils/List.h>
14#include <utils/Errors.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080015
16#include <fcntl.h>
17#include <errno.h>
18
19using namespace android;
20
21/*
22 * Show version info. All the cool kids do it.
23 */
24int doVersion(Bundle* bundle)
25{
26 if (bundle->getFileSpecCount() != 0)
27 printf("(ignoring extra arguments)\n");
28 printf("Android Asset Packaging Tool, v0.2\n");
29
30 return 0;
31}
32
33
34/*
35 * Open the file read only. The call fails if the file doesn't exist.
36 *
37 * Returns NULL on failure.
38 */
39ZipFile* openReadOnly(const char* fileName)
40{
41 ZipFile* zip;
42 status_t result;
43
44 zip = new ZipFile;
45 result = zip->open(fileName, ZipFile::kOpenReadOnly);
46 if (result != NO_ERROR) {
47 if (result == NAME_NOT_FOUND)
48 fprintf(stderr, "ERROR: '%s' not found\n", fileName);
49 else if (result == PERMISSION_DENIED)
50 fprintf(stderr, "ERROR: '%s' access denied\n", fileName);
51 else
52 fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n",
53 fileName);
54 delete zip;
55 return NULL;
56 }
57
58 return zip;
59}
60
61/*
62 * Open the file read-write. The file will be created if it doesn't
63 * already exist and "okayToCreate" is set.
64 *
65 * Returns NULL on failure.
66 */
67ZipFile* openReadWrite(const char* fileName, bool okayToCreate)
68{
69 ZipFile* zip = NULL;
70 status_t result;
71 int flags;
72
73 flags = ZipFile::kOpenReadWrite;
74 if (okayToCreate)
75 flags |= ZipFile::kOpenCreate;
76
77 zip = new ZipFile;
78 result = zip->open(fileName, flags);
79 if (result != NO_ERROR) {
80 delete zip;
81 zip = NULL;
82 goto bail;
83 }
84
85bail:
86 return zip;
87}
88
89
90/*
91 * Return a short string describing the compression method.
92 */
93const char* compressionName(int method)
94{
95 if (method == ZipEntry::kCompressStored)
96 return "Stored";
97 else if (method == ZipEntry::kCompressDeflated)
98 return "Deflated";
99 else
100 return "Unknown";
101}
102
103/*
104 * Return the percent reduction in size (0% == no compression).
105 */
106int calcPercent(long uncompressedLen, long compressedLen)
107{
108 if (!uncompressedLen)
109 return 0;
110 else
111 return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5);
112}
113
114/*
115 * Handle the "list" command, which can be a simple file dump or
116 * a verbose listing.
117 *
118 * The verbose listing closely matches the output of the Info-ZIP "unzip"
119 * command.
120 */
121int doList(Bundle* bundle)
122{
123 int result = 1;
124 ZipFile* zip = NULL;
125 const ZipEntry* entry;
126 long totalUncLen, totalCompLen;
127 const char* zipFileName;
128
129 if (bundle->getFileSpecCount() != 1) {
130 fprintf(stderr, "ERROR: specify zip file name (only)\n");
131 goto bail;
132 }
133 zipFileName = bundle->getFileSpecEntry(0);
134
135 zip = openReadOnly(zipFileName);
136 if (zip == NULL)
137 goto bail;
138
139 int count, i;
140
141 if (bundle->getVerbose()) {
142 printf("Archive: %s\n", zipFileName);
143 printf(
144 " Length Method Size Ratio Date Time CRC-32 Name\n");
145 printf(
146 "-------- ------ ------- ----- ---- ---- ------ ----\n");
147 }
148
149 totalUncLen = totalCompLen = 0;
150
151 count = zip->getNumEntries();
152 for (i = 0; i < count; i++) {
153 entry = zip->getEntryByIndex(i);
154 if (bundle->getVerbose()) {
155 char dateBuf[32];
156 time_t when;
157
158 when = entry->getModWhen();
159 strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M",
160 localtime(&when));
161
162 printf("%8ld %-7.7s %7ld %3d%% %s %08lx %s\n",
163 (long) entry->getUncompressedLen(),
164 compressionName(entry->getCompressionMethod()),
165 (long) entry->getCompressedLen(),
166 calcPercent(entry->getUncompressedLen(),
167 entry->getCompressedLen()),
168 dateBuf,
169 entry->getCRC32(),
170 entry->getFileName());
171 } else {
172 printf("%s\n", entry->getFileName());
173 }
174
175 totalUncLen += entry->getUncompressedLen();
176 totalCompLen += entry->getCompressedLen();
177 }
178
179 if (bundle->getVerbose()) {
180 printf(
181 "-------- ------- --- -------\n");
182 printf("%8ld %7ld %2d%% %d files\n",
183 totalUncLen,
184 totalCompLen,
185 calcPercent(totalUncLen, totalCompLen),
186 zip->getNumEntries());
187 }
188
189 if (bundle->getAndroidList()) {
190 AssetManager assets;
191 if (!assets.addAssetPath(String8(zipFileName), NULL)) {
192 fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n");
193 goto bail;
194 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700195
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196 const ResTable& res = assets.getResources(false);
197 if (&res == NULL) {
198 printf("\nNo resource table found.\n");
199 } else {
200 printf("\nResource table:\n");
Dianne Hackborne17086b2009-06-19 15:13:28 -0700201 res.print(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800202 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700203
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204 Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
205 Asset::ACCESS_BUFFER);
206 if (manifestAsset == NULL) {
207 printf("\nNo AndroidManifest.xml found.\n");
208 } else {
209 printf("\nAndroid manifest:\n");
210 ResXMLTree tree;
211 tree.setTo(manifestAsset->getBuffer(true),
212 manifestAsset->getLength());
213 printXMLBlock(&tree);
214 }
215 delete manifestAsset;
216 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700217
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218 result = 0;
219
220bail:
221 delete zip;
222 return result;
223}
224
225static ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes)
226{
227 size_t N = tree.getAttributeCount();
228 for (size_t i=0; i<N; i++) {
229 if (tree.getAttributeNameResID(i) == attrRes) {
230 return (ssize_t)i;
231 }
232 }
233 return -1;
234}
235
236static String8 getAttribute(const ResXMLTree& tree, const char* ns,
237 const char* attr, String8* outError)
238{
239 ssize_t idx = tree.indexOfAttribute(ns, attr);
240 if (idx < 0) {
241 return String8();
242 }
243 Res_value value;
244 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
245 if (value.dataType != Res_value::TYPE_STRING) {
246 if (outError != NULL) *outError = "attribute is not a string value";
247 return String8();
248 }
249 }
250 size_t len;
251 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
252 return str ? String8(str, len) : String8();
253}
254
255static String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError)
256{
257 ssize_t idx = indexOfAttribute(tree, attrRes);
258 if (idx < 0) {
259 return String8();
260 }
261 Res_value value;
262 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
263 if (value.dataType != Res_value::TYPE_STRING) {
264 if (outError != NULL) *outError = "attribute is not a string value";
265 return String8();
266 }
267 }
268 size_t len;
269 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
270 return str ? String8(str, len) : String8();
271}
272
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700273static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes,
274 String8* outError, int32_t defValue = -1)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800275{
276 ssize_t idx = indexOfAttribute(tree, attrRes);
277 if (idx < 0) {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700278 return defValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800279 }
280 Res_value value;
281 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700282 if (value.dataType < Res_value::TYPE_FIRST_INT
283 || value.dataType > Res_value::TYPE_LAST_INT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800284 if (outError != NULL) *outError = "attribute is not an integer value";
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700285 return defValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800286 }
287 }
288 return value.data;
289}
290
291static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree,
292 uint32_t attrRes, String8* outError)
293{
294 ssize_t idx = indexOfAttribute(tree, attrRes);
295 if (idx < 0) {
296 return String8();
297 }
298 Res_value value;
299 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
300 if (value.dataType == Res_value::TYPE_STRING) {
301 size_t len;
302 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
303 return str ? String8(str, len) : String8();
304 }
305 resTable->resolveReference(&value, 0);
306 if (value.dataType != Res_value::TYPE_STRING) {
307 if (outError != NULL) *outError = "attribute is not a string value";
308 return String8();
309 }
310 }
311 size_t len;
312 const Res_value* value2 = &value;
313 const char16_t* str = const_cast<ResTable*>(resTable)->valueToString(value2, 0, NULL, &len);
314 return str ? String8(str, len) : String8();
315}
316
317// These are attribute resource constants for the platform, as found
318// in android.R.attr
319enum {
320 NAME_ATTR = 0x01010003,
321 VERSION_CODE_ATTR = 0x0101021b,
322 VERSION_NAME_ATTR = 0x0101021c,
323 LABEL_ATTR = 0x01010001,
324 ICON_ATTR = 0x01010002,
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700325 MIN_SDK_VERSION_ATTR = 0x0101020c,
Suchi Amalapurapu75c49842009-08-14 15:13:09 -0700326 MAX_SDK_VERSION_ATTR = 0x01010271,
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700327 REQ_TOUCH_SCREEN_ATTR = 0x01010227,
328 REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
329 REQ_HARD_KEYBOARD_ATTR = 0x01010229,
330 REQ_NAVIGATION_ATTR = 0x0101022a,
331 REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
332 TARGET_SDK_VERSION_ATTR = 0x01010270,
333 TEST_ONLY_ATTR = 0x01010272,
334 DENSITY_ATTR = 0x0101026c,
Dianne Hackborn723738c2009-06-25 19:48:04 -0700335 SMALL_SCREEN_ATTR = 0x01010284,
336 NORMAL_SCREEN_ATTR = 0x01010285,
337 LARGE_SCREEN_ATTR = 0x01010286,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800338};
339
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700340const char *getComponentName(String8 &pkgName, String8 &componentName) {
341 ssize_t idx = componentName.find(".");
342 String8 retStr(pkgName);
343 if (idx == 0) {
344 retStr += componentName;
345 } else if (idx < 0) {
346 retStr += ".";
347 retStr += componentName;
348 } else {
349 return componentName.string();
350 }
351 return retStr.string();
352}
353
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800354/*
355 * Handle the "dump" command, to extract select data from an archive.
356 */
357int doDump(Bundle* bundle)
358{
359 status_t result = UNKNOWN_ERROR;
360 Asset* asset = NULL;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700361
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362 if (bundle->getFileSpecCount() < 1) {
363 fprintf(stderr, "ERROR: no dump option specified\n");
364 return 1;
365 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700366
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800367 if (bundle->getFileSpecCount() < 2) {
368 fprintf(stderr, "ERROR: no dump file specified\n");
369 return 1;
370 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700371
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800372 const char* option = bundle->getFileSpecEntry(0);
373 const char* filename = bundle->getFileSpecEntry(1);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700374
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800375 AssetManager assets;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700376 void* assetsCookie;
377 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800378 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
379 return 1;
380 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700381
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800382 const ResTable& res = assets.getResources(false);
383 if (&res == NULL) {
384 fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
385 goto bail;
386 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700387
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800388 if (strcmp("resources", option) == 0) {
Dianne Hackborne17086b2009-06-19 15:13:28 -0700389 res.print(bundle->getValues());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700390
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800391 } else if (strcmp("xmltree", option) == 0) {
392 if (bundle->getFileSpecCount() < 3) {
393 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
394 goto bail;
395 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700396
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800397 for (int i=2; i<bundle->getFileSpecCount(); i++) {
398 const char* resname = bundle->getFileSpecEntry(i);
399 ResXMLTree tree;
400 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
401 if (asset == NULL) {
402 fprintf(stderr, "ERROR: dump failed because resource %p found\n", resname);
403 goto bail;
404 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700405
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800406 if (tree.setTo(asset->getBuffer(true),
407 asset->getLength()) != NO_ERROR) {
408 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
409 goto bail;
410 }
411 tree.restart();
412 printXMLBlock(&tree);
413 delete asset;
414 asset = NULL;
415 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700416
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800417 } else if (strcmp("xmlstrings", option) == 0) {
418 if (bundle->getFileSpecCount() < 3) {
419 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
420 goto bail;
421 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700422
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800423 for (int i=2; i<bundle->getFileSpecCount(); i++) {
424 const char* resname = bundle->getFileSpecEntry(i);
425 ResXMLTree tree;
426 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
427 if (asset == NULL) {
428 fprintf(stderr, "ERROR: dump failed because resource %p found\n", resname);
429 goto bail;
430 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700431
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800432 if (tree.setTo(asset->getBuffer(true),
433 asset->getLength()) != NO_ERROR) {
434 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
435 goto bail;
436 }
437 printStringPool(&tree.getStrings());
438 delete asset;
439 asset = NULL;
440 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700441
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800442 } else {
443 ResXMLTree tree;
444 asset = assets.openNonAsset("AndroidManifest.xml",
445 Asset::ACCESS_BUFFER);
446 if (asset == NULL) {
447 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
448 goto bail;
449 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700450
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800451 if (tree.setTo(asset->getBuffer(true),
452 asset->getLength()) != NO_ERROR) {
453 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
454 goto bail;
455 }
456 tree.restart();
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700457
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800458 if (strcmp("permissions", option) == 0) {
459 size_t len;
460 ResXMLTree::event_code_t code;
461 int depth = 0;
462 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
463 if (code == ResXMLTree::END_TAG) {
464 depth--;
465 continue;
466 }
467 if (code != ResXMLTree::START_TAG) {
468 continue;
469 }
470 depth++;
471 String8 tag(tree.getElementName(&len));
472 //printf("Depth %d tag %s\n", depth, tag.string());
473 if (depth == 1) {
474 if (tag != "manifest") {
475 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
476 goto bail;
477 }
478 String8 pkg = getAttribute(tree, NULL, "package", NULL);
479 printf("package: %s\n", pkg.string());
480 } else if (depth == 2 && tag == "permission") {
481 String8 error;
482 String8 name = getAttribute(tree, NAME_ATTR, &error);
483 if (error != "") {
484 fprintf(stderr, "ERROR: %s\n", error.string());
485 goto bail;
486 }
487 printf("permission: %s\n", name.string());
488 } else if (depth == 2 && tag == "uses-permission") {
489 String8 error;
490 String8 name = getAttribute(tree, NAME_ATTR, &error);
491 if (error != "") {
492 fprintf(stderr, "ERROR: %s\n", error.string());
493 goto bail;
494 }
495 printf("uses-permission: %s\n", name.string());
496 }
497 }
498 } else if (strcmp("badging", option) == 0) {
499 size_t len;
500 ResXMLTree::event_code_t code;
501 int depth = 0;
502 String8 error;
503 bool withinActivity = false;
504 bool isMainActivity = false;
505 bool isLauncherActivity = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700506 bool isSearchable = false;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700507 bool withinApplication = false;
508 bool withinReceiver = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700509 bool withinService = false;
510 bool withinIntentFilter = false;
511 bool hasMainActivity = false;
512 bool hasOtherActivities = false;
513 bool hasOtherReceivers = false;
514 bool hasOtherServices = false;
515 bool hasWallpaperService = false;
516 bool hasImeService = false;
517 bool hasWidgetReceivers = false;
518 bool hasIntentFilter = false;
519 bool actMainActivity = false;
520 bool actWidgetReceivers = false;
521 bool actImeService = false;
522 bool actWallpaperService = false;
Dianne Hackborn723738c2009-06-25 19:48:04 -0700523 int targetSdk = 0;
524 int smallScreen = 1;
525 int normalScreen = 1;
526 int largeScreen = 1;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700527 String8 pkg;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800528 String8 activityName;
529 String8 activityLabel;
530 String8 activityIcon;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700531 String8 receiverName;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700532 String8 serviceName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800533 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
534 if (code == ResXMLTree::END_TAG) {
535 depth--;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700536 if (depth < 2) {
537 withinApplication = false;
538 } else if (depth < 3) {
539 if (withinActivity && isMainActivity && isLauncherActivity) {
540 const char *aName = getComponentName(pkg, activityName);
541 if (aName != NULL) {
542 printf("launchable activity name='%s'", aName);
543 }
544 printf("label='%s' icon='%s'\n",
545 activityLabel.string(),
546 activityIcon.string());
547 }
548 if (!hasIntentFilter) {
549 hasOtherActivities |= withinActivity;
550 hasOtherReceivers |= withinReceiver;
551 hasOtherServices |= withinService;
552 }
553 withinActivity = false;
554 withinService = false;
555 withinReceiver = false;
556 hasIntentFilter = false;
557 isMainActivity = isLauncherActivity = false;
558 } else if (depth < 4) {
559 if (withinIntentFilter) {
560 if (withinActivity) {
561 hasMainActivity |= actMainActivity;
562 hasOtherActivities |= !actMainActivity;
563 } else if (withinReceiver) {
564 hasWidgetReceivers |= actWidgetReceivers;
565 hasOtherReceivers |= !actWidgetReceivers;
566 } else if (withinService) {
567 hasImeService |= actImeService;
568 hasWallpaperService |= actWallpaperService;
569 hasOtherServices |= (!actImeService && !actWallpaperService);
570 }
571 }
572 withinIntentFilter = false;
573 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800574 continue;
575 }
576 if (code != ResXMLTree::START_TAG) {
577 continue;
578 }
579 depth++;
580 String8 tag(tree.getElementName(&len));
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700581 //printf("Depth %d, %s\n", depth, tag.string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800582 if (depth == 1) {
583 if (tag != "manifest") {
584 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
585 goto bail;
586 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700587 pkg = getAttribute(tree, NULL, "package", NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800588 printf("package: name='%s' ", pkg.string());
589 int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
590 if (error != "") {
591 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
592 goto bail;
593 }
594 if (versionCode > 0) {
595 printf("versionCode='%d' ", versionCode);
596 } else {
597 printf("versionCode='' ");
598 }
599 String8 versionName = getAttribute(tree, VERSION_NAME_ATTR, &error);
600 if (error != "") {
601 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
602 goto bail;
603 }
604 printf("versionName='%s'\n", versionName.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700605 } else if (depth == 2) {
606 withinApplication = false;
607 if (tag == "application") {
608 withinApplication = true;
609 String8 label = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
610 if (error != "") {
611 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string());
612 goto bail;
613 }
614 printf("application: label='%s' ", label.string());
615 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
616 if (error != "") {
617 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
618 goto bail;
619 }
620 printf("icon='%s'\n", icon.string());
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700621 int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700622 if (error != "") {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700623 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700624 goto bail;
625 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700626 if (testOnly != 0) {
627 printf("testOnly='%d'\n", testOnly);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700628 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700629 } else if (tag == "uses-sdk") {
630 int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
631 if (error != "") {
632 error = "";
633 String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
634 if (error != "") {
635 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
636 error.string());
637 goto bail;
638 }
Dianne Hackborn723738c2009-06-25 19:48:04 -0700639 if (name == "Donut") targetSdk = 4;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700640 printf("sdkVersion:'%s'\n", name.string());
641 } else if (code != -1) {
Dianne Hackborn723738c2009-06-25 19:48:04 -0700642 targetSdk = code;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700643 printf("sdkVersion:'%d'\n", code);
644 }
Suchi Amalapurapu75c49842009-08-14 15:13:09 -0700645 code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
646 if (code != -1) {
647 printf("maxSdkVersion:'%d'\n", code);
648 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700649 code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
650 if (error != "") {
651 error = "";
652 String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
653 if (error != "") {
654 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
655 error.string());
656 goto bail;
657 }
Dianne Hackborn723738c2009-06-25 19:48:04 -0700658 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700659 printf("targetSdkVersion:'%s'\n", name.string());
660 } else if (code != -1) {
Dianne Hackborn723738c2009-06-25 19:48:04 -0700661 if (targetSdk < code) {
662 targetSdk = code;
663 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700664 printf("targetSdkVersion:'%d'\n", code);
665 }
666 } else if (tag == "uses-configuration") {
667 int32_t reqTouchScreen = getIntegerAttribute(tree,
668 REQ_TOUCH_SCREEN_ATTR, NULL, 0);
669 int32_t reqKeyboardType = getIntegerAttribute(tree,
670 REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
671 int32_t reqHardKeyboard = getIntegerAttribute(tree,
672 REQ_HARD_KEYBOARD_ATTR, NULL, 0);
673 int32_t reqNavigation = getIntegerAttribute(tree,
674 REQ_NAVIGATION_ATTR, NULL, 0);
675 int32_t reqFiveWayNav = getIntegerAttribute(tree,
676 REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
677 printf("uses-configuation:");
678 if (reqTouchScreen != 0) {
679 printf(" reqTouchScreen='%d'", reqTouchScreen);
680 }
681 if (reqKeyboardType != 0) {
682 printf(" reqKeyboardType='%d'", reqKeyboardType);
683 }
684 if (reqHardKeyboard != 0) {
685 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
686 }
687 if (reqNavigation != 0) {
688 printf(" reqNavigation='%d'", reqNavigation);
689 }
690 if (reqFiveWayNav != 0) {
691 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
692 }
693 printf("\n");
694 } else if (tag == "supports-density") {
695 int32_t dens = getIntegerAttribute(tree, DENSITY_ATTR, &error);
696 if (error != "") {
697 fprintf(stderr, "ERROR getting 'android:density' attribute: %s\n",
698 error.string());
699 goto bail;
700 }
701 printf("supports-density:'%d'\n", dens);
Dianne Hackborn723738c2009-06-25 19:48:04 -0700702 } else if (tag == "supports-screens") {
703 smallScreen = getIntegerAttribute(tree,
704 SMALL_SCREEN_ATTR, NULL, 1);
705 normalScreen = getIntegerAttribute(tree,
706 NORMAL_SCREEN_ATTR, NULL, 1);
707 largeScreen = getIntegerAttribute(tree,
708 LARGE_SCREEN_ATTR, NULL, 1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800709 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700710 } else if (depth == 3 && withinApplication) {
711 withinActivity = false;
712 withinReceiver = false;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700713 withinService = false;
714 hasIntentFilter = false;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700715 if(tag == "activity") {
716 withinActivity = true;
717 activityName = getAttribute(tree, NAME_ATTR, &error);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800718 if (error != "") {
719 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
720 goto bail;
721 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700722
723 activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800724 if (error != "") {
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700725 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800726 goto bail;
727 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700728
729 activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
730 if (error != "") {
731 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
732 goto bail;
733 }
734 } else if (tag == "uses-library") {
735 String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
736 if (error != "") {
737 fprintf(stderr, "ERROR getting 'android:name' attribute for uses-library: %s\n", error.string());
738 goto bail;
739 }
740 printf("uses-library:'%s'\n", libraryName.string());
741 } else if (tag == "receiver") {
742 withinReceiver = true;
743 receiverName = getAttribute(tree, NAME_ATTR, &error);
744
745 if (error != "") {
746 fprintf(stderr, "ERROR getting 'android:name' attribute for receiver: %s\n", error.string());
747 goto bail;
748 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700749 } else if (tag == "service") {
750 withinService = true;
751 serviceName = getAttribute(tree, NAME_ATTR, &error);
752
753 if (error != "") {
754 fprintf(stderr, "ERROR getting 'android:name' attribute for service: %s\n", error.string());
755 goto bail;
756 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700757 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700758 } else if ((depth == 4) && (tag == "intent-filter")) {
759 hasIntentFilter = true;
760 withinIntentFilter = true;
761 actMainActivity = actWidgetReceivers = actImeService = actWallpaperService = false;
762 } else if ((depth == 5) && withinIntentFilter){
763 String8 action;
764 if (tag == "action") {
765 action = getAttribute(tree, NAME_ATTR, &error);
766 if (error != "") {
767 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
768 goto bail;
769 }
770 if (withinActivity) {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700771 if (action == "android.intent.action.MAIN") {
772 isMainActivity = true;
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700773 actMainActivity = true;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700774 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700775 } else if (withinReceiver) {
776 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
777 actWidgetReceivers = true;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700778 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700779 } else if (withinService) {
780 if (action == "android.view.InputMethod") {
781 actImeService = true;
782 } else if (action == "android.service.wallpaper.WallpaperService") {
783 actWallpaperService = true;
784 }
785 }
786 if (action == "android.intent.action.SEARCH") {
787 isSearchable = true;
788 }
789 }
790
791 if (tag == "category") {
792 String8 category = getAttribute(tree, NAME_ATTR, &error);
793 if (error != "") {
794 fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string());
795 goto bail;
796 }
797 if (withinActivity) {
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700798 if (category == "android.intent.category.LAUNCHER") {
799 isLauncherActivity = true;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700800 }
801 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800802 }
803 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800804 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700805
806 if (hasMainActivity) {
807 printf("main\n");
808 }
809 if (hasWidgetReceivers) {
810 printf("app-widget\n");
811 }
812 if (hasImeService) {
813 printf("ime\n");
814 }
815 if (hasWallpaperService) {
816 printf("wallpaper\n");
817 }
818 if (hasOtherActivities) {
819 printf("other-activities\n");
820 }
821 if (isSearchable) {
822 printf("search\n");
823 }
824 if (hasOtherReceivers) {
825 printf("other-receivers\n");
826 }
827 if (hasOtherServices) {
828 printf("other-services\n");
829 }
830
Dianne Hackborn723738c2009-06-25 19:48:04 -0700831 // Determine default values for any unspecified screen sizes,
832 // based on the target SDK of the package. As of 4 (donut)
833 // the screen size support was introduced, so all default to
834 // enabled.
835 if (smallScreen > 0) {
836 smallScreen = targetSdk >= 4 ? -1 : 0;
837 }
838 if (normalScreen > 0) {
839 normalScreen = -1;
840 }
841 if (largeScreen > 0) {
842 largeScreen = targetSdk >= 4 ? -1 : 0;
843 }
844 printf("supports-screens:");
845 if (smallScreen != 0) printf(" 'small'");
846 if (normalScreen != 0) printf(" 'normal'");
847 if (largeScreen != 0) printf(" 'large'");
848 printf("\n");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700849
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800850 printf("locales:");
851 Vector<String8> locales;
852 res.getLocales(&locales);
Dianne Hackborne17086b2009-06-19 15:13:28 -0700853 const size_t NL = locales.size();
854 for (size_t i=0; i<NL; i++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800855 const char* localeStr = locales[i].string();
856 if (localeStr == NULL || strlen(localeStr) == 0) {
857 localeStr = "--_--";
858 }
859 printf(" '%s'", localeStr);
860 }
861 printf("\n");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700862
Dianne Hackborne17086b2009-06-19 15:13:28 -0700863 Vector<ResTable_config> configs;
864 res.getConfigurations(&configs);
865 SortedVector<int> densities;
866 const size_t NC = configs.size();
867 for (size_t i=0; i<NC; i++) {
868 int dens = configs[i].density;
869 if (dens == 0) dens = 160;
870 densities.add(dens);
871 }
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700872
Dianne Hackborne17086b2009-06-19 15:13:28 -0700873 printf("densities:");
874 const size_t ND = densities.size();
875 for (size_t i=0; i<ND; i++) {
876 printf(" '%d'", densities[i]);
877 }
878 printf("\n");
Suchi Amalapurapu1b125982009-08-18 01:42:27 -0700879
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700880 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
881 if (dir != NULL) {
882 if (dir->getFileCount() > 0) {
883 printf("native-code:");
884 for (size_t i=0; i<dir->getFileCount(); i++) {
885 printf(" '%s'", dir->getFileName(i).string());
886 }
887 printf("\n");
888 }
889 delete dir;
890 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800891 } else if (strcmp("configurations", option) == 0) {
892 Vector<ResTable_config> configs;
893 res.getConfigurations(&configs);
894 const size_t N = configs.size();
895 for (size_t i=0; i<N; i++) {
896 printf("%s\n", configs[i].toString().string());
897 }
898 } else {
899 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
900 goto bail;
901 }
902 }
903
904 result = NO_ERROR;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700905
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800906bail:
907 if (asset) {
908 delete asset;
909 }
910 return (result != NO_ERROR);
911}
912
913
914/*
915 * Handle the "add" command, which wants to add files to a new or
916 * pre-existing archive.
917 */
918int doAdd(Bundle* bundle)
919{
920 ZipFile* zip = NULL;
921 status_t result = UNKNOWN_ERROR;
922 const char* zipFileName;
923
924 if (bundle->getUpdate()) {
925 /* avoid confusion */
926 fprintf(stderr, "ERROR: can't use '-u' with add\n");
927 goto bail;
928 }
929
930 if (bundle->getFileSpecCount() < 1) {
931 fprintf(stderr, "ERROR: must specify zip file name\n");
932 goto bail;
933 }
934 zipFileName = bundle->getFileSpecEntry(0);
935
936 if (bundle->getFileSpecCount() < 2) {
937 fprintf(stderr, "NOTE: nothing to do\n");
938 goto bail;
939 }
940
941 zip = openReadWrite(zipFileName, true);
942 if (zip == NULL) {
943 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
944 goto bail;
945 }
946
947 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
948 const char* fileName = bundle->getFileSpecEntry(i);
949
950 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
951 printf(" '%s'... (from gzip)\n", fileName);
952 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
953 } else {
954 printf(" '%s'...\n", fileName);
955 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
956 }
957 if (result != NO_ERROR) {
958 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
959 if (result == NAME_NOT_FOUND)
960 fprintf(stderr, ": file not found\n");
961 else if (result == ALREADY_EXISTS)
962 fprintf(stderr, ": already exists in archive\n");
963 else
964 fprintf(stderr, "\n");
965 goto bail;
966 }
967 }
968
969 result = NO_ERROR;
970
971bail:
972 delete zip;
973 return (result != NO_ERROR);
974}
975
976
977/*
978 * Delete files from an existing archive.
979 */
980int doRemove(Bundle* bundle)
981{
982 ZipFile* zip = NULL;
983 status_t result = UNKNOWN_ERROR;
984 const char* zipFileName;
985
986 if (bundle->getFileSpecCount() < 1) {
987 fprintf(stderr, "ERROR: must specify zip file name\n");
988 goto bail;
989 }
990 zipFileName = bundle->getFileSpecEntry(0);
991
992 if (bundle->getFileSpecCount() < 2) {
993 fprintf(stderr, "NOTE: nothing to do\n");
994 goto bail;
995 }
996
997 zip = openReadWrite(zipFileName, false);
998 if (zip == NULL) {
999 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
1000 zipFileName);
1001 goto bail;
1002 }
1003
1004 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1005 const char* fileName = bundle->getFileSpecEntry(i);
1006 ZipEntry* entry;
1007
1008 entry = zip->getEntryByName(fileName);
1009 if (entry == NULL) {
1010 printf(" '%s' NOT FOUND\n", fileName);
1011 continue;
1012 }
1013
1014 result = zip->remove(entry);
1015
1016 if (result != NO_ERROR) {
1017 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
1018 bundle->getFileSpecEntry(i), zipFileName);
1019 goto bail;
1020 }
1021 }
1022
1023 /* update the archive */
1024 zip->flush();
1025
1026bail:
1027 delete zip;
1028 return (result != NO_ERROR);
1029}
1030
1031
1032/*
1033 * Package up an asset directory and associated application files.
1034 */
1035int doPackage(Bundle* bundle)
1036{
1037 const char* outputAPKFile;
1038 int retVal = 1;
1039 status_t err;
1040 sp<AaptAssets> assets;
1041 int N;
1042
1043 // -c zz_ZZ means do pseudolocalization
1044 ResourceFilter filter;
1045 err = filter.parse(bundle->getConfigurations());
1046 if (err != NO_ERROR) {
1047 goto bail;
1048 }
1049 if (filter.containsPseudo()) {
1050 bundle->setPseudolocalize(true);
1051 }
1052
1053 N = bundle->getFileSpecCount();
1054 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
1055 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDir() == NULL) {
1056 fprintf(stderr, "ERROR: no input files\n");
1057 goto bail;
1058 }
1059
1060 outputAPKFile = bundle->getOutputAPKFile();
1061
1062 // Make sure the filenames provided exist and are of the appropriate type.
1063 if (outputAPKFile) {
1064 FileType type;
1065 type = getFileType(outputAPKFile);
1066 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
1067 fprintf(stderr,
1068 "ERROR: output file '%s' exists but is not regular file\n",
1069 outputAPKFile);
1070 goto bail;
1071 }
1072 }
1073
1074 // Load the assets.
1075 assets = new AaptAssets();
1076 err = assets->slurpFromArgs(bundle);
1077 if (err < 0) {
1078 goto bail;
1079 }
1080
1081 if (bundle->getVerbose()) {
1082 assets->print();
1083 }
1084
1085 // If they asked for any files that need to be compiled, do so.
1086 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
1087 err = buildResources(bundle, assets);
1088 if (err != 0) {
1089 goto bail;
1090 }
1091 }
1092
1093 // At this point we've read everything and processed everything. From here
1094 // on out it's just writing output files.
1095 if (SourcePos::hasErrors()) {
1096 goto bail;
1097 }
1098
1099 // Write out R.java constants
1100 if (assets->getPackage() == assets->getSymbolsPrivatePackage()) {
1101 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
1102 if (err < 0) {
1103 goto bail;
1104 }
1105 } else {
1106 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
1107 if (err < 0) {
1108 goto bail;
1109 }
1110 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
1111 if (err < 0) {
1112 goto bail;
1113 }
1114 }
1115
1116 // Write the apk
1117 if (outputAPKFile) {
1118 err = writeAPK(bundle, assets, String8(outputAPKFile));
1119 if (err != NO_ERROR) {
1120 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
1121 goto bail;
1122 }
1123 }
1124
1125 retVal = 0;
1126bail:
1127 if (SourcePos::hasErrors()) {
1128 SourcePos::printErrors(stderr);
1129 }
1130 return retVal;
1131}