]> git.saurik.com Git - android/aapt.git/blob - Command.cpp
Fix aapt to be able to add configs from overlays.
[android/aapt.git] / Command.cpp
1 //
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
11 #include <utils/Log.h>
12 #include <utils/threads.h>
13 #include <utils/List.h>
14 #include <utils/Errors.h>
15
16 #include <fcntl.h>
17 #include <errno.h>
18
19 using namespace android;
20
21 /*
22 * Show version info. All the cool kids do it.
23 */
24 int 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 */
39 ZipFile* 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 */
67 ZipFile* 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
85 bail:
86 return zip;
87 }
88
89
90 /*
91 * Return a short string describing the compression method.
92 */
93 const 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 */
106 int 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 */
121 int 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 }
195
196 const ResTable& res = assets.getResources(false);
197 if (&res == NULL) {
198 printf("\nNo resource table found.\n");
199 } else {
200 printf("\nResource table:\n");
201 res.print(false);
202 }
203
204 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 }
217
218 result = 0;
219
220 bail:
221 delete zip;
222 return result;
223 }
224
225 static 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
236 static 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
255 static 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
273 static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes,
274 String8* outError, int32_t defValue = -1)
275 {
276 ssize_t idx = indexOfAttribute(tree, attrRes);
277 if (idx < 0) {
278 return defValue;
279 }
280 Res_value value;
281 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
282 if (value.dataType < Res_value::TYPE_FIRST_INT
283 || value.dataType > Res_value::TYPE_LAST_INT) {
284 if (outError != NULL) *outError = "attribute is not an integer value";
285 return defValue;
286 }
287 }
288 return value.data;
289 }
290
291 static 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
319 enum {
320 NAME_ATTR = 0x01010003,
321 VERSION_CODE_ATTR = 0x0101021b,
322 VERSION_NAME_ATTR = 0x0101021c,
323 LABEL_ATTR = 0x01010001,
324 ICON_ATTR = 0x01010002,
325 MIN_SDK_VERSION_ATTR = 0x0101020c,
326 REQ_TOUCH_SCREEN_ATTR = 0x01010227,
327 REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
328 REQ_HARD_KEYBOARD_ATTR = 0x01010229,
329 REQ_NAVIGATION_ATTR = 0x0101022a,
330 REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
331 TARGET_SDK_VERSION_ATTR = 0x01010270,
332 TEST_ONLY_ATTR = 0x01010272,
333 DENSITY_ATTR = 0x0101026c,
334 SMALL_SCREEN_ATTR = 0x01010284,
335 NORMAL_SCREEN_ATTR = 0x01010285,
336 LARGE_SCREEN_ATTR = 0x01010286,
337 };
338
339 const char *getComponentName(String8 &pkgName, String8 &componentName) {
340 ssize_t idx = componentName.find(".");
341 String8 retStr(pkgName);
342 if (idx == 0) {
343 retStr += componentName;
344 } else if (idx < 0) {
345 retStr += ".";
346 retStr += componentName;
347 } else {
348 return componentName.string();
349 }
350 return retStr.string();
351 }
352
353 /*
354 * Handle the "dump" command, to extract select data from an archive.
355 */
356 int doDump(Bundle* bundle)
357 {
358 status_t result = UNKNOWN_ERROR;
359 Asset* asset = NULL;
360
361 if (bundle->getFileSpecCount() < 1) {
362 fprintf(stderr, "ERROR: no dump option specified\n");
363 return 1;
364 }
365
366 if (bundle->getFileSpecCount() < 2) {
367 fprintf(stderr, "ERROR: no dump file specified\n");
368 return 1;
369 }
370
371 const char* option = bundle->getFileSpecEntry(0);
372 const char* filename = bundle->getFileSpecEntry(1);
373
374 AssetManager assets;
375 void* assetsCookie;
376 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
377 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
378 return 1;
379 }
380
381 const ResTable& res = assets.getResources(false);
382 if (&res == NULL) {
383 fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
384 goto bail;
385 }
386
387 if (strcmp("resources", option) == 0) {
388 res.print(bundle->getValues());
389
390 } else if (strcmp("xmltree", option) == 0) {
391 if (bundle->getFileSpecCount() < 3) {
392 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
393 goto bail;
394 }
395
396 for (int i=2; i<bundle->getFileSpecCount(); i++) {
397 const char* resname = bundle->getFileSpecEntry(i);
398 ResXMLTree tree;
399 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
400 if (asset == NULL) {
401 fprintf(stderr, "ERROR: dump failed because resource %p found\n", resname);
402 goto bail;
403 }
404
405 if (tree.setTo(asset->getBuffer(true),
406 asset->getLength()) != NO_ERROR) {
407 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
408 goto bail;
409 }
410 tree.restart();
411 printXMLBlock(&tree);
412 delete asset;
413 asset = NULL;
414 }
415
416 } else if (strcmp("xmlstrings", option) == 0) {
417 if (bundle->getFileSpecCount() < 3) {
418 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
419 goto bail;
420 }
421
422 for (int i=2; i<bundle->getFileSpecCount(); i++) {
423 const char* resname = bundle->getFileSpecEntry(i);
424 ResXMLTree tree;
425 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
426 if (asset == NULL) {
427 fprintf(stderr, "ERROR: dump failed because resource %p found\n", resname);
428 goto bail;
429 }
430
431 if (tree.setTo(asset->getBuffer(true),
432 asset->getLength()) != NO_ERROR) {
433 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
434 goto bail;
435 }
436 printStringPool(&tree.getStrings());
437 delete asset;
438 asset = NULL;
439 }
440
441 } else {
442 ResXMLTree tree;
443 asset = assets.openNonAsset("AndroidManifest.xml",
444 Asset::ACCESS_BUFFER);
445 if (asset == NULL) {
446 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
447 goto bail;
448 }
449
450 if (tree.setTo(asset->getBuffer(true),
451 asset->getLength()) != NO_ERROR) {
452 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
453 goto bail;
454 }
455 tree.restart();
456
457 if (strcmp("permissions", option) == 0) {
458 size_t len;
459 ResXMLTree::event_code_t code;
460 int depth = 0;
461 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
462 if (code == ResXMLTree::END_TAG) {
463 depth--;
464 continue;
465 }
466 if (code != ResXMLTree::START_TAG) {
467 continue;
468 }
469 depth++;
470 String8 tag(tree.getElementName(&len));
471 //printf("Depth %d tag %s\n", depth, tag.string());
472 if (depth == 1) {
473 if (tag != "manifest") {
474 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
475 goto bail;
476 }
477 String8 pkg = getAttribute(tree, NULL, "package", NULL);
478 printf("package: %s\n", pkg.string());
479 } else if (depth == 2 && tag == "permission") {
480 String8 error;
481 String8 name = getAttribute(tree, NAME_ATTR, &error);
482 if (error != "") {
483 fprintf(stderr, "ERROR: %s\n", error.string());
484 goto bail;
485 }
486 printf("permission: %s\n", name.string());
487 } else if (depth == 2 && tag == "uses-permission") {
488 String8 error;
489 String8 name = getAttribute(tree, NAME_ATTR, &error);
490 if (error != "") {
491 fprintf(stderr, "ERROR: %s\n", error.string());
492 goto bail;
493 }
494 printf("uses-permission: %s\n", name.string());
495 }
496 }
497 } else if (strcmp("badging", option) == 0) {
498 size_t len;
499 ResXMLTree::event_code_t code;
500 int depth = 0;
501 String8 error;
502 bool withinActivity = false;
503 bool isMainActivity = false;
504 bool isLauncherActivity = false;
505 bool withinApplication = false;
506 bool withinReceiver = false;
507 int targetSdk = 0;
508 int smallScreen = 1;
509 int normalScreen = 1;
510 int largeScreen = 1;
511 String8 pkg;
512 String8 activityName;
513 String8 activityLabel;
514 String8 activityIcon;
515 String8 receiverName;
516 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
517 if (code == ResXMLTree::END_TAG) {
518 depth--;
519 continue;
520 }
521 if (code != ResXMLTree::START_TAG) {
522 continue;
523 }
524 depth++;
525 String8 tag(tree.getElementName(&len));
526 //printf("Depth %d tag %s\n", depth, tag.string());
527 if (depth == 1) {
528 if (tag != "manifest") {
529 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
530 goto bail;
531 }
532 pkg = getAttribute(tree, NULL, "package", NULL);
533 printf("package: name='%s' ", pkg.string());
534 int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
535 if (error != "") {
536 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
537 goto bail;
538 }
539 if (versionCode > 0) {
540 printf("versionCode='%d' ", versionCode);
541 } else {
542 printf("versionCode='' ");
543 }
544 String8 versionName = getAttribute(tree, VERSION_NAME_ATTR, &error);
545 if (error != "") {
546 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
547 goto bail;
548 }
549 printf("versionName='%s'\n", versionName.string());
550 } else if (depth == 2) {
551 withinApplication = false;
552 if (tag == "application") {
553 withinApplication = true;
554 String8 label = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
555 if (error != "") {
556 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string());
557 goto bail;
558 }
559 printf("application: label='%s' ", label.string());
560 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
561 if (error != "") {
562 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
563 goto bail;
564 }
565 printf("icon='%s'\n", icon.string());
566 int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
567 if (error != "") {
568 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
569 goto bail;
570 }
571 if (testOnly != 0) {
572 printf("testOnly='%d'\n", testOnly);
573 }
574 } else if (tag == "uses-sdk") {
575 int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
576 if (error != "") {
577 error = "";
578 String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
579 if (error != "") {
580 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
581 error.string());
582 goto bail;
583 }
584 if (name == "Donut") targetSdk = 4;
585 printf("sdkVersion:'%s'\n", name.string());
586 } else if (code != -1) {
587 targetSdk = code;
588 printf("sdkVersion:'%d'\n", code);
589 }
590 code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
591 if (error != "") {
592 error = "";
593 String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
594 if (error != "") {
595 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
596 error.string());
597 goto bail;
598 }
599 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
600 printf("targetSdkVersion:'%s'\n", name.string());
601 } else if (code != -1) {
602 if (targetSdk < code) {
603 targetSdk = code;
604 }
605 printf("targetSdkVersion:'%d'\n", code);
606 }
607 } else if (tag == "uses-configuration") {
608 int32_t reqTouchScreen = getIntegerAttribute(tree,
609 REQ_TOUCH_SCREEN_ATTR, NULL, 0);
610 int32_t reqKeyboardType = getIntegerAttribute(tree,
611 REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
612 int32_t reqHardKeyboard = getIntegerAttribute(tree,
613 REQ_HARD_KEYBOARD_ATTR, NULL, 0);
614 int32_t reqNavigation = getIntegerAttribute(tree,
615 REQ_NAVIGATION_ATTR, NULL, 0);
616 int32_t reqFiveWayNav = getIntegerAttribute(tree,
617 REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
618 printf("uses-configuation:");
619 if (reqTouchScreen != 0) {
620 printf(" reqTouchScreen='%d'", reqTouchScreen);
621 }
622 if (reqKeyboardType != 0) {
623 printf(" reqKeyboardType='%d'", reqKeyboardType);
624 }
625 if (reqHardKeyboard != 0) {
626 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
627 }
628 if (reqNavigation != 0) {
629 printf(" reqNavigation='%d'", reqNavigation);
630 }
631 if (reqFiveWayNav != 0) {
632 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
633 }
634 printf("\n");
635 } else if (tag == "supports-density") {
636 int32_t dens = getIntegerAttribute(tree, DENSITY_ATTR, &error);
637 if (error != "") {
638 fprintf(stderr, "ERROR getting 'android:density' attribute: %s\n",
639 error.string());
640 goto bail;
641 }
642 printf("supports-density:'%d'\n", dens);
643 } else if (tag == "supports-screens") {
644 smallScreen = getIntegerAttribute(tree,
645 SMALL_SCREEN_ATTR, NULL, 1);
646 normalScreen = getIntegerAttribute(tree,
647 NORMAL_SCREEN_ATTR, NULL, 1);
648 largeScreen = getIntegerAttribute(tree,
649 LARGE_SCREEN_ATTR, NULL, 1);
650 }
651 } else if (depth == 3 && withinApplication) {
652 withinActivity = false;
653 withinReceiver = false;
654 if(tag == "activity") {
655 withinActivity = true;
656 activityName = getAttribute(tree, NAME_ATTR, &error);
657 if (error != "") {
658 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
659 goto bail;
660 }
661
662 activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
663 if (error != "") {
664 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string());
665 goto bail;
666 }
667
668 activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
669 if (error != "") {
670 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
671 goto bail;
672 }
673 } else if (tag == "uses-library") {
674 String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
675 if (error != "") {
676 fprintf(stderr, "ERROR getting 'android:name' attribute for uses-library: %s\n", error.string());
677 goto bail;
678 }
679 printf("uses-library:'%s'\n", libraryName.string());
680 } else if (tag == "receiver") {
681 withinReceiver = true;
682 receiverName = getAttribute(tree, NAME_ATTR, &error);
683
684 if (error != "") {
685 fprintf(stderr, "ERROR getting 'android:name' attribute for receiver: %s\n", error.string());
686 goto bail;
687 }
688 }
689 } else if (depth == 5) {
690 if (withinActivity) {
691 if (tag == "action") {
692 //printf("LOG: action tag\n");
693 String8 action = getAttribute(tree, NAME_ATTR, &error);
694 if (error != "") {
695 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
696 goto bail;
697 }
698 if (action == "android.intent.action.MAIN") {
699 isMainActivity = true;
700 //printf("LOG: isMainActivity==true\n");
701 }
702 } else if (tag == "category") {
703 String8 category = getAttribute(tree, NAME_ATTR, &error);
704 if (error != "") {
705 fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string());
706 goto bail;
707 }
708 if (category == "android.intent.category.LAUNCHER") {
709 isLauncherActivity = true;
710 //printf("LOG: isLauncherActivity==true\n");
711 }
712 }
713 } else if (withinReceiver) {
714 if (tag == "action") {
715 String8 action = getAttribute(tree, NAME_ATTR, &error);
716 if (error != "") {
717 fprintf(stderr, "ERROR getting 'android:name' attribute for receiver: %s\n", error.string());
718 goto bail;
719 }
720 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
721 const char *rName = getComponentName(pkg, receiverName);
722 if (rName != NULL) {
723 printf("gadget-receiver:'%s/%s'\n", pkg.string(), rName);
724 }
725 }
726 }
727 }
728 }
729
730 if (depth < 2) {
731 withinApplication = false;
732 }
733 if (depth < 3) {
734 //if (withinActivity) printf("LOG: withinActivity==false\n");
735 withinActivity = false;
736 withinReceiver = false;
737 }
738
739 if (depth < 5) {
740 //if (isMainActivity) printf("LOG: isMainActivity==false\n");
741 //if (isLauncherActivity) printf("LOG: isLauncherActivity==false\n");
742 isMainActivity = false;
743 isLauncherActivity = false;
744 }
745
746 if (withinActivity && isMainActivity && isLauncherActivity) {
747 printf("launchable activity:");
748 const char *aName = getComponentName(pkg, activityName);
749 if (aName != NULL) {
750 printf(" name='%s'", aName);
751 }
752 printf("label='%s' icon='%s'\n",
753 activityLabel.string(),
754 activityIcon.string());
755 }
756 }
757
758 // Determine default values for any unspecified screen sizes,
759 // based on the target SDK of the package. As of 4 (donut)
760 // the screen size support was introduced, so all default to
761 // enabled.
762 if (smallScreen > 0) {
763 smallScreen = targetSdk >= 4 ? -1 : 0;
764 }
765 if (normalScreen > 0) {
766 normalScreen = -1;
767 }
768 if (largeScreen > 0) {
769 largeScreen = targetSdk >= 4 ? -1 : 0;
770 }
771 printf("supports-screens:");
772 if (smallScreen != 0) printf(" 'small'");
773 if (normalScreen != 0) printf(" 'normal'");
774 if (largeScreen != 0) printf(" 'large'");
775 printf("\n");
776
777 printf("locales:");
778 Vector<String8> locales;
779 res.getLocales(&locales);
780 const size_t NL = locales.size();
781 for (size_t i=0; i<NL; i++) {
782 const char* localeStr = locales[i].string();
783 if (localeStr == NULL || strlen(localeStr) == 0) {
784 localeStr = "--_--";
785 }
786 printf(" '%s'", localeStr);
787 }
788 printf("\n");
789
790 Vector<ResTable_config> configs;
791 res.getConfigurations(&configs);
792 SortedVector<int> densities;
793 const size_t NC = configs.size();
794 for (size_t i=0; i<NC; i++) {
795 int dens = configs[i].density;
796 if (dens == 0) dens = 160;
797 densities.add(dens);
798 }
799
800 printf("densities:");
801 const size_t ND = densities.size();
802 for (size_t i=0; i<ND; i++) {
803 printf(" '%d'", densities[i]);
804 }
805 printf("\n");
806
807 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
808 if (dir != NULL) {
809 if (dir->getFileCount() > 0) {
810 printf("native-code:");
811 for (size_t i=0; i<dir->getFileCount(); i++) {
812 printf(" '%s'", dir->getFileName(i).string());
813 }
814 printf("\n");
815 }
816 delete dir;
817 }
818 } else if (strcmp("configurations", option) == 0) {
819 Vector<ResTable_config> configs;
820 res.getConfigurations(&configs);
821 const size_t N = configs.size();
822 for (size_t i=0; i<N; i++) {
823 printf("%s\n", configs[i].toString().string());
824 }
825 } else {
826 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
827 goto bail;
828 }
829 }
830
831 result = NO_ERROR;
832
833 bail:
834 if (asset) {
835 delete asset;
836 }
837 return (result != NO_ERROR);
838 }
839
840
841 /*
842 * Handle the "add" command, which wants to add files to a new or
843 * pre-existing archive.
844 */
845 int doAdd(Bundle* bundle)
846 {
847 ZipFile* zip = NULL;
848 status_t result = UNKNOWN_ERROR;
849 const char* zipFileName;
850
851 if (bundle->getUpdate()) {
852 /* avoid confusion */
853 fprintf(stderr, "ERROR: can't use '-u' with add\n");
854 goto bail;
855 }
856
857 if (bundle->getFileSpecCount() < 1) {
858 fprintf(stderr, "ERROR: must specify zip file name\n");
859 goto bail;
860 }
861 zipFileName = bundle->getFileSpecEntry(0);
862
863 if (bundle->getFileSpecCount() < 2) {
864 fprintf(stderr, "NOTE: nothing to do\n");
865 goto bail;
866 }
867
868 zip = openReadWrite(zipFileName, true);
869 if (zip == NULL) {
870 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
871 goto bail;
872 }
873
874 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
875 const char* fileName = bundle->getFileSpecEntry(i);
876
877 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
878 printf(" '%s'... (from gzip)\n", fileName);
879 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
880 } else {
881 printf(" '%s'...\n", fileName);
882 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
883 }
884 if (result != NO_ERROR) {
885 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
886 if (result == NAME_NOT_FOUND)
887 fprintf(stderr, ": file not found\n");
888 else if (result == ALREADY_EXISTS)
889 fprintf(stderr, ": already exists in archive\n");
890 else
891 fprintf(stderr, "\n");
892 goto bail;
893 }
894 }
895
896 result = NO_ERROR;
897
898 bail:
899 delete zip;
900 return (result != NO_ERROR);
901 }
902
903
904 /*
905 * Delete files from an existing archive.
906 */
907 int doRemove(Bundle* bundle)
908 {
909 ZipFile* zip = NULL;
910 status_t result = UNKNOWN_ERROR;
911 const char* zipFileName;
912
913 if (bundle->getFileSpecCount() < 1) {
914 fprintf(stderr, "ERROR: must specify zip file name\n");
915 goto bail;
916 }
917 zipFileName = bundle->getFileSpecEntry(0);
918
919 if (bundle->getFileSpecCount() < 2) {
920 fprintf(stderr, "NOTE: nothing to do\n");
921 goto bail;
922 }
923
924 zip = openReadWrite(zipFileName, false);
925 if (zip == NULL) {
926 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
927 zipFileName);
928 goto bail;
929 }
930
931 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
932 const char* fileName = bundle->getFileSpecEntry(i);
933 ZipEntry* entry;
934
935 entry = zip->getEntryByName(fileName);
936 if (entry == NULL) {
937 printf(" '%s' NOT FOUND\n", fileName);
938 continue;
939 }
940
941 result = zip->remove(entry);
942
943 if (result != NO_ERROR) {
944 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
945 bundle->getFileSpecEntry(i), zipFileName);
946 goto bail;
947 }
948 }
949
950 /* update the archive */
951 zip->flush();
952
953 bail:
954 delete zip;
955 return (result != NO_ERROR);
956 }
957
958
959 /*
960 * Package up an asset directory and associated application files.
961 */
962 int doPackage(Bundle* bundle)
963 {
964 const char* outputAPKFile;
965 int retVal = 1;
966 status_t err;
967 sp<AaptAssets> assets;
968 int N;
969
970 // -c zz_ZZ means do pseudolocalization
971 ResourceFilter filter;
972 err = filter.parse(bundle->getConfigurations());
973 if (err != NO_ERROR) {
974 goto bail;
975 }
976 if (filter.containsPseudo()) {
977 bundle->setPseudolocalize(true);
978 }
979
980 N = bundle->getFileSpecCount();
981 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
982 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDir() == NULL) {
983 fprintf(stderr, "ERROR: no input files\n");
984 goto bail;
985 }
986
987 outputAPKFile = bundle->getOutputAPKFile();
988
989 // Make sure the filenames provided exist and are of the appropriate type.
990 if (outputAPKFile) {
991 FileType type;
992 type = getFileType(outputAPKFile);
993 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
994 fprintf(stderr,
995 "ERROR: output file '%s' exists but is not regular file\n",
996 outputAPKFile);
997 goto bail;
998 }
999 }
1000
1001 // Load the assets.
1002 assets = new AaptAssets();
1003 err = assets->slurpFromArgs(bundle);
1004 if (err < 0) {
1005 goto bail;
1006 }
1007
1008 if (bundle->getVerbose()) {
1009 assets->print();
1010 }
1011
1012 // If they asked for any files that need to be compiled, do so.
1013 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
1014 err = buildResources(bundle, assets);
1015 if (err != 0) {
1016 goto bail;
1017 }
1018 }
1019
1020 // At this point we've read everything and processed everything. From here
1021 // on out it's just writing output files.
1022 if (SourcePos::hasErrors()) {
1023 goto bail;
1024 }
1025
1026 // Write out R.java constants
1027 if (assets->getPackage() == assets->getSymbolsPrivatePackage()) {
1028 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
1029 if (err < 0) {
1030 goto bail;
1031 }
1032 } else {
1033 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
1034 if (err < 0) {
1035 goto bail;
1036 }
1037 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
1038 if (err < 0) {
1039 goto bail;
1040 }
1041 }
1042
1043 // Write the apk
1044 if (outputAPKFile) {
1045 err = writeAPK(bundle, assets, String8(outputAPKFile));
1046 if (err != NO_ERROR) {
1047 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
1048 goto bail;
1049 }
1050 }
1051
1052 retVal = 0;
1053 bail:
1054 if (SourcePos::hasErrors()) {
1055 SourcePos::printErrors(stderr);
1056 }
1057 return retVal;
1058 }