Command.cpp revision d98e1be20e1cca5c36c7e0344500d4a5574568aa
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
19using namespace android;
20
21/*
22 * Show version info.  All the cool kids do it.
23 */
24int doVersion(Bundle* bundle)
25{
26    if (bundle->getFileSpecCount() != 0)
27        printf("(ignoring extra arguments)\n");
28    printf("Android Asset Packaging Tool, v0.2\n");
29
30    return 0;
31}
32
33
34/*
35 * Open the file read only.  The call fails if the file doesn't exist.
36 *
37 * Returns NULL on failure.
38 */
39ZipFile* openReadOnly(const char* fileName)
40{
41    ZipFile* zip;
42    status_t result;
43
44    zip = new ZipFile;
45    result = zip->open(fileName, ZipFile::kOpenReadOnly);
46    if (result != NO_ERROR) {
47        if (result == NAME_NOT_FOUND)
48            fprintf(stderr, "ERROR: '%s' not found\n", fileName);
49        else if (result == PERMISSION_DENIED)
50            fprintf(stderr, "ERROR: '%s' access denied\n", fileName);
51        else
52            fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n",
53                fileName);
54        delete zip;
55        return NULL;
56    }
57
58    return zip;
59}
60
61/*
62 * Open the file read-write.  The file will be created if it doesn't
63 * already exist and "okayToCreate" is set.
64 *
65 * Returns NULL on failure.
66 */
67ZipFile* openReadWrite(const char* fileName, bool okayToCreate)
68{
69    ZipFile* zip = NULL;
70    status_t result;
71    int flags;
72
73    flags = ZipFile::kOpenReadWrite;
74    if (okayToCreate)
75        flags |= ZipFile::kOpenCreate;
76
77    zip = new ZipFile;
78    result = zip->open(fileName, flags);
79    if (result != NO_ERROR) {
80        delete zip;
81        zip = NULL;
82        goto bail;
83    }
84
85bail:
86    return zip;
87}
88
89
90/*
91 * Return a short string describing the compression method.
92 */
93const char* compressionName(int method)
94{
95    if (method == ZipEntry::kCompressStored)
96        return "Stored";
97    else if (method == ZipEntry::kCompressDeflated)
98        return "Deflated";
99    else
100        return "Unknown";
101}
102
103/*
104 * Return the percent reduction in size (0% == no compression).
105 */
106int calcPercent(long uncompressedLen, long compressedLen)
107{
108    if (!uncompressedLen)
109        return 0;
110    else
111        return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5);
112}
113
114/*
115 * Handle the "list" command, which can be a simple file dump or
116 * a verbose listing.
117 *
118 * The verbose listing closely matches the output of the Info-ZIP "unzip"
119 * command.
120 */
121int doList(Bundle* bundle)
122{
123    int result = 1;
124    ZipFile* zip = NULL;
125    const ZipEntry* entry;
126    long totalUncLen, totalCompLen;
127    const char* zipFileName;
128
129    if (bundle->getFileSpecCount() != 1) {
130        fprintf(stderr, "ERROR: specify zip file name (only)\n");
131        goto bail;
132    }
133    zipFileName = bundle->getFileSpecEntry(0);
134
135    zip = openReadOnly(zipFileName);
136    if (zip == NULL)
137        goto bail;
138
139    int count, i;
140
141    if (bundle->getVerbose()) {
142        printf("Archive:  %s\n", zipFileName);
143        printf(
144            " Length   Method    Size  Ratio   Offset      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%%  %8zd  %s  %08lx  %s\n",
163                (long) entry->getUncompressedLen(),
164                compressionName(entry->getCompressionMethod()),
165                (long) entry->getCompressedLen(),
166                calcPercent(entry->getUncompressedLen(),
167                            entry->getCompressedLen()),
168                (size_t) entry->getLFHOffset(),
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        }
196
197        const ResTable& res = assets.getResources(false);
198        if (&res == NULL) {
199            printf("\nNo resource table found.\n");
200        } else {
201#ifndef HAVE_ANDROID_OS
202            printf("\nResource table:\n");
203            res.print(false);
204#endif
205        }
206
207        Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
208                                                   Asset::ACCESS_BUFFER);
209        if (manifestAsset == NULL) {
210            printf("\nNo AndroidManifest.xml found.\n");
211        } else {
212            printf("\nAndroid manifest:\n");
213            ResXMLTree tree;
214            tree.setTo(manifestAsset->getBuffer(true),
215                       manifestAsset->getLength());
216            printXMLBlock(&tree);
217        }
218        delete manifestAsset;
219    }
220
221    result = 0;
222
223bail:
224    delete zip;
225    return result;
226}
227
228static ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes)
229{
230    size_t N = tree.getAttributeCount();
231    for (size_t i=0; i<N; i++) {
232        if (tree.getAttributeNameResID(i) == attrRes) {
233            return (ssize_t)i;
234        }
235    }
236    return -1;
237}
238
239String8 getAttribute(const ResXMLTree& tree, const char* ns,
240                            const char* attr, String8* outError)
241{
242    ssize_t idx = tree.indexOfAttribute(ns, attr);
243    if (idx < 0) {
244        return String8();
245    }
246    Res_value value;
247    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
248        if (value.dataType != Res_value::TYPE_STRING) {
249            if (outError != NULL) *outError = "attribute is not a string value";
250            return String8();
251        }
252    }
253    size_t len;
254    const uint16_t* str = tree.getAttributeStringValue(idx, &len);
255    return str ? String8(str, len) : String8();
256}
257
258static String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError)
259{
260    ssize_t idx = indexOfAttribute(tree, attrRes);
261    if (idx < 0) {
262        return String8();
263    }
264    Res_value value;
265    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
266        if (value.dataType != Res_value::TYPE_STRING) {
267            if (outError != NULL) *outError = "attribute is not a string value";
268            return String8();
269        }
270    }
271    size_t len;
272    const uint16_t* str = tree.getAttributeStringValue(idx, &len);
273    return str ? String8(str, len) : String8();
274}
275
276static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes,
277        String8* outError, int32_t defValue = -1)
278{
279    ssize_t idx = indexOfAttribute(tree, attrRes);
280    if (idx < 0) {
281        return defValue;
282    }
283    Res_value value;
284    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
285        if (value.dataType < Res_value::TYPE_FIRST_INT
286                || value.dataType > Res_value::TYPE_LAST_INT) {
287            if (outError != NULL) *outError = "attribute is not an integer value";
288            return defValue;
289        }
290    }
291    return value.data;
292}
293
294static int32_t getResolvedIntegerAttribute(const ResTable* resTable, const ResXMLTree& tree,
295        uint32_t attrRes, String8* outError, int32_t defValue = -1)
296{
297    ssize_t idx = indexOfAttribute(tree, attrRes);
298    if (idx < 0) {
299        return defValue;
300    }
301    Res_value value;
302    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
303        if (value.dataType == Res_value::TYPE_REFERENCE) {
304            resTable->resolveReference(&value, 0);
305        }
306        if (value.dataType < Res_value::TYPE_FIRST_INT
307                || value.dataType > Res_value::TYPE_LAST_INT) {
308            if (outError != NULL) *outError = "attribute is not an integer value";
309            return defValue;
310        }
311    }
312    return value.data;
313}
314
315static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree,
316        uint32_t attrRes, String8* outError)
317{
318    ssize_t idx = indexOfAttribute(tree, attrRes);
319    if (idx < 0) {
320        return String8();
321    }
322    Res_value value;
323    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
324        if (value.dataType == Res_value::TYPE_STRING) {
325            size_t len;
326            const uint16_t* str = tree.getAttributeStringValue(idx, &len);
327            return str ? String8(str, len) : String8();
328        }
329        resTable->resolveReference(&value, 0);
330        if (value.dataType != Res_value::TYPE_STRING) {
331            if (outError != NULL) *outError = "attribute is not a string value";
332            return String8();
333        }
334    }
335    size_t len;
336    const Res_value* value2 = &value;
337    const char16_t* str = const_cast<ResTable*>(resTable)->valueToString(value2, 0, NULL, &len);
338    return str ? String8(str, len) : String8();
339}
340
341// These are attribute resource constants for the platform, as found
342// in android.R.attr
343enum {
344    LABEL_ATTR = 0x01010001,
345    ICON_ATTR = 0x01010002,
346    NAME_ATTR = 0x01010003,
347    VERSION_CODE_ATTR = 0x0101021b,
348    VERSION_NAME_ATTR = 0x0101021c,
349    SCREEN_ORIENTATION_ATTR = 0x0101001e,
350    MIN_SDK_VERSION_ATTR = 0x0101020c,
351    MAX_SDK_VERSION_ATTR = 0x01010271,
352    REQ_TOUCH_SCREEN_ATTR = 0x01010227,
353    REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
354    REQ_HARD_KEYBOARD_ATTR = 0x01010229,
355    REQ_NAVIGATION_ATTR = 0x0101022a,
356    REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
357    TARGET_SDK_VERSION_ATTR = 0x01010270,
358    TEST_ONLY_ATTR = 0x01010272,
359    ANY_DENSITY_ATTR = 0x0101026c,
360    GL_ES_VERSION_ATTR = 0x01010281,
361    SMALL_SCREEN_ATTR = 0x01010284,
362    NORMAL_SCREEN_ATTR = 0x01010285,
363    LARGE_SCREEN_ATTR = 0x01010286,
364    XLARGE_SCREEN_ATTR = 0x010102bf,
365    REQUIRED_ATTR = 0x0101028e,
366    SCREEN_SIZE_ATTR = 0x010102ca,
367    SCREEN_DENSITY_ATTR = 0x010102cb,
368    REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
369    COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
370    LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
371};
372
373const char *getComponentName(String8 &pkgName, String8 &componentName) {
374    ssize_t idx = componentName.find(".");
375    String8 retStr(pkgName);
376    if (idx == 0) {
377        retStr += componentName;
378    } else if (idx < 0) {
379        retStr += ".";
380        retStr += componentName;
381    } else {
382        return componentName.string();
383    }
384    return retStr.string();
385}
386
387static void printCompatibleScreens(ResXMLTree& tree) {
388    size_t len;
389    ResXMLTree::event_code_t code;
390    int depth = 0;
391    bool first = true;
392    printf("compatible-screens:");
393    while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
394        if (code == ResXMLTree::END_TAG) {
395            depth--;
396            if (depth < 0) {
397                break;
398            }
399            continue;
400        }
401        if (code != ResXMLTree::START_TAG) {
402            continue;
403        }
404        depth++;
405        String8 tag(tree.getElementName(&len));
406        if (tag == "screen") {
407            int32_t screenSize = getIntegerAttribute(tree,
408                    SCREEN_SIZE_ATTR, NULL, -1);
409            int32_t screenDensity = getIntegerAttribute(tree,
410                    SCREEN_DENSITY_ATTR, NULL, -1);
411            if (screenSize > 0 && screenDensity > 0) {
412                if (!first) {
413                    printf(",");
414                }
415                first = false;
416                printf("'%d/%d'", screenSize, screenDensity);
417            }
418        }
419    }
420    printf("\n");
421}
422
423/*
424 * Handle the "dump" command, to extract select data from an archive.
425 */
426int doDump(Bundle* bundle)
427{
428    status_t result = UNKNOWN_ERROR;
429    Asset* asset = NULL;
430
431    if (bundle->getFileSpecCount() < 1) {
432        fprintf(stderr, "ERROR: no dump option specified\n");
433        return 1;
434    }
435
436    if (bundle->getFileSpecCount() < 2) {
437        fprintf(stderr, "ERROR: no dump file specified\n");
438        return 1;
439    }
440
441    const char* option = bundle->getFileSpecEntry(0);
442    const char* filename = bundle->getFileSpecEntry(1);
443
444    AssetManager assets;
445    void* assetsCookie;
446    if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
447        fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
448        return 1;
449    }
450
451    // Make a dummy config for retrieving resources...  we need to supply
452    // non-default values for some configs so that we can retrieve resources
453    // in the app that don't have a default.  The most important of these is
454    // the API version because key resources like icons will have an implicit
455    // version if they are using newer config types like density.
456    ResTable_config config;
457    config.language[0] = 'e';
458    config.language[1] = 'n';
459    config.country[0] = 'U';
460    config.country[1] = 'S';
461    config.orientation = ResTable_config::ORIENTATION_PORT;
462    config.density = ResTable_config::DENSITY_MEDIUM;
463    config.sdkVersion = 10000; // Very high.
464    config.screenWidthDp = 320;
465    config.screenHeightDp = 480;
466    config.smallestScreenWidthDp = 320;
467    assets.setConfiguration(config);
468
469    const ResTable& res = assets.getResources(false);
470    if (&res == NULL) {
471        fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
472        goto bail;
473    }
474
475    if (strcmp("resources", option) == 0) {
476#ifndef HAVE_ANDROID_OS
477        res.print(bundle->getValues());
478#endif
479    } else if (strcmp("xmltree", option) == 0) {
480        if (bundle->getFileSpecCount() < 3) {
481            fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
482            goto bail;
483        }
484
485        for (int i=2; i<bundle->getFileSpecCount(); i++) {
486            const char* resname = bundle->getFileSpecEntry(i);
487            ResXMLTree tree;
488            asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
489            if (asset == NULL) {
490                fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
491                goto bail;
492            }
493
494            if (tree.setTo(asset->getBuffer(true),
495                           asset->getLength()) != NO_ERROR) {
496                fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
497                goto bail;
498            }
499            tree.restart();
500            printXMLBlock(&tree);
501            tree.uninit();
502            delete asset;
503            asset = NULL;
504        }
505
506    } else if (strcmp("xmlstrings", option) == 0) {
507        if (bundle->getFileSpecCount() < 3) {
508            fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
509            goto bail;
510        }
511
512        for (int i=2; i<bundle->getFileSpecCount(); i++) {
513            const char* resname = bundle->getFileSpecEntry(i);
514            ResXMLTree tree;
515            asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
516            if (asset == NULL) {
517                fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
518                goto bail;
519            }
520
521            if (tree.setTo(asset->getBuffer(true),
522                           asset->getLength()) != NO_ERROR) {
523                fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
524                goto bail;
525            }
526            printStringPool(&tree.getStrings());
527            delete asset;
528            asset = NULL;
529        }
530
531    } else {
532        ResXMLTree tree;
533        asset = assets.openNonAsset("AndroidManifest.xml",
534                                            Asset::ACCESS_BUFFER);
535        if (asset == NULL) {
536            fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
537            goto bail;
538        }
539
540        if (tree.setTo(asset->getBuffer(true),
541                       asset->getLength()) != NO_ERROR) {
542            fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
543            goto bail;
544        }
545        tree.restart();
546
547        if (strcmp("permissions", option) == 0) {
548            size_t len;
549            ResXMLTree::event_code_t code;
550            int depth = 0;
551            while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
552                if (code == ResXMLTree::END_TAG) {
553                    depth--;
554                    continue;
555                }
556                if (code != ResXMLTree::START_TAG) {
557                    continue;
558                }
559                depth++;
560                String8 tag(tree.getElementName(&len));
561                //printf("Depth %d tag %s\n", depth, tag.string());
562                if (depth == 1) {
563                    if (tag != "manifest") {
564                        fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
565                        goto bail;
566                    }
567                    String8 pkg = getAttribute(tree, NULL, "package", NULL);
568                    printf("package: %s\n", pkg.string());
569                } else if (depth == 2 && tag == "permission") {
570                    String8 error;
571                    String8 name = getAttribute(tree, NAME_ATTR, &error);
572                    if (error != "") {
573                        fprintf(stderr, "ERROR: %s\n", error.string());
574                        goto bail;
575                    }
576                    printf("permission: %s\n", name.string());
577                } else if (depth == 2 && tag == "uses-permission") {
578                    String8 error;
579                    String8 name = getAttribute(tree, NAME_ATTR, &error);
580                    if (error != "") {
581                        fprintf(stderr, "ERROR: %s\n", error.string());
582                        goto bail;
583                    }
584                    printf("uses-permission: %s\n", name.string());
585                }
586            }
587        } else if (strcmp("badging", option) == 0) {
588            Vector<String8> locales;
589            res.getLocales(&locales);
590
591            Vector<ResTable_config> configs;
592            res.getConfigurations(&configs);
593            SortedVector<int> densities;
594            const size_t NC = configs.size();
595            for (size_t i=0; i<NC; i++) {
596                int dens = configs[i].density;
597                if (dens == 0) dens = 160;
598                densities.add(dens);
599            }
600
601            size_t len;
602            ResXMLTree::event_code_t code;
603            int depth = 0;
604            String8 error;
605            bool withinActivity = false;
606            bool isMainActivity = false;
607            bool isLauncherActivity = false;
608            bool isSearchable = false;
609            bool withinApplication = false;
610            bool withinReceiver = false;
611            bool withinService = false;
612            bool withinIntentFilter = false;
613            bool hasMainActivity = false;
614            bool hasOtherActivities = false;
615            bool hasOtherReceivers = false;
616            bool hasOtherServices = false;
617            bool hasWallpaperService = false;
618            bool hasImeService = false;
619            bool hasWidgetReceivers = false;
620            bool hasIntentFilter = false;
621            bool actMainActivity = false;
622            bool actWidgetReceivers = false;
623            bool actImeService = false;
624            bool actWallpaperService = false;
625
626            // This next group of variables is used to implement a group of
627            // backward-compatibility heuristics necessitated by the addition of
628            // some new uses-feature constants in 2.1 and 2.2. In most cases, the
629            // heuristic is "if an app requests a permission but doesn't explicitly
630            // request the corresponding <uses-feature>, presume it's there anyway".
631            bool specCameraFeature = false; // camera-related
632            bool specCameraAutofocusFeature = false;
633            bool reqCameraAutofocusFeature = false;
634            bool reqCameraFlashFeature = false;
635            bool hasCameraPermission = false;
636            bool specLocationFeature = false; // location-related
637            bool specNetworkLocFeature = false;
638            bool reqNetworkLocFeature = false;
639            bool specGpsFeature = false;
640            bool reqGpsFeature = false;
641            bool hasMockLocPermission = false;
642            bool hasCoarseLocPermission = false;
643            bool hasGpsPermission = false;
644            bool hasGeneralLocPermission = false;
645            bool specBluetoothFeature = false; // Bluetooth API-related
646            bool hasBluetoothPermission = false;
647            bool specMicrophoneFeature = false; // microphone-related
648            bool hasRecordAudioPermission = false;
649            bool specWiFiFeature = false;
650            bool hasWiFiPermission = false;
651            bool specTelephonyFeature = false; // telephony-related
652            bool reqTelephonySubFeature = false;
653            bool hasTelephonyPermission = false;
654            bool specTouchscreenFeature = false; // touchscreen-related
655            bool specMultitouchFeature = false;
656            bool reqDistinctMultitouchFeature = false;
657            bool specScreenPortraitFeature = false;
658            bool specScreenLandscapeFeature = false;
659            bool reqScreenPortraitFeature = false;
660            bool reqScreenLandscapeFeature = false;
661            // 2.2 also added some other features that apps can request, but that
662            // have no corresponding permission, so we cannot implement any
663            // back-compatibility heuristic for them. The below are thus unnecessary
664            // (but are retained here for documentary purposes.)
665            //bool specCompassFeature = false;
666            //bool specAccelerometerFeature = false;
667            //bool specProximityFeature = false;
668            //bool specAmbientLightFeature = false;
669            //bool specLiveWallpaperFeature = false;
670
671            int targetSdk = 0;
672            int smallScreen = 1;
673            int normalScreen = 1;
674            int largeScreen = 1;
675            int xlargeScreen = 1;
676            int anyDensity = 1;
677            int requiresSmallestWidthDp = 0;
678            int compatibleWidthLimitDp = 0;
679            int largestWidthLimitDp = 0;
680            String8 pkg;
681            String8 activityName;
682            String8 activityLabel;
683            String8 activityIcon;
684            String8 receiverName;
685            String8 serviceName;
686            while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
687                if (code == ResXMLTree::END_TAG) {
688                    depth--;
689                    if (depth < 2) {
690                        withinApplication = false;
691                    } else if (depth < 3) {
692                        if (withinActivity && isMainActivity && isLauncherActivity) {
693                            const char *aName = getComponentName(pkg, activityName);
694                            printf("launchable-activity:");
695                            if (aName != NULL) {
696                                printf(" name='%s' ", aName);
697                            }
698                            printf(" label='%s' icon='%s'\n",
699                                    activityLabel.string(),
700                                    activityIcon.string());
701                        }
702                        if (!hasIntentFilter) {
703                            hasOtherActivities |= withinActivity;
704                            hasOtherReceivers |= withinReceiver;
705                            hasOtherServices |= withinService;
706                        }
707                        withinActivity = false;
708                        withinService = false;
709                        withinReceiver = false;
710                        hasIntentFilter = false;
711                        isMainActivity = isLauncherActivity = false;
712                    } else if (depth < 4) {
713                        if (withinIntentFilter) {
714                            if (withinActivity) {
715                                hasMainActivity |= actMainActivity;
716                                hasOtherActivities |= !actMainActivity;
717                            } else if (withinReceiver) {
718                                hasWidgetReceivers |= actWidgetReceivers;
719                                hasOtherReceivers |= !actWidgetReceivers;
720                            } else if (withinService) {
721                                hasImeService |= actImeService;
722                                hasWallpaperService |= actWallpaperService;
723                                hasOtherServices |= (!actImeService && !actWallpaperService);
724                            }
725                        }
726                        withinIntentFilter = false;
727                    }
728                    continue;
729                }
730                if (code != ResXMLTree::START_TAG) {
731                    continue;
732                }
733                depth++;
734                String8 tag(tree.getElementName(&len));
735                //printf("Depth %d,  %s\n", depth, tag.string());
736                if (depth == 1) {
737                    if (tag != "manifest") {
738                        fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
739                        goto bail;
740                    }
741                    pkg = getAttribute(tree, NULL, "package", NULL);
742                    printf("package: name='%s' ", pkg.string());
743                    int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
744                    if (error != "") {
745                        fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
746                        goto bail;
747                    }
748                    if (versionCode > 0) {
749                        printf("versionCode='%d' ", versionCode);
750                    } else {
751                        printf("versionCode='' ");
752                    }
753                    String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
754                    if (error != "") {
755                        fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
756                        goto bail;
757                    }
758                    printf("versionName='%s'\n", versionName.string());
759                } else if (depth == 2) {
760                    withinApplication = false;
761                    if (tag == "application") {
762                        withinApplication = true;
763
764                        String8 label;
765                        const size_t NL = locales.size();
766                        for (size_t i=0; i<NL; i++) {
767                            const char* localeStr =  locales[i].string();
768                            assets.setLocale(localeStr != NULL ? localeStr : "");
769                            String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
770                            if (llabel != "") {
771                                if (localeStr == NULL || strlen(localeStr) == 0) {
772                                    label = llabel;
773                                    printf("application-label:'%s'\n", llabel.string());
774                                } else {
775                                    if (label == "") {
776                                        label = llabel;
777                                    }
778                                    printf("application-label-%s:'%s'\n", localeStr,
779                                            llabel.string());
780                                }
781                            }
782                        }
783
784                        ResTable_config tmpConfig = config;
785                        const size_t ND = densities.size();
786                        for (size_t i=0; i<ND; i++) {
787                            tmpConfig.density = densities[i];
788                            assets.setConfiguration(tmpConfig);
789                            String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
790                            if (icon != "") {
791                                printf("application-icon-%d:'%s'\n", densities[i], icon.string());
792                            }
793                        }
794                        assets.setConfiguration(config);
795
796                        String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
797                        if (error != "") {
798                            fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
799                            goto bail;
800                        }
801                        int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
802                        if (error != "") {
803                            fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
804                            goto bail;
805                        }
806                        printf("application: label='%s' ", label.string());
807                        printf("icon='%s'\n", icon.string());
808                        if (testOnly != 0) {
809                            printf("testOnly='%d'\n", testOnly);
810                        }
811                    } else if (tag == "uses-sdk") {
812                        int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
813                        if (error != "") {
814                            error = "";
815                            String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
816                            if (error != "") {
817                                fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
818                                        error.string());
819                                goto bail;
820                            }
821                            if (name == "Donut") targetSdk = 4;
822                            printf("sdkVersion:'%s'\n", name.string());
823                        } else if (code != -1) {
824                            targetSdk = code;
825                            printf("sdkVersion:'%d'\n", code);
826                        }
827                        code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
828                        if (code != -1) {
829                            printf("maxSdkVersion:'%d'\n", code);
830                        }
831                        code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
832                        if (error != "") {
833                            error = "";
834                            String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
835                            if (error != "") {
836                                fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
837                                        error.string());
838                                goto bail;
839                            }
840                            if (name == "Donut" && targetSdk < 4) targetSdk = 4;
841                            printf("targetSdkVersion:'%s'\n", name.string());
842                        } else if (code != -1) {
843                            if (targetSdk < code) {
844                                targetSdk = code;
845                            }
846                            printf("targetSdkVersion:'%d'\n", code);
847                        }
848                    } else if (tag == "uses-configuration") {
849                        int32_t reqTouchScreen = getIntegerAttribute(tree,
850                                REQ_TOUCH_SCREEN_ATTR, NULL, 0);
851                        int32_t reqKeyboardType = getIntegerAttribute(tree,
852                                REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
853                        int32_t reqHardKeyboard = getIntegerAttribute(tree,
854                                REQ_HARD_KEYBOARD_ATTR, NULL, 0);
855                        int32_t reqNavigation = getIntegerAttribute(tree,
856                                REQ_NAVIGATION_ATTR, NULL, 0);
857                        int32_t reqFiveWayNav = getIntegerAttribute(tree,
858                                REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
859                        printf("uses-configuration:");
860                        if (reqTouchScreen != 0) {
861                            printf(" reqTouchScreen='%d'", reqTouchScreen);
862                        }
863                        if (reqKeyboardType != 0) {
864                            printf(" reqKeyboardType='%d'", reqKeyboardType);
865                        }
866                        if (reqHardKeyboard != 0) {
867                            printf(" reqHardKeyboard='%d'", reqHardKeyboard);
868                        }
869                        if (reqNavigation != 0) {
870                            printf(" reqNavigation='%d'", reqNavigation);
871                        }
872                        if (reqFiveWayNav != 0) {
873                            printf(" reqFiveWayNav='%d'", reqFiveWayNav);
874                        }
875                        printf("\n");
876                    } else if (tag == "supports-screens") {
877                        smallScreen = getIntegerAttribute(tree,
878                                SMALL_SCREEN_ATTR, NULL, 1);
879                        normalScreen = getIntegerAttribute(tree,
880                                NORMAL_SCREEN_ATTR, NULL, 1);
881                        largeScreen = getIntegerAttribute(tree,
882                                LARGE_SCREEN_ATTR, NULL, 1);
883                        xlargeScreen = getIntegerAttribute(tree,
884                                XLARGE_SCREEN_ATTR, NULL, 1);
885                        anyDensity = getIntegerAttribute(tree,
886                                ANY_DENSITY_ATTR, NULL, 1);
887                        requiresSmallestWidthDp = getIntegerAttribute(tree,
888                                REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
889                        compatibleWidthLimitDp = getIntegerAttribute(tree,
890                                COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
891                        largestWidthLimitDp = getIntegerAttribute(tree,
892                                LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
893                    } else if (tag == "uses-feature") {
894                        String8 name = getAttribute(tree, NAME_ATTR, &error);
895
896                        if (name != "" && error == "") {
897                            int req = getIntegerAttribute(tree,
898                                    REQUIRED_ATTR, NULL, 1);
899
900                            if (name == "android.hardware.camera") {
901                                specCameraFeature = true;
902                            } else if (name == "android.hardware.camera.autofocus") {
903                                // these have no corresponding permission to check for,
904                                // but should imply the foundational camera permission
905                                reqCameraAutofocusFeature = reqCameraAutofocusFeature || req;
906                                specCameraAutofocusFeature = true;
907                            } else if (req && (name == "android.hardware.camera.flash")) {
908                                // these have no corresponding permission to check for,
909                                // but should imply the foundational camera permission
910                                reqCameraFlashFeature = true;
911                            } else if (name == "android.hardware.location") {
912                                specLocationFeature = true;
913                            } else if (name == "android.hardware.location.network") {
914                                specNetworkLocFeature = true;
915                                reqNetworkLocFeature = reqNetworkLocFeature || req;
916                            } else if (name == "android.hardware.location.gps") {
917                                specGpsFeature = true;
918                                reqGpsFeature = reqGpsFeature || req;
919                            } else if (name == "android.hardware.bluetooth") {
920                                specBluetoothFeature = true;
921                            } else if (name == "android.hardware.touchscreen") {
922                                specTouchscreenFeature = true;
923                            } else if (name == "android.hardware.touchscreen.multitouch") {
924                                specMultitouchFeature = true;
925                            } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
926                                reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req;
927                            } else if (name == "android.hardware.microphone") {
928                                specMicrophoneFeature = true;
929                            } else if (name == "android.hardware.wifi") {
930                                specWiFiFeature = true;
931                            } else if (name == "android.hardware.telephony") {
932                                specTelephonyFeature = true;
933                            } else if (req && (name == "android.hardware.telephony.gsm" ||
934                                               name == "android.hardware.telephony.cdma")) {
935                                // these have no corresponding permission to check for,
936                                // but should imply the foundational telephony permission
937                                reqTelephonySubFeature = true;
938                            } else if (name == "android.hardware.screen.portrait") {
939                                specScreenPortraitFeature = true;
940                            } else if (name == "android.hardware.screen.landscape") {
941                                specScreenLandscapeFeature = true;
942                            }
943                            printf("uses-feature%s:'%s'\n",
944                                    req ? "" : "-not-required", name.string());
945                        } else {
946                            int vers = getIntegerAttribute(tree,
947                                    GL_ES_VERSION_ATTR, &error);
948                            if (error == "") {
949                                printf("uses-gl-es:'0x%x'\n", vers);
950                            }
951                        }
952                    } else if (tag == "uses-permission") {
953                        String8 name = getAttribute(tree, NAME_ATTR, &error);
954                        if (name != "" && error == "") {
955                            if (name == "android.permission.CAMERA") {
956                                hasCameraPermission = true;
957                            } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
958                                hasGpsPermission = true;
959                            } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
960                                hasMockLocPermission = true;
961                            } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
962                                hasCoarseLocPermission = true;
963                            } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
964                                       name == "android.permission.INSTALL_LOCATION_PROVIDER") {
965                                hasGeneralLocPermission = true;
966                            } else if (name == "android.permission.BLUETOOTH" ||
967                                       name == "android.permission.BLUETOOTH_ADMIN") {
968                                hasBluetoothPermission = true;
969                            } else if (name == "android.permission.RECORD_AUDIO") {
970                                hasRecordAudioPermission = true;
971                            } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
972                                       name == "android.permission.CHANGE_WIFI_STATE" ||
973                                       name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
974                                hasWiFiPermission = true;
975                            } else if (name == "android.permission.CALL_PHONE" ||
976                                       name == "android.permission.CALL_PRIVILEGED" ||
977                                       name == "android.permission.MODIFY_PHONE_STATE" ||
978                                       name == "android.permission.PROCESS_OUTGOING_CALLS" ||
979                                       name == "android.permission.READ_SMS" ||
980                                       name == "android.permission.RECEIVE_SMS" ||
981                                       name == "android.permission.RECEIVE_MMS" ||
982                                       name == "android.permission.RECEIVE_WAP_PUSH" ||
983                                       name == "android.permission.SEND_SMS" ||
984                                       name == "android.permission.WRITE_APN_SETTINGS" ||
985                                       name == "android.permission.WRITE_SMS") {
986                                hasTelephonyPermission = true;
987                            }
988                            printf("uses-permission:'%s'\n", name.string());
989                        } else {
990                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
991                                    error.string());
992                            goto bail;
993                        }
994                    } else if (tag == "uses-package") {
995                        String8 name = getAttribute(tree, NAME_ATTR, &error);
996                        if (name != "" && error == "") {
997                            printf("uses-package:'%s'\n", name.string());
998                        } else {
999                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1000                                    error.string());
1001                                goto bail;
1002                        }
1003                    } else if (tag == "original-package") {
1004                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1005                        if (name != "" && error == "") {
1006                            printf("original-package:'%s'\n", name.string());
1007                        } else {
1008                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1009                                    error.string());
1010                                goto bail;
1011                        }
1012                    } else if (tag == "supports-gl-texture") {
1013                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1014                        if (name != "" && error == "") {
1015                            printf("supports-gl-texture:'%s'\n", name.string());
1016                        } else {
1017                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1018                                    error.string());
1019                                goto bail;
1020                        }
1021                    } else if (tag == "compatible-screens") {
1022                        printCompatibleScreens(tree);
1023                        depth--;
1024                    }
1025                } else if (depth == 3 && withinApplication) {
1026                    withinActivity = false;
1027                    withinReceiver = false;
1028                    withinService = false;
1029                    hasIntentFilter = false;
1030                    if(tag == "activity") {
1031                        withinActivity = true;
1032                        activityName = getAttribute(tree, NAME_ATTR, &error);
1033                        if (error != "") {
1034                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1035                            goto bail;
1036                        }
1037
1038                        activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1039                        if (error != "") {
1040                            fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string());
1041                            goto bail;
1042                        }
1043
1044                        activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1045                        if (error != "") {
1046                            fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
1047                            goto bail;
1048                        }
1049
1050                        int32_t orien = getResolvedIntegerAttribute(&res, tree,
1051                                SCREEN_ORIENTATION_ATTR, &error);
1052                        if (error == "") {
1053                            if (orien == 0 || orien == 6 || orien == 8) {
1054                                // Requests landscape, sensorLandscape, or reverseLandscape.
1055                                reqScreenLandscapeFeature = true;
1056                            } else if (orien == 1 || orien == 7 || orien == 9) {
1057                                // Requests portrait, sensorPortrait, or reversePortrait.
1058                                reqScreenPortraitFeature = true;
1059                            }
1060                        }
1061                    } else if (tag == "uses-library") {
1062                        String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1063                        if (error != "") {
1064                            fprintf(stderr, "ERROR getting 'android:name' attribute for uses-library: %s\n", error.string());
1065                            goto bail;
1066                        }
1067                        int req = getIntegerAttribute(tree,
1068                                REQUIRED_ATTR, NULL, 1);
1069                        printf("uses-library%s:'%s'\n",
1070                                req ? "" : "-not-required", libraryName.string());
1071                    } else if (tag == "receiver") {
1072                        withinReceiver = true;
1073                        receiverName = getAttribute(tree, NAME_ATTR, &error);
1074
1075                        if (error != "") {
1076                            fprintf(stderr, "ERROR getting 'android:name' attribute for receiver: %s\n", error.string());
1077                            goto bail;
1078                        }
1079                    } else if (tag == "service") {
1080                        withinService = true;
1081                        serviceName = getAttribute(tree, NAME_ATTR, &error);
1082
1083                        if (error != "") {
1084                            fprintf(stderr, "ERROR getting 'android:name' attribute for service: %s\n", error.string());
1085                            goto bail;
1086                        }
1087                    }
1088                } else if ((depth == 4) && (tag == "intent-filter")) {
1089                    hasIntentFilter = true;
1090                    withinIntentFilter = true;
1091                    actMainActivity = actWidgetReceivers = actImeService = actWallpaperService = false;
1092                } else if ((depth == 5) && withinIntentFilter){
1093                    String8 action;
1094                    if (tag == "action") {
1095                        action = getAttribute(tree, NAME_ATTR, &error);
1096                        if (error != "") {
1097                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1098                            goto bail;
1099                        }
1100                        if (withinActivity) {
1101                            if (action == "android.intent.action.MAIN") {
1102                                isMainActivity = true;
1103                                actMainActivity = true;
1104                            }
1105                        } else if (withinReceiver) {
1106                            if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1107                                actWidgetReceivers = true;
1108                            }
1109                        } else if (withinService) {
1110                            if (action == "android.view.InputMethod") {
1111                                actImeService = true;
1112                            } else if (action == "android.service.wallpaper.WallpaperService") {
1113                                actWallpaperService = true;
1114                            }
1115                        }
1116                        if (action == "android.intent.action.SEARCH") {
1117                            isSearchable = true;
1118                        }
1119                    }
1120
1121                    if (tag == "category") {
1122                        String8 category = getAttribute(tree, NAME_ATTR, &error);
1123                        if (error != "") {
1124                            fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string());
1125                            goto bail;
1126                        }
1127                        if (withinActivity) {
1128                            if (category == "android.intent.category.LAUNCHER") {
1129                                isLauncherActivity = true;
1130                            }
1131                        }
1132                    }
1133                }
1134            }
1135
1136            /* The following blocks handle printing "inferred" uses-features, based
1137             * on whether related features or permissions are used by the app.
1138             * Note that the various spec*Feature variables denote whether the
1139             * relevant tag was *present* in the AndroidManfest, not that it was
1140             * present and set to true.
1141             */
1142            // Camera-related back-compatibility logic
1143            if (!specCameraFeature) {
1144                if (reqCameraFlashFeature || reqCameraAutofocusFeature) {
1145                    // if app requested a sub-feature (autofocus or flash) and didn't
1146                    // request the base camera feature, we infer that it meant to
1147                    printf("uses-feature:'android.hardware.camera'\n");
1148                } else if (hasCameraPermission) {
1149                    // if app wants to use camera but didn't request the feature, we infer
1150                    // that it meant to, and further that it wants autofocus
1151                    // (which was the 1.0 - 1.5 behavior)
1152                    printf("uses-feature:'android.hardware.camera'\n");
1153                    if (!specCameraAutofocusFeature) {
1154                        printf("uses-feature:'android.hardware.camera.autofocus'\n");
1155                    }
1156                }
1157            }
1158
1159            // Location-related back-compatibility logic
1160            if (!specLocationFeature &&
1161                (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission ||
1162                 hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) {
1163                // if app either takes a location-related permission or requests one of the
1164                // sub-features, we infer that it also meant to request the base location feature
1165                printf("uses-feature:'android.hardware.location'\n");
1166            }
1167            if (!specGpsFeature && hasGpsPermission) {
1168                // if app takes GPS (FINE location) perm but does not request the GPS
1169                // feature, we infer that it meant to
1170                printf("uses-feature:'android.hardware.location.gps'\n");
1171            }
1172            if (!specNetworkLocFeature && hasCoarseLocPermission) {
1173                // if app takes Network location (COARSE location) perm but does not request the
1174                // network location feature, we infer that it meant to
1175                printf("uses-feature:'android.hardware.location.network'\n");
1176            }
1177
1178            // Bluetooth-related compatibility logic
1179            if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
1180                // if app takes a Bluetooth permission but does not request the Bluetooth
1181                // feature, we infer that it meant to
1182                printf("uses-feature:'android.hardware.bluetooth'\n");
1183            }
1184
1185            // Microphone-related compatibility logic
1186            if (!specMicrophoneFeature && hasRecordAudioPermission) {
1187                // if app takes the record-audio permission but does not request the microphone
1188                // feature, we infer that it meant to
1189                printf("uses-feature:'android.hardware.microphone'\n");
1190            }
1191
1192            // WiFi-related compatibility logic
1193            if (!specWiFiFeature && hasWiFiPermission) {
1194                // if app takes one of the WiFi permissions but does not request the WiFi
1195                // feature, we infer that it meant to
1196                printf("uses-feature:'android.hardware.wifi'\n");
1197            }
1198
1199            // Telephony-related compatibility logic
1200            if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) {
1201                // if app takes one of the telephony permissions or requests a sub-feature but
1202                // does not request the base telephony feature, we infer that it meant to
1203                printf("uses-feature:'android.hardware.telephony'\n");
1204            }
1205
1206            // Touchscreen-related back-compatibility logic
1207            if (!specTouchscreenFeature) { // not a typo!
1208                // all apps are presumed to require a touchscreen, unless they explicitly say
1209                // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
1210                // Note that specTouchscreenFeature is true if the tag is present, regardless
1211                // of whether its value is true or false, so this is safe
1212                printf("uses-feature:'android.hardware.touchscreen'\n");
1213            }
1214            if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
1215                // if app takes one of the telephony permissions or requests a sub-feature but
1216                // does not request the base telephony feature, we infer that it meant to
1217                printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
1218            }
1219
1220            // Landscape/portrait-related compatibility logic
1221            if (!specScreenLandscapeFeature && !specScreenPortraitFeature) {
1222                // If the app has specified any activities in its manifest
1223                // that request a specific orientation, then assume that
1224                // orientation is required.
1225                if (reqScreenLandscapeFeature) {
1226                    printf("uses-feature:'android.hardware.screen.landscape'\n");
1227                }
1228                if (reqScreenPortraitFeature) {
1229                    printf("uses-feature:'android.hardware.screen.portrait'\n");
1230                }
1231            }
1232
1233            if (hasMainActivity) {
1234                printf("main\n");
1235            }
1236            if (hasWidgetReceivers) {
1237                printf("app-widget\n");
1238            }
1239            if (hasImeService) {
1240                printf("ime\n");
1241            }
1242            if (hasWallpaperService) {
1243                printf("wallpaper\n");
1244            }
1245            if (hasOtherActivities) {
1246                printf("other-activities\n");
1247            }
1248            if (isSearchable) {
1249                printf("search\n");
1250            }
1251            if (hasOtherReceivers) {
1252                printf("other-receivers\n");
1253            }
1254            if (hasOtherServices) {
1255                printf("other-services\n");
1256            }
1257
1258            // For modern apps, if screen size buckets haven't been specified
1259            // but the new width ranges have, then infer the buckets from them.
1260            if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1261                    && requiresSmallestWidthDp > 0) {
1262                int compatWidth = compatibleWidthLimitDp;
1263                if (compatWidth <= 0) compatWidth = requiresSmallestWidthDp;
1264                if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1265                    smallScreen = -1;
1266                } else {
1267                    smallScreen = 0;
1268                }
1269                if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1270                    normalScreen = -1;
1271                } else {
1272                    normalScreen = 0;
1273                }
1274                if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1275                    largeScreen = -1;
1276                } else {
1277                    largeScreen = 0;
1278                }
1279                if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1280                    xlargeScreen = -1;
1281                } else {
1282                    xlargeScreen = 0;
1283                }
1284            }
1285
1286            // Determine default values for any unspecified screen sizes,
1287            // based on the target SDK of the package.  As of 4 (donut)
1288            // the screen size support was introduced, so all default to
1289            // enabled.
1290            if (smallScreen > 0) {
1291                smallScreen = targetSdk >= 4 ? -1 : 0;
1292            }
1293            if (normalScreen > 0) {
1294                normalScreen = -1;
1295            }
1296            if (largeScreen > 0) {
1297                largeScreen = targetSdk >= 4 ? -1 : 0;
1298            }
1299            if (xlargeScreen > 0) {
1300                // Introduced in Gingerbread.
1301                xlargeScreen = targetSdk >= 9 ? -1 : 0;
1302            }
1303            if (anyDensity > 0) {
1304                anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1305                        || compatibleWidthLimitDp > 0) ? -1 : 0;
1306            }
1307            printf("supports-screens:");
1308            if (smallScreen != 0) printf(" 'small'");
1309            if (normalScreen != 0) printf(" 'normal'");
1310            if (largeScreen != 0) printf(" 'large'");
1311            if (xlargeScreen != 0) printf(" 'xlarge'");
1312            printf("\n");
1313            printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
1314            if (requiresSmallestWidthDp > 0) {
1315                printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1316            }
1317            if (compatibleWidthLimitDp > 0) {
1318                printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1319            }
1320            if (largestWidthLimitDp > 0) {
1321                printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1322            }
1323
1324            printf("locales:");
1325            const size_t NL = locales.size();
1326            for (size_t i=0; i<NL; i++) {
1327                const char* localeStr =  locales[i].string();
1328                if (localeStr == NULL || strlen(localeStr) == 0) {
1329                    localeStr = "--_--";
1330                }
1331                printf(" '%s'", localeStr);
1332            }
1333            printf("\n");
1334
1335            printf("densities:");
1336            const size_t ND = densities.size();
1337            for (size_t i=0; i<ND; i++) {
1338                printf(" '%d'", densities[i]);
1339            }
1340            printf("\n");
1341
1342            AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1343            if (dir != NULL) {
1344                if (dir->getFileCount() > 0) {
1345                    printf("native-code:");
1346                    for (size_t i=0; i<dir->getFileCount(); i++) {
1347                        printf(" '%s'", dir->getFileName(i).string());
1348                    }
1349                    printf("\n");
1350                }
1351                delete dir;
1352            }
1353        } else if (strcmp("configurations", option) == 0) {
1354            Vector<ResTable_config> configs;
1355            res.getConfigurations(&configs);
1356            const size_t N = configs.size();
1357            for (size_t i=0; i<N; i++) {
1358                printf("%s\n", configs[i].toString().string());
1359            }
1360        } else {
1361            fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
1362            goto bail;
1363        }
1364    }
1365
1366    result = NO_ERROR;
1367
1368bail:
1369    if (asset) {
1370        delete asset;
1371    }
1372    return (result != NO_ERROR);
1373}
1374
1375
1376/*
1377 * Handle the "add" command, which wants to add files to a new or
1378 * pre-existing archive.
1379 */
1380int doAdd(Bundle* bundle)
1381{
1382    ZipFile* zip = NULL;
1383    status_t result = UNKNOWN_ERROR;
1384    const char* zipFileName;
1385
1386    if (bundle->getUpdate()) {
1387        /* avoid confusion */
1388        fprintf(stderr, "ERROR: can't use '-u' with add\n");
1389        goto bail;
1390    }
1391
1392    if (bundle->getFileSpecCount() < 1) {
1393        fprintf(stderr, "ERROR: must specify zip file name\n");
1394        goto bail;
1395    }
1396    zipFileName = bundle->getFileSpecEntry(0);
1397
1398    if (bundle->getFileSpecCount() < 2) {
1399        fprintf(stderr, "NOTE: nothing to do\n");
1400        goto bail;
1401    }
1402
1403    zip = openReadWrite(zipFileName, true);
1404    if (zip == NULL) {
1405        fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
1406        goto bail;
1407    }
1408
1409    for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1410        const char* fileName = bundle->getFileSpecEntry(i);
1411
1412        if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
1413            printf(" '%s'... (from gzip)\n", fileName);
1414            result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
1415        } else {
1416            if (bundle->getJunkPath()) {
1417                String8 storageName = String8(fileName).getPathLeaf();
1418                printf(" '%s' as '%s'...\n", fileName, storageName.string());
1419                result = zip->add(fileName, storageName.string(),
1420                                  bundle->getCompressionMethod(), NULL);
1421            } else {
1422                printf(" '%s'...\n", fileName);
1423                result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
1424            }
1425        }
1426        if (result != NO_ERROR) {
1427            fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
1428            if (result == NAME_NOT_FOUND)
1429                fprintf(stderr, ": file not found\n");
1430            else if (result == ALREADY_EXISTS)
1431                fprintf(stderr, ": already exists in archive\n");
1432            else
1433                fprintf(stderr, "\n");
1434            goto bail;
1435        }
1436    }
1437
1438    result = NO_ERROR;
1439
1440bail:
1441    delete zip;
1442    return (result != NO_ERROR);
1443}
1444
1445
1446/*
1447 * Delete files from an existing archive.
1448 */
1449int doRemove(Bundle* bundle)
1450{
1451    ZipFile* zip = NULL;
1452    status_t result = UNKNOWN_ERROR;
1453    const char* zipFileName;
1454
1455    if (bundle->getFileSpecCount() < 1) {
1456        fprintf(stderr, "ERROR: must specify zip file name\n");
1457        goto bail;
1458    }
1459    zipFileName = bundle->getFileSpecEntry(0);
1460
1461    if (bundle->getFileSpecCount() < 2) {
1462        fprintf(stderr, "NOTE: nothing to do\n");
1463        goto bail;
1464    }
1465
1466    zip = openReadWrite(zipFileName, false);
1467    if (zip == NULL) {
1468        fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
1469            zipFileName);
1470        goto bail;
1471    }
1472
1473    for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1474        const char* fileName = bundle->getFileSpecEntry(i);
1475        ZipEntry* entry;
1476
1477        entry = zip->getEntryByName(fileName);
1478        if (entry == NULL) {
1479            printf(" '%s' NOT FOUND\n", fileName);
1480            continue;
1481        }
1482
1483        result = zip->remove(entry);
1484
1485        if (result != NO_ERROR) {
1486            fprintf(stderr, "Unable to delete '%s' from '%s'\n",
1487                bundle->getFileSpecEntry(i), zipFileName);
1488            goto bail;
1489        }
1490    }
1491
1492    /* update the archive */
1493    zip->flush();
1494
1495bail:
1496    delete zip;
1497    return (result != NO_ERROR);
1498}
1499
1500
1501/*
1502 * Package up an asset directory and associated application files.
1503 */
1504int doPackage(Bundle* bundle)
1505{
1506    const char* outputAPKFile;
1507    int retVal = 1;
1508    status_t err;
1509    sp<AaptAssets> assets;
1510    int N;
1511    FILE* fp;
1512    String8 dependencyFile;
1513
1514    // -c zz_ZZ means do pseudolocalization
1515    ResourceFilter filter;
1516    err = filter.parse(bundle->getConfigurations());
1517    if (err != NO_ERROR) {
1518        goto bail;
1519    }
1520    if (filter.containsPseudo()) {
1521        bundle->setPseudolocalize(true);
1522    }
1523
1524    N = bundle->getFileSpecCount();
1525    if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
1526            && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDir() == NULL) {
1527        fprintf(stderr, "ERROR: no input files\n");
1528        goto bail;
1529    }
1530
1531    outputAPKFile = bundle->getOutputAPKFile();
1532
1533    // Make sure the filenames provided exist and are of the appropriate type.
1534    if (outputAPKFile) {
1535        FileType type;
1536        type = getFileType(outputAPKFile);
1537        if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
1538            fprintf(stderr,
1539                "ERROR: output file '%s' exists but is not regular file\n",
1540                outputAPKFile);
1541            goto bail;
1542        }
1543    }
1544
1545    // Load the assets.
1546    assets = new AaptAssets();
1547
1548    // Set up the resource gathering in assets if we're going to generate
1549    // dependency files. Every time we encounter a resource while slurping
1550    // the tree, we'll add it to these stores so we have full resource paths
1551    // to write to a dependency file.
1552    if (bundle->getGenDependencies()) {
1553        sp<FilePathStore> resPathStore = new FilePathStore;
1554        assets->setFullResPaths(resPathStore);
1555        sp<FilePathStore> assetPathStore = new FilePathStore;
1556        assets->setFullAssetPaths(assetPathStore);
1557    }
1558
1559    err = assets->slurpFromArgs(bundle);
1560    if (err < 0) {
1561        goto bail;
1562    }
1563
1564    if (bundle->getVerbose()) {
1565        assets->print();
1566    }
1567
1568    // If they asked for any fileAs that need to be compiled, do so.
1569    if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
1570        err = buildResources(bundle, assets);
1571        if (err != 0) {
1572            goto bail;
1573        }
1574    }
1575
1576    // At this point we've read everything and processed everything.  From here
1577    // on out it's just writing output files.
1578    if (SourcePos::hasErrors()) {
1579        goto bail;
1580    }
1581
1582    // If we've been asked to generate a dependency file, do that here
1583    if (bundle->getGenDependencies()) {
1584        // If this is the packaging step, generate the dependency file next to
1585        // the output apk (e.g. bin/resources.ap_.d)
1586        if (outputAPKFile) {
1587            dependencyFile = String8(outputAPKFile);
1588            // Add the .d extension to the dependency file.
1589            dependencyFile.append(".d");
1590        } else {
1591            // Else if this is the R.java dependency generation step,
1592            // generate the dependency file in the R.java package subdirectory
1593            // e.g. gen/com/foo/app/R.java.d
1594            dependencyFile = String8(bundle->getRClassDir());
1595            dependencyFile.appendPath("R.java.d");
1596        }
1597        // Make sure we have a clean dependency file to start with
1598        fp = fopen(dependencyFile, "w");
1599        fclose(fp);
1600    }
1601
1602    // Write out R.java constants
1603    if (assets->getPackage() == assets->getSymbolsPrivatePackage()) {
1604        if (bundle->getCustomPackage() == NULL) {
1605            // Write the R.java file into the appropriate class directory
1606            // e.g. gen/com/foo/app/R.java
1607            err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
1608            // If we have library files, we're going to write our R.java file into
1609            // the appropriate class directory for those libraries as well.
1610            // e.g. gen/com/foo/app/lib/R.java
1611            if (bundle->getExtraPackages() != NULL) {
1612                // Split on colon
1613                String8 libs(bundle->getExtraPackages());
1614                char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
1615                while (packageString != NULL) {
1616                    // Write the R.java file out with the correct package name
1617                    err = writeResourceSymbols(bundle, assets, String8(packageString), true);
1618                    packageString = strtok(NULL, ":");
1619                }
1620                libs.unlockBuffer();
1621            }
1622        } else {
1623            const String8 customPkg(bundle->getCustomPackage());
1624            err = writeResourceSymbols(bundle, assets, customPkg, true);
1625        }
1626        if (err < 0) {
1627            goto bail;
1628        }
1629    } else {
1630        err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
1631        if (err < 0) {
1632            goto bail;
1633        }
1634        err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
1635        if (err < 0) {
1636            goto bail;
1637        }
1638    }
1639
1640    // Write out the ProGuard file
1641    err = writeProguardFile(bundle, assets);
1642    if (err < 0) {
1643        goto bail;
1644    }
1645
1646    // Write the apk
1647    if (outputAPKFile) {
1648        err = writeAPK(bundle, assets, String8(outputAPKFile));
1649        if (err != NO_ERROR) {
1650            fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
1651            goto bail;
1652        }
1653    }
1654
1655    // If we've been asked to generate a dependency file, we need to finish up here.
1656    // the writeResourceSymbols and writeAPK functions have already written the target
1657    // half of the dependency file, now we need to write the prerequisites. (files that
1658    // the R.java file or .ap_ file depend on)
1659    if (bundle->getGenDependencies()) {
1660        // Now that writeResourceSymbols or writeAPK has taken care of writing
1661        // the targets to our dependency file, we'll write the prereqs
1662        fp = fopen(dependencyFile, "a+");
1663        fprintf(fp, " : ");
1664        bool includeRaw = (outputAPKFile != NULL);
1665        err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
1666        // Also manually add the AndroidManifeset since it's not under res/ or assets/
1667        // and therefore was not added to our pathstores during slurping
1668        fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
1669        fclose(fp);
1670    }
1671
1672    retVal = 0;
1673bail:
1674    if (SourcePos::hasErrors()) {
1675        SourcePos::printErrors(stderr);
1676    }
1677    return retVal;
1678}
1679
1680/*
1681 * Do PNG Crunching
1682 * PRECONDITIONS
1683 *  -S flag points to a source directory containing drawable* folders
1684 *  -C flag points to destination directory. The folder structure in the
1685 *     source directory will be mirrored to the destination (cache) directory
1686 *
1687 * POSTCONDITIONS
1688 *  Destination directory will be updated to match the PNG files in
1689 *  the source directory.
1690 */
1691int doCrunch(Bundle* bundle)
1692{
1693    fprintf(stdout, "Crunching PNG Files in ");
1694    fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
1695    fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
1696
1697    updatePreProcessedCache(bundle);
1698
1699    return NO_ERROR;
1700}
1701