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