blob: 32df6317e2884973ab362e230d54e4b779c3ed23 [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#include <utils/ZipFile.h>
16
17#include <fcntl.h>
18#include <errno.h>
19
20using namespace android;
21
22/*
23 * Show version info. All the cool kids do it.
24 */
25int doVersion(Bundle* bundle)
26{
27 if (bundle->getFileSpecCount() != 0)
28 printf("(ignoring extra arguments)\n");
29 printf("Android Asset Packaging Tool, v0.2\n");
30
31 return 0;
32}
33
34
35/*
36 * Open the file read only. The call fails if the file doesn't exist.
37 *
38 * Returns NULL on failure.
39 */
40ZipFile* openReadOnly(const char* fileName)
41{
42 ZipFile* zip;
43 status_t result;
44
45 zip = new ZipFile;
46 result = zip->open(fileName, ZipFile::kOpenReadOnly);
47 if (result != NO_ERROR) {
48 if (result == NAME_NOT_FOUND)
49 fprintf(stderr, "ERROR: '%s' not found\n", fileName);
50 else if (result == PERMISSION_DENIED)
51 fprintf(stderr, "ERROR: '%s' access denied\n", fileName);
52 else
53 fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n",
54 fileName);
55 delete zip;
56 return NULL;
57 }
58
59 return zip;
60}
61
62/*
63 * Open the file read-write. The file will be created if it doesn't
64 * already exist and "okayToCreate" is set.
65 *
66 * Returns NULL on failure.
67 */
68ZipFile* openReadWrite(const char* fileName, bool okayToCreate)
69{
70 ZipFile* zip = NULL;
71 status_t result;
72 int flags;
73
74 flags = ZipFile::kOpenReadWrite;
75 if (okayToCreate)
76 flags |= ZipFile::kOpenCreate;
77
78 zip = new ZipFile;
79 result = zip->open(fileName, flags);
80 if (result != NO_ERROR) {
81 delete zip;
82 zip = NULL;
83 goto bail;
84 }
85
86bail:
87 return zip;
88}
89
90
91/*
92 * Return a short string describing the compression method.
93 */
94const char* compressionName(int method)
95{
96 if (method == ZipEntry::kCompressStored)
97 return "Stored";
98 else if (method == ZipEntry::kCompressDeflated)
99 return "Deflated";
100 else
101 return "Unknown";
102}
103
104/*
105 * Return the percent reduction in size (0% == no compression).
106 */
107int calcPercent(long uncompressedLen, long compressedLen)
108{
109 if (!uncompressedLen)
110 return 0;
111 else
112 return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5);
113}
114
115/*
116 * Handle the "list" command, which can be a simple file dump or
117 * a verbose listing.
118 *
119 * The verbose listing closely matches the output of the Info-ZIP "unzip"
120 * command.
121 */
122int doList(Bundle* bundle)
123{
124 int result = 1;
125 ZipFile* zip = NULL;
126 const ZipEntry* entry;
127 long totalUncLen, totalCompLen;
128 const char* zipFileName;
129
130 if (bundle->getFileSpecCount() != 1) {
131 fprintf(stderr, "ERROR: specify zip file name (only)\n");
132 goto bail;
133 }
134 zipFileName = bundle->getFileSpecEntry(0);
135
136 zip = openReadOnly(zipFileName);
137 if (zip == NULL)
138 goto bail;
139
140 int count, i;
141
142 if (bundle->getVerbose()) {
143 printf("Archive: %s\n", zipFileName);
144 printf(
145 " Length Method Size Ratio Date Time CRC-32 Name\n");
146 printf(
147 "-------- ------ ------- ----- ---- ---- ------ ----\n");
148 }
149
150 totalUncLen = totalCompLen = 0;
151
152 count = zip->getNumEntries();
153 for (i = 0; i < count; i++) {
154 entry = zip->getEntryByIndex(i);
155 if (bundle->getVerbose()) {
156 char dateBuf[32];
157 time_t when;
158
159 when = entry->getModWhen();
160 strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M",
161 localtime(&when));
162
163 printf("%8ld %-7.7s %7ld %3d%% %s %08lx %s\n",
164 (long) entry->getUncompressedLen(),
165 compressionName(entry->getCompressionMethod()),
166 (long) entry->getCompressedLen(),
167 calcPercent(entry->getUncompressedLen(),
168 entry->getCompressedLen()),
169 dateBuf,
170 entry->getCRC32(),
171 entry->getFileName());
172 } else {
173 printf("%s\n", entry->getFileName());
174 }
175
176 totalUncLen += entry->getUncompressedLen();
177 totalCompLen += entry->getCompressedLen();
178 }
179
180 if (bundle->getVerbose()) {
181 printf(
182 "-------- ------- --- -------\n");
183 printf("%8ld %7ld %2d%% %d files\n",
184 totalUncLen,
185 totalCompLen,
186 calcPercent(totalUncLen, totalCompLen),
187 zip->getNumEntries());
188 }
189
190 if (bundle->getAndroidList()) {
191 AssetManager assets;
192 if (!assets.addAssetPath(String8(zipFileName), NULL)) {
193 fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n");
194 goto bail;
195 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700196
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800197 const ResTable& res = assets.getResources(false);
198 if (&res == NULL) {
199 printf("\nNo resource table found.\n");
200 } else {
201 printf("\nResource table:\n");
202 res.print();
203 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700204
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800205 Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
206 Asset::ACCESS_BUFFER);
207 if (manifestAsset == NULL) {
208 printf("\nNo AndroidManifest.xml found.\n");
209 } else {
210 printf("\nAndroid manifest:\n");
211 ResXMLTree tree;
212 tree.setTo(manifestAsset->getBuffer(true),
213 manifestAsset->getLength());
214 printXMLBlock(&tree);
215 }
216 delete manifestAsset;
217 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700218
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800219 result = 0;
220
221bail:
222 delete zip;
223 return result;
224}
225
226static ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes)
227{
228 size_t N = tree.getAttributeCount();
229 for (size_t i=0; i<N; i++) {
230 if (tree.getAttributeNameResID(i) == attrRes) {
231 return (ssize_t)i;
232 }
233 }
234 return -1;
235}
236
237static String8 getAttribute(const ResXMLTree& tree, const char* ns,
238 const char* attr, String8* outError)
239{
240 ssize_t idx = tree.indexOfAttribute(ns, attr);
241 if (idx < 0) {
242 return String8();
243 }
244 Res_value value;
245 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
246 if (value.dataType != Res_value::TYPE_STRING) {
247 if (outError != NULL) *outError = "attribute is not a string value";
248 return String8();
249 }
250 }
251 size_t len;
252 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
253 return str ? String8(str, len) : String8();
254}
255
256static String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError)
257{
258 ssize_t idx = indexOfAttribute(tree, attrRes);
259 if (idx < 0) {
260 return String8();
261 }
262 Res_value value;
263 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
264 if (value.dataType != Res_value::TYPE_STRING) {
265 if (outError != NULL) *outError = "attribute is not a string value";
266 return String8();
267 }
268 }
269 size_t len;
270 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
271 return str ? String8(str, len) : String8();
272}
273
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700274static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes,
275 String8* outError, int32_t defValue = -1)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800276{
277 ssize_t idx = indexOfAttribute(tree, attrRes);
278 if (idx < 0) {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700279 return defValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800280 }
281 Res_value value;
282 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700283 if (value.dataType < Res_value::TYPE_FIRST_INT
284 || value.dataType > Res_value::TYPE_LAST_INT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800285 if (outError != NULL) *outError = "attribute is not an integer value";
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700286 return defValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800287 }
288 }
289 return value.data;
290}
291
292static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree,
293 uint32_t attrRes, String8* outError)
294{
295 ssize_t idx = indexOfAttribute(tree, attrRes);
296 if (idx < 0) {
297 return String8();
298 }
299 Res_value value;
300 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
301 if (value.dataType == Res_value::TYPE_STRING) {
302 size_t len;
303 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
304 return str ? String8(str, len) : String8();
305 }
306 resTable->resolveReference(&value, 0);
307 if (value.dataType != Res_value::TYPE_STRING) {
308 if (outError != NULL) *outError = "attribute is not a string value";
309 return String8();
310 }
311 }
312 size_t len;
313 const Res_value* value2 = &value;
314 const char16_t* str = const_cast<ResTable*>(resTable)->valueToString(value2, 0, NULL, &len);
315 return str ? String8(str, len) : String8();
316}
317
318// These are attribute resource constants for the platform, as found
319// in android.R.attr
320enum {
321 NAME_ATTR = 0x01010003,
322 VERSION_CODE_ATTR = 0x0101021b,
323 VERSION_NAME_ATTR = 0x0101021c,
324 LABEL_ATTR = 0x01010001,
325 ICON_ATTR = 0x01010002,
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700326 MIN_SDK_VERSION_ATTR = 0x0101020c,
327 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,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800335};
336
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700337const char *getComponentName(String8 &pkgName, String8 &componentName) {
338 ssize_t idx = componentName.find(".");
339 String8 retStr(pkgName);
340 if (idx == 0) {
341 retStr += componentName;
342 } else if (idx < 0) {
343 retStr += ".";
344 retStr += componentName;
345 } else {
346 return componentName.string();
347 }
348 return retStr.string();
349}
350
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800351/*
352 * Handle the "dump" command, to extract select data from an archive.
353 */
354int doDump(Bundle* bundle)
355{
356 status_t result = UNKNOWN_ERROR;
357 Asset* asset = NULL;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700358
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800359 if (bundle->getFileSpecCount() < 1) {
360 fprintf(stderr, "ERROR: no dump option specified\n");
361 return 1;
362 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700363
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800364 if (bundle->getFileSpecCount() < 2) {
365 fprintf(stderr, "ERROR: no dump file specified\n");
366 return 1;
367 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700368
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800369 const char* option = bundle->getFileSpecEntry(0);
370 const char* filename = bundle->getFileSpecEntry(1);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700371
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800372 AssetManager assets;
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700373 void* assetsCookie;
374 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800375 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
376 return 1;
377 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700378
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800379 const ResTable& res = assets.getResources(false);
380 if (&res == NULL) {
381 fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
382 goto bail;
383 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700384
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800385 if (strcmp("resources", option) == 0) {
386 res.print();
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700387
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800388 } else if (strcmp("xmltree", option) == 0) {
389 if (bundle->getFileSpecCount() < 3) {
390 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
391 goto bail;
392 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700393
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800394 for (int i=2; i<bundle->getFileSpecCount(); i++) {
395 const char* resname = bundle->getFileSpecEntry(i);
396 ResXMLTree tree;
397 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
398 if (asset == NULL) {
399 fprintf(stderr, "ERROR: dump failed because resource %p found\n", resname);
400 goto bail;
401 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700402
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800403 if (tree.setTo(asset->getBuffer(true),
404 asset->getLength()) != NO_ERROR) {
405 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
406 goto bail;
407 }
408 tree.restart();
409 printXMLBlock(&tree);
410 delete asset;
411 asset = NULL;
412 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700413
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800414 } else if (strcmp("xmlstrings", option) == 0) {
415 if (bundle->getFileSpecCount() < 3) {
416 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
417 goto bail;
418 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700419
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800420 for (int i=2; i<bundle->getFileSpecCount(); i++) {
421 const char* resname = bundle->getFileSpecEntry(i);
422 ResXMLTree tree;
423 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
424 if (asset == NULL) {
425 fprintf(stderr, "ERROR: dump failed because resource %p found\n", resname);
426 goto bail;
427 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700428
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800429 if (tree.setTo(asset->getBuffer(true),
430 asset->getLength()) != NO_ERROR) {
431 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
432 goto bail;
433 }
434 printStringPool(&tree.getStrings());
435 delete asset;
436 asset = NULL;
437 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700438
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800439 } else {
440 ResXMLTree tree;
441 asset = assets.openNonAsset("AndroidManifest.xml",
442 Asset::ACCESS_BUFFER);
443 if (asset == NULL) {
444 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
445 goto bail;
446 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700447
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800448 if (tree.setTo(asset->getBuffer(true),
449 asset->getLength()) != NO_ERROR) {
450 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
451 goto bail;
452 }
453 tree.restart();
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700454
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800455 if (strcmp("permissions", option) == 0) {
456 size_t len;
457 ResXMLTree::event_code_t code;
458 int depth = 0;
459 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
460 if (code == ResXMLTree::END_TAG) {
461 depth--;
462 continue;
463 }
464 if (code != ResXMLTree::START_TAG) {
465 continue;
466 }
467 depth++;
468 String8 tag(tree.getElementName(&len));
469 //printf("Depth %d tag %s\n", depth, tag.string());
470 if (depth == 1) {
471 if (tag != "manifest") {
472 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
473 goto bail;
474 }
475 String8 pkg = getAttribute(tree, NULL, "package", NULL);
476 printf("package: %s\n", pkg.string());
477 } else if (depth == 2 && tag == "permission") {
478 String8 error;
479 String8 name = getAttribute(tree, NAME_ATTR, &error);
480 if (error != "") {
481 fprintf(stderr, "ERROR: %s\n", error.string());
482 goto bail;
483 }
484 printf("permission: %s\n", name.string());
485 } else if (depth == 2 && tag == "uses-permission") {
486 String8 error;
487 String8 name = getAttribute(tree, NAME_ATTR, &error);
488 if (error != "") {
489 fprintf(stderr, "ERROR: %s\n", error.string());
490 goto bail;
491 }
492 printf("uses-permission: %s\n", name.string());
493 }
494 }
495 } else if (strcmp("badging", option) == 0) {
496 size_t len;
497 ResXMLTree::event_code_t code;
498 int depth = 0;
499 String8 error;
500 bool withinActivity = false;
501 bool isMainActivity = false;
502 bool isLauncherActivity = false;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700503 bool withinApplication = false;
504 bool withinReceiver = false;
505 String8 pkg;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800506 String8 activityName;
507 String8 activityLabel;
508 String8 activityIcon;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700509 String8 receiverName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800510 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
511 if (code == ResXMLTree::END_TAG) {
512 depth--;
513 continue;
514 }
515 if (code != ResXMLTree::START_TAG) {
516 continue;
517 }
518 depth++;
519 String8 tag(tree.getElementName(&len));
520 //printf("Depth %d tag %s\n", depth, tag.string());
521 if (depth == 1) {
522 if (tag != "manifest") {
523 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
524 goto bail;
525 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700526 pkg = getAttribute(tree, NULL, "package", NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800527 printf("package: name='%s' ", pkg.string());
528 int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
529 if (error != "") {
530 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
531 goto bail;
532 }
533 if (versionCode > 0) {
534 printf("versionCode='%d' ", versionCode);
535 } else {
536 printf("versionCode='' ");
537 }
538 String8 versionName = getAttribute(tree, VERSION_NAME_ATTR, &error);
539 if (error != "") {
540 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
541 goto bail;
542 }
543 printf("versionName='%s'\n", versionName.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700544 } else if (depth == 2) {
545 withinApplication = false;
546 if (tag == "application") {
547 withinApplication = true;
548 String8 label = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
549 if (error != "") {
550 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string());
551 goto bail;
552 }
553 printf("application: label='%s' ", label.string());
554 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
555 if (error != "") {
556 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
557 goto bail;
558 }
559 printf("icon='%s'\n", icon.string());
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700560 int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700561 if (error != "") {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700562 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700563 goto bail;
564 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700565 if (testOnly != 0) {
566 printf("testOnly='%d'\n", testOnly);
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700567 }
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700568 } else if (tag == "uses-sdk") {
569 int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
570 if (error != "") {
571 error = "";
572 String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
573 if (error != "") {
574 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
575 error.string());
576 goto bail;
577 }
578 printf("sdkVersion:'%s'\n", name.string());
579 } else if (code != -1) {
580 printf("sdkVersion:'%d'\n", code);
581 }
582 code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
583 if (error != "") {
584 error = "";
585 String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
586 if (error != "") {
587 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
588 error.string());
589 goto bail;
590 }
591 printf("targetSdkVersion:'%s'\n", name.string());
592 } else if (code != -1) {
593 printf("targetSdkVersion:'%d'\n", code);
594 }
595 } else if (tag == "uses-configuration") {
596 int32_t reqTouchScreen = getIntegerAttribute(tree,
597 REQ_TOUCH_SCREEN_ATTR, NULL, 0);
598 int32_t reqKeyboardType = getIntegerAttribute(tree,
599 REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
600 int32_t reqHardKeyboard = getIntegerAttribute(tree,
601 REQ_HARD_KEYBOARD_ATTR, NULL, 0);
602 int32_t reqNavigation = getIntegerAttribute(tree,
603 REQ_NAVIGATION_ATTR, NULL, 0);
604 int32_t reqFiveWayNav = getIntegerAttribute(tree,
605 REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
606 printf("uses-configuation:");
607 if (reqTouchScreen != 0) {
608 printf(" reqTouchScreen='%d'", reqTouchScreen);
609 }
610 if (reqKeyboardType != 0) {
611 printf(" reqKeyboardType='%d'", reqKeyboardType);
612 }
613 if (reqHardKeyboard != 0) {
614 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
615 }
616 if (reqNavigation != 0) {
617 printf(" reqNavigation='%d'", reqNavigation);
618 }
619 if (reqFiveWayNav != 0) {
620 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
621 }
622 printf("\n");
623 } else if (tag == "supports-density") {
624 int32_t dens = getIntegerAttribute(tree, DENSITY_ATTR, &error);
625 if (error != "") {
626 fprintf(stderr, "ERROR getting 'android:density' attribute: %s\n",
627 error.string());
628 goto bail;
629 }
630 printf("supports-density:'%d'\n", dens);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800631 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700632 } else if (depth == 3 && withinApplication) {
633 withinActivity = false;
634 withinReceiver = false;
635 if(tag == "activity") {
636 withinActivity = true;
637 activityName = getAttribute(tree, NAME_ATTR, &error);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800638 if (error != "") {
639 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
640 goto bail;
641 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700642
643 activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800644 if (error != "") {
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700645 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800646 goto bail;
647 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700648
649 activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
650 if (error != "") {
651 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
652 goto bail;
653 }
654 } else if (tag == "uses-library") {
655 String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
656 if (error != "") {
657 fprintf(stderr, "ERROR getting 'android:name' attribute for uses-library: %s\n", error.string());
658 goto bail;
659 }
660 printf("uses-library:'%s'\n", libraryName.string());
661 } else if (tag == "receiver") {
662 withinReceiver = true;
663 receiverName = getAttribute(tree, NAME_ATTR, &error);
664
665 if (error != "") {
666 fprintf(stderr, "ERROR getting 'android:name' attribute for receiver: %s\n", error.string());
667 goto bail;
668 }
669 }
670 } else if (depth == 5) {
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700671 if (withinActivity) {
672 if (tag == "action") {
673 //printf("LOG: action tag\n");
674 String8 action = getAttribute(tree, NAME_ATTR, &error);
675 if (error != "") {
676 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
677 goto bail;
678 }
679 if (action == "android.intent.action.MAIN") {
680 isMainActivity = true;
681 //printf("LOG: isMainActivity==true\n");
682 }
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700683 } else if (tag == "category") {
684 String8 category = getAttribute(tree, NAME_ATTR, &error);
685 if (error != "") {
686 fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string());
687 goto bail;
688 }
689 if (category == "android.intent.category.LAUNCHER") {
690 isLauncherActivity = true;
691 //printf("LOG: isLauncherActivity==true\n");
692 }
693 }
694 } else if (withinReceiver) {
695 if (tag == "action") {
696 String8 action = getAttribute(tree, NAME_ATTR, &error);
697 if (error != "") {
698 fprintf(stderr, "ERROR getting 'android:name' attribute for receiver: %s\n", error.string());
699 goto bail;
700 }
701 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
702 const char *rName = getComponentName(pkg, receiverName);
703 if (rName != NULL) {
704 printf("gadget-receiver:'%s/%s'\n", pkg.string(), rName);
705 }
706 }
707 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800708 }
709 }
710
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700711 if (depth < 2) {
712 withinApplication = false;
713 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800714 if (depth < 3) {
715 //if (withinActivity) printf("LOG: withinActivity==false\n");
716 withinActivity = false;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700717 withinReceiver = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800718 }
719
720 if (depth < 5) {
721 //if (isMainActivity) printf("LOG: isMainActivity==false\n");
722 //if (isLauncherActivity) printf("LOG: isLauncherActivity==false\n");
723 isMainActivity = false;
724 isLauncherActivity = false;
725 }
726
727 if (withinActivity && isMainActivity && isLauncherActivity) {
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700728 printf("launchable activity:");
729 const char *aName = getComponentName(pkg, activityName);
730 if (aName != NULL) {
731 printf(" name='%s'", aName);
732 }
733 printf("label='%s' icon='%s'\n",
734 activityLabel.string(),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800735 activityIcon.string());
736 }
737 }
738 printf("locales:");
739 Vector<String8> locales;
740 res.getLocales(&locales);
741 const size_t N = locales.size();
742 for (size_t i=0; i<N; i++) {
743 const char* localeStr = locales[i].string();
744 if (localeStr == NULL || strlen(localeStr) == 0) {
745 localeStr = "--_--";
746 }
747 printf(" '%s'", localeStr);
748 }
749 printf("\n");
Dianne Hackbornbb9ea302009-05-18 15:22:00 -0700750 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
751 if (dir != NULL) {
752 if (dir->getFileCount() > 0) {
753 printf("native-code:");
754 for (size_t i=0; i<dir->getFileCount(); i++) {
755 printf(" '%s'", dir->getFileName(i).string());
756 }
757 printf("\n");
758 }
759 delete dir;
760 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800761 } else if (strcmp("configurations", option) == 0) {
762 Vector<ResTable_config> configs;
763 res.getConfigurations(&configs);
764 const size_t N = configs.size();
765 for (size_t i=0; i<N; i++) {
766 printf("%s\n", configs[i].toString().string());
767 }
768 } else {
769 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
770 goto bail;
771 }
772 }
773
774 result = NO_ERROR;
Suchi Amalapurapu7ef189d2009-04-02 15:20:29 -0700775
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800776bail:
777 if (asset) {
778 delete asset;
779 }
780 return (result != NO_ERROR);
781}
782
783
784/*
785 * Handle the "add" command, which wants to add files to a new or
786 * pre-existing archive.
787 */
788int doAdd(Bundle* bundle)
789{
790 ZipFile* zip = NULL;
791 status_t result = UNKNOWN_ERROR;
792 const char* zipFileName;
793
794 if (bundle->getUpdate()) {
795 /* avoid confusion */
796 fprintf(stderr, "ERROR: can't use '-u' with add\n");
797 goto bail;
798 }
799
800 if (bundle->getFileSpecCount() < 1) {
801 fprintf(stderr, "ERROR: must specify zip file name\n");
802 goto bail;
803 }
804 zipFileName = bundle->getFileSpecEntry(0);
805
806 if (bundle->getFileSpecCount() < 2) {
807 fprintf(stderr, "NOTE: nothing to do\n");
808 goto bail;
809 }
810
811 zip = openReadWrite(zipFileName, true);
812 if (zip == NULL) {
813 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
814 goto bail;
815 }
816
817 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
818 const char* fileName = bundle->getFileSpecEntry(i);
819
820 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
821 printf(" '%s'... (from gzip)\n", fileName);
822 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
823 } else {
824 printf(" '%s'...\n", fileName);
825 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
826 }
827 if (result != NO_ERROR) {
828 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
829 if (result == NAME_NOT_FOUND)
830 fprintf(stderr, ": file not found\n");
831 else if (result == ALREADY_EXISTS)
832 fprintf(stderr, ": already exists in archive\n");
833 else
834 fprintf(stderr, "\n");
835 goto bail;
836 }
837 }
838
839 result = NO_ERROR;
840
841bail:
842 delete zip;
843 return (result != NO_ERROR);
844}
845
846
847/*
848 * Delete files from an existing archive.
849 */
850int doRemove(Bundle* bundle)
851{
852 ZipFile* zip = NULL;
853 status_t result = UNKNOWN_ERROR;
854 const char* zipFileName;
855
856 if (bundle->getFileSpecCount() < 1) {
857 fprintf(stderr, "ERROR: must specify zip file name\n");
858 goto bail;
859 }
860 zipFileName = bundle->getFileSpecEntry(0);
861
862 if (bundle->getFileSpecCount() < 2) {
863 fprintf(stderr, "NOTE: nothing to do\n");
864 goto bail;
865 }
866
867 zip = openReadWrite(zipFileName, false);
868 if (zip == NULL) {
869 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
870 zipFileName);
871 goto bail;
872 }
873
874 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
875 const char* fileName = bundle->getFileSpecEntry(i);
876 ZipEntry* entry;
877
878 entry = zip->getEntryByName(fileName);
879 if (entry == NULL) {
880 printf(" '%s' NOT FOUND\n", fileName);
881 continue;
882 }
883
884 result = zip->remove(entry);
885
886 if (result != NO_ERROR) {
887 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
888 bundle->getFileSpecEntry(i), zipFileName);
889 goto bail;
890 }
891 }
892
893 /* update the archive */
894 zip->flush();
895
896bail:
897 delete zip;
898 return (result != NO_ERROR);
899}
900
901
902/*
903 * Package up an asset directory and associated application files.
904 */
905int doPackage(Bundle* bundle)
906{
907 const char* outputAPKFile;
908 int retVal = 1;
909 status_t err;
910 sp<AaptAssets> assets;
911 int N;
912
913 // -c zz_ZZ means do pseudolocalization
914 ResourceFilter filter;
915 err = filter.parse(bundle->getConfigurations());
916 if (err != NO_ERROR) {
917 goto bail;
918 }
919 if (filter.containsPseudo()) {
920 bundle->setPseudolocalize(true);
921 }
922
923 N = bundle->getFileSpecCount();
924 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
925 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDir() == NULL) {
926 fprintf(stderr, "ERROR: no input files\n");
927 goto bail;
928 }
929
930 outputAPKFile = bundle->getOutputAPKFile();
931
932 // Make sure the filenames provided exist and are of the appropriate type.
933 if (outputAPKFile) {
934 FileType type;
935 type = getFileType(outputAPKFile);
936 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
937 fprintf(stderr,
938 "ERROR: output file '%s' exists but is not regular file\n",
939 outputAPKFile);
940 goto bail;
941 }
942 }
943
944 // Load the assets.
945 assets = new AaptAssets();
946 err = assets->slurpFromArgs(bundle);
947 if (err < 0) {
948 goto bail;
949 }
950
951 if (bundle->getVerbose()) {
952 assets->print();
953 }
954
955 // If they asked for any files that need to be compiled, do so.
956 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
957 err = buildResources(bundle, assets);
958 if (err != 0) {
959 goto bail;
960 }
961 }
962
963 // At this point we've read everything and processed everything. From here
964 // on out it's just writing output files.
965 if (SourcePos::hasErrors()) {
966 goto bail;
967 }
968
969 // Write out R.java constants
970 if (assets->getPackage() == assets->getSymbolsPrivatePackage()) {
971 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
972 if (err < 0) {
973 goto bail;
974 }
975 } else {
976 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
977 if (err < 0) {
978 goto bail;
979 }
980 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
981 if (err < 0) {
982 goto bail;
983 }
984 }
985
986 // Write the apk
987 if (outputAPKFile) {
988 err = writeAPK(bundle, assets, String8(outputAPKFile));
989 if (err != NO_ERROR) {
990 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
991 goto bail;
992 }
993 }
994
995 retVal = 0;
996bail:
997 if (SourcePos::hasErrors()) {
998 SourcePos::printErrors(stderr);
999 }
1000 return retVal;
1001}