Command.cpp revision e289bff0ec2b1af4ba773e0c8d49d5fd46eb9921
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 String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree,
295        uint32_t attrRes, String8* outError)
296{
297    ssize_t idx = indexOfAttribute(tree, attrRes);
298    if (idx < 0) {
299        return String8();
300    }
301    Res_value value;
302    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
303        if (value.dataType == Res_value::TYPE_STRING) {
304            size_t len;
305            const uint16_t* str = tree.getAttributeStringValue(idx, &len);
306            return str ? String8(str, len) : String8();
307        }
308        resTable->resolveReference(&value, 0);
309        if (value.dataType != Res_value::TYPE_STRING) {
310            if (outError != NULL) *outError = "attribute is not a string value";
311            return String8();
312        }
313    }
314    size_t len;
315    const Res_value* value2 = &value;
316    const char16_t* str = const_cast<ResTable*>(resTable)->valueToString(value2, 0, NULL, &len);
317    return str ? String8(str, len) : String8();
318}
319
320// These are attribute resource constants for the platform, as found
321// in android.R.attr
322enum {
323    NAME_ATTR = 0x01010003,
324    VERSION_CODE_ATTR = 0x0101021b,
325    VERSION_NAME_ATTR = 0x0101021c,
326    LABEL_ATTR = 0x01010001,
327    ICON_ATTR = 0x01010002,
328    MIN_SDK_VERSION_ATTR = 0x0101020c,
329    MAX_SDK_VERSION_ATTR = 0x01010271,
330    REQ_TOUCH_SCREEN_ATTR = 0x01010227,
331    REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
332    REQ_HARD_KEYBOARD_ATTR = 0x01010229,
333    REQ_NAVIGATION_ATTR = 0x0101022a,
334    REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
335    TARGET_SDK_VERSION_ATTR = 0x01010270,
336    TEST_ONLY_ATTR = 0x01010272,
337    ANY_DENSITY_ATTR = 0x0101026c,
338    GL_ES_VERSION_ATTR = 0x01010281,
339    SMALL_SCREEN_ATTR = 0x01010284,
340    NORMAL_SCREEN_ATTR = 0x01010285,
341    LARGE_SCREEN_ATTR = 0x01010286,
342    XLARGE_SCREEN_ATTR = 0x010102bf,
343    REQUIRED_ATTR = 0x0101028e,
344    SCREEN_SIZE_ATTR = 0x010102ca,
345    SCREEN_DENSITY_ATTR = 0x010102cb,
346    REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
347    COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
348    LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
349};
350
351const char *getComponentName(String8 &pkgName, String8 &componentName) {
352    ssize_t idx = componentName.find(".");
353    String8 retStr(pkgName);
354    if (idx == 0) {
355        retStr += componentName;
356    } else if (idx < 0) {
357        retStr += ".";
358        retStr += componentName;
359    } else {
360        return componentName.string();
361    }
362    return retStr.string();
363}
364
365static void printCompatibleScreens(ResXMLTree& tree) {
366    size_t len;
367    ResXMLTree::event_code_t code;
368    int depth = 0;
369    bool first = true;
370    printf("compatible-screens:");
371    while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
372        if (code == ResXMLTree::END_TAG) {
373            depth--;
374            if (depth < 0) {
375                break;
376            }
377            continue;
378        }
379        if (code != ResXMLTree::START_TAG) {
380            continue;
381        }
382        depth++;
383        String8 tag(tree.getElementName(&len));
384        if (tag == "screen") {
385            int32_t screenSize = getIntegerAttribute(tree,
386                    SCREEN_SIZE_ATTR, NULL, -1);
387            int32_t screenDensity = getIntegerAttribute(tree,
388                    SCREEN_DENSITY_ATTR, NULL, -1);
389            if (screenSize > 0 && screenDensity > 0) {
390                if (!first) {
391                    printf(",");
392                }
393                first = false;
394                printf("'%d/%d'", screenSize, screenDensity);
395            }
396        }
397    }
398    printf("\n");
399}
400
401/*
402 * Handle the "dump" command, to extract select data from an archive.
403 */
404int doDump(Bundle* bundle)
405{
406    status_t result = UNKNOWN_ERROR;
407    Asset* asset = NULL;
408
409    if (bundle->getFileSpecCount() < 1) {
410        fprintf(stderr, "ERROR: no dump option specified\n");
411        return 1;
412    }
413
414    if (bundle->getFileSpecCount() < 2) {
415        fprintf(stderr, "ERROR: no dump file specified\n");
416        return 1;
417    }
418
419    const char* option = bundle->getFileSpecEntry(0);
420    const char* filename = bundle->getFileSpecEntry(1);
421
422    AssetManager assets;
423    void* assetsCookie;
424    if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
425        fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
426        return 1;
427    }
428
429    // Make a dummy config for retrieving resources...  we need to supply
430    // non-default values for some configs so that we can retrieve resources
431    // in the app that don't have a default.  The most important of these is
432    // the API version because key resources like icons will have an implicit
433    // version if they are using newer config types like density.
434    ResTable_config config;
435    config.language[0] = 'e';
436    config.language[1] = 'n';
437    config.country[0] = 'U';
438    config.country[1] = 'S';
439    config.orientation = ResTable_config::ORIENTATION_PORT;
440    config.density = ResTable_config::DENSITY_MEDIUM;
441    config.sdkVersion = 10000; // Very high.
442    config.screenWidthDp = 320;
443    config.screenHeightDp = 480;
444    config.smallestScreenWidthDp = 320;
445    assets.setConfiguration(config);
446
447    const ResTable& res = assets.getResources(false);
448    if (&res == NULL) {
449        fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
450        goto bail;
451    }
452
453    if (strcmp("resources", option) == 0) {
454#ifndef HAVE_ANDROID_OS
455        res.print(bundle->getValues());
456#endif
457    } else if (strcmp("xmltree", option) == 0) {
458        if (bundle->getFileSpecCount() < 3) {
459            fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
460            goto bail;
461        }
462
463        for (int i=2; i<bundle->getFileSpecCount(); i++) {
464            const char* resname = bundle->getFileSpecEntry(i);
465            ResXMLTree tree;
466            asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
467            if (asset == NULL) {
468                fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
469                goto bail;
470            }
471
472            if (tree.setTo(asset->getBuffer(true),
473                           asset->getLength()) != NO_ERROR) {
474                fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
475                goto bail;
476            }
477            tree.restart();
478            printXMLBlock(&tree);
479            tree.uninit();
480            delete asset;
481            asset = NULL;
482        }
483
484    } else if (strcmp("xmlstrings", option) == 0) {
485        if (bundle->getFileSpecCount() < 3) {
486            fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
487            goto bail;
488        }
489
490        for (int i=2; i<bundle->getFileSpecCount(); i++) {
491            const char* resname = bundle->getFileSpecEntry(i);
492            ResXMLTree tree;
493            asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
494            if (asset == NULL) {
495                fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
496                goto bail;
497            }
498
499            if (tree.setTo(asset->getBuffer(true),
500                           asset->getLength()) != NO_ERROR) {
501                fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
502                goto bail;
503            }
504            printStringPool(&tree.getStrings());
505            delete asset;
506            asset = NULL;
507        }
508
509    } else {
510        ResXMLTree tree;
511        asset = assets.openNonAsset("AndroidManifest.xml",
512                                            Asset::ACCESS_BUFFER);
513        if (asset == NULL) {
514            fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
515            goto bail;
516        }
517
518        if (tree.setTo(asset->getBuffer(true),
519                       asset->getLength()) != NO_ERROR) {
520            fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
521            goto bail;
522        }
523        tree.restart();
524
525        if (strcmp("permissions", option) == 0) {
526            size_t len;
527            ResXMLTree::event_code_t code;
528            int depth = 0;
529            while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
530                if (code == ResXMLTree::END_TAG) {
531                    depth--;
532                    continue;
533                }
534                if (code != ResXMLTree::START_TAG) {
535                    continue;
536                }
537                depth++;
538                String8 tag(tree.getElementName(&len));
539                //printf("Depth %d tag %s\n", depth, tag.string());
540                if (depth == 1) {
541                    if (tag != "manifest") {
542                        fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
543                        goto bail;
544                    }
545                    String8 pkg = getAttribute(tree, NULL, "package", NULL);
546                    printf("package: %s\n", pkg.string());
547                } else if (depth == 2 && tag == "permission") {
548                    String8 error;
549                    String8 name = getAttribute(tree, NAME_ATTR, &error);
550                    if (error != "") {
551                        fprintf(stderr, "ERROR: %s\n", error.string());
552                        goto bail;
553                    }
554                    printf("permission: %s\n", name.string());
555                } else if (depth == 2 && tag == "uses-permission") {
556                    String8 error;
557                    String8 name = getAttribute(tree, NAME_ATTR, &error);
558                    if (error != "") {
559                        fprintf(stderr, "ERROR: %s\n", error.string());
560                        goto bail;
561                    }
562                    printf("uses-permission: %s\n", name.string());
563                }
564            }
565        } else if (strcmp("badging", option) == 0) {
566            Vector<String8> locales;
567            res.getLocales(&locales);
568
569            Vector<ResTable_config> configs;
570            res.getConfigurations(&configs);
571            SortedVector<int> densities;
572            const size_t NC = configs.size();
573            for (size_t i=0; i<NC; i++) {
574                int dens = configs[i].density;
575                if (dens == 0) dens = 160;
576                densities.add(dens);
577            }
578
579            size_t len;
580            ResXMLTree::event_code_t code;
581            int depth = 0;
582            String8 error;
583            bool withinActivity = false;
584            bool isMainActivity = false;
585            bool isLauncherActivity = false;
586            bool isSearchable = false;
587            bool withinApplication = false;
588            bool withinReceiver = false;
589            bool withinService = false;
590            bool withinIntentFilter = false;
591            bool hasMainActivity = false;
592            bool hasOtherActivities = false;
593            bool hasOtherReceivers = false;
594            bool hasOtherServices = false;
595            bool hasWallpaperService = false;
596            bool hasImeService = false;
597            bool hasWidgetReceivers = false;
598            bool hasIntentFilter = false;
599            bool actMainActivity = false;
600            bool actWidgetReceivers = false;
601            bool actImeService = false;
602            bool actWallpaperService = false;
603
604            // This next group of variables is used to implement a group of
605            // backward-compatibility heuristics necessitated by the addition of
606            // some new uses-feature constants in 2.1 and 2.2. In most cases, the
607            // heuristic is "if an app requests a permission but doesn't explicitly
608            // request the corresponding <uses-feature>, presume it's there anyway".
609            bool specCameraFeature = false; // camera-related
610            bool specCameraAutofocusFeature = false;
611            bool reqCameraAutofocusFeature = false;
612            bool reqCameraFlashFeature = false;
613            bool hasCameraPermission = false;
614            bool specLocationFeature = false; // location-related
615            bool specNetworkLocFeature = false;
616            bool reqNetworkLocFeature = false;
617            bool specGpsFeature = false;
618            bool reqGpsFeature = false;
619            bool hasMockLocPermission = false;
620            bool hasCoarseLocPermission = false;
621            bool hasGpsPermission = false;
622            bool hasGeneralLocPermission = false;
623            bool specBluetoothFeature = false; // Bluetooth API-related
624            bool hasBluetoothPermission = false;
625            bool specMicrophoneFeature = false; // microphone-related
626            bool hasRecordAudioPermission = false;
627            bool specWiFiFeature = false;
628            bool hasWiFiPermission = false;
629            bool specTelephonyFeature = false; // telephony-related
630            bool reqTelephonySubFeature = false;
631            bool hasTelephonyPermission = false;
632            bool specTouchscreenFeature = false; // touchscreen-related
633            bool specMultitouchFeature = false;
634            bool reqDistinctMultitouchFeature = false;
635            bool specScreenPortraitFeature = false;
636            bool specScreenLandscapeFeature = false;
637            // 2.2 also added some other features that apps can request, but that
638            // have no corresponding permission, so we cannot implement any
639            // back-compatibility heuristic for them. The below are thus unnecessary
640            // (but are retained here for documentary purposes.)
641            //bool specCompassFeature = false;
642            //bool specAccelerometerFeature = false;
643            //bool specProximityFeature = false;
644            //bool specAmbientLightFeature = false;
645            //bool specLiveWallpaperFeature = false;
646
647            int targetSdk = 0;
648            int smallScreen = 1;
649            int normalScreen = 1;
650            int largeScreen = 1;
651            int xlargeScreen = 1;
652            int anyDensity = 1;
653            int requiresSmallestWidthDp = 0;
654            int compatibleWidthLimitDp = 0;
655            int largestWidthLimitDp = 0;
656            String8 pkg;
657            String8 activityName;
658            String8 activityLabel;
659            String8 activityIcon;
660            String8 receiverName;
661            String8 serviceName;
662            while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
663                if (code == ResXMLTree::END_TAG) {
664                    depth--;
665                    if (depth < 2) {
666                        withinApplication = false;
667                    } else if (depth < 3) {
668                        if (withinActivity && isMainActivity && isLauncherActivity) {
669                            const char *aName = getComponentName(pkg, activityName);
670                            printf("launchable-activity:");
671                            if (aName != NULL) {
672                                printf(" name='%s' ", aName);
673                            }
674                            printf(" label='%s' icon='%s'\n",
675                                    activityLabel.string(),
676                                    activityIcon.string());
677                        }
678                        if (!hasIntentFilter) {
679                            hasOtherActivities |= withinActivity;
680                            hasOtherReceivers |= withinReceiver;
681                            hasOtherServices |= withinService;
682                        }
683                        withinActivity = false;
684                        withinService = false;
685                        withinReceiver = false;
686                        hasIntentFilter = false;
687                        isMainActivity = isLauncherActivity = false;
688                    } else if (depth < 4) {
689                        if (withinIntentFilter) {
690                            if (withinActivity) {
691                                hasMainActivity |= actMainActivity;
692                                hasOtherActivities |= !actMainActivity;
693                            } else if (withinReceiver) {
694                                hasWidgetReceivers |= actWidgetReceivers;
695                                hasOtherReceivers |= !actWidgetReceivers;
696                            } else if (withinService) {
697                                hasImeService |= actImeService;
698                                hasWallpaperService |= actWallpaperService;
699                                hasOtherServices |= (!actImeService && !actWallpaperService);
700                            }
701                        }
702                        withinIntentFilter = false;
703                    }
704                    continue;
705                }
706                if (code != ResXMLTree::START_TAG) {
707                    continue;
708                }
709                depth++;
710                String8 tag(tree.getElementName(&len));
711                //printf("Depth %d,  %s\n", depth, tag.string());
712                if (depth == 1) {
713                    if (tag != "manifest") {
714                        fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
715                        goto bail;
716                    }
717                    pkg = getAttribute(tree, NULL, "package", NULL);
718                    printf("package: name='%s' ", pkg.string());
719                    int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
720                    if (error != "") {
721                        fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
722                        goto bail;
723                    }
724                    if (versionCode > 0) {
725                        printf("versionCode='%d' ", versionCode);
726                    } else {
727                        printf("versionCode='' ");
728                    }
729                    String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
730                    if (error != "") {
731                        fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
732                        goto bail;
733                    }
734                    printf("versionName='%s'\n", versionName.string());
735                } else if (depth == 2) {
736                    withinApplication = false;
737                    if (tag == "application") {
738                        withinApplication = true;
739
740                        String8 label;
741                        const size_t NL = locales.size();
742                        for (size_t i=0; i<NL; i++) {
743                            const char* localeStr =  locales[i].string();
744                            assets.setLocale(localeStr != NULL ? localeStr : "");
745                            String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
746                            if (llabel != "") {
747                                if (localeStr == NULL || strlen(localeStr) == 0) {
748                                    label = llabel;
749                                    printf("application-label:'%s'\n", llabel.string());
750                                } else {
751                                    if (label == "") {
752                                        label = llabel;
753                                    }
754                                    printf("application-label-%s:'%s'\n", localeStr,
755                                            llabel.string());
756                                }
757                            }
758                        }
759
760                        ResTable_config tmpConfig = config;
761                        const size_t ND = densities.size();
762                        for (size_t i=0; i<ND; i++) {
763                            tmpConfig.density = densities[i];
764                            assets.setConfiguration(tmpConfig);
765                            String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
766                            if (icon != "") {
767                                printf("application-icon-%d:'%s'\n", densities[i], icon.string());
768                            }
769                        }
770                        assets.setConfiguration(config);
771
772                        String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
773                        if (error != "") {
774                            fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
775                            goto bail;
776                        }
777                        int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
778                        if (error != "") {
779                            fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
780                            goto bail;
781                        }
782                        printf("application: label='%s' ", label.string());
783                        printf("icon='%s'\n", icon.string());
784                        if (testOnly != 0) {
785                            printf("testOnly='%d'\n", testOnly);
786                        }
787                    } else if (tag == "uses-sdk") {
788                        int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
789                        if (error != "") {
790                            error = "";
791                            String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
792                            if (error != "") {
793                                fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
794                                        error.string());
795                                goto bail;
796                            }
797                            if (name == "Donut") targetSdk = 4;
798                            printf("sdkVersion:'%s'\n", name.string());
799                        } else if (code != -1) {
800                            targetSdk = code;
801                            printf("sdkVersion:'%d'\n", code);
802                        }
803                        code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
804                        if (code != -1) {
805                            printf("maxSdkVersion:'%d'\n", code);
806                        }
807                        code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
808                        if (error != "") {
809                            error = "";
810                            String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
811                            if (error != "") {
812                                fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
813                                        error.string());
814                                goto bail;
815                            }
816                            if (name == "Donut" && targetSdk < 4) targetSdk = 4;
817                            printf("targetSdkVersion:'%s'\n", name.string());
818                        } else if (code != -1) {
819                            if (targetSdk < code) {
820                                targetSdk = code;
821                            }
822                            printf("targetSdkVersion:'%d'\n", code);
823                        }
824                    } else if (tag == "uses-configuration") {
825                        int32_t reqTouchScreen = getIntegerAttribute(tree,
826                                REQ_TOUCH_SCREEN_ATTR, NULL, 0);
827                        int32_t reqKeyboardType = getIntegerAttribute(tree,
828                                REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
829                        int32_t reqHardKeyboard = getIntegerAttribute(tree,
830                                REQ_HARD_KEYBOARD_ATTR, NULL, 0);
831                        int32_t reqNavigation = getIntegerAttribute(tree,
832                                REQ_NAVIGATION_ATTR, NULL, 0);
833                        int32_t reqFiveWayNav = getIntegerAttribute(tree,
834                                REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
835                        printf("uses-configuration:");
836                        if (reqTouchScreen != 0) {
837                            printf(" reqTouchScreen='%d'", reqTouchScreen);
838                        }
839                        if (reqKeyboardType != 0) {
840                            printf(" reqKeyboardType='%d'", reqKeyboardType);
841                        }
842                        if (reqHardKeyboard != 0) {
843                            printf(" reqHardKeyboard='%d'", reqHardKeyboard);
844                        }
845                        if (reqNavigation != 0) {
846                            printf(" reqNavigation='%d'", reqNavigation);
847                        }
848                        if (reqFiveWayNav != 0) {
849                            printf(" reqFiveWayNav='%d'", reqFiveWayNav);
850                        }
851                        printf("\n");
852                    } else if (tag == "supports-screens") {
853                        smallScreen = getIntegerAttribute(tree,
854                                SMALL_SCREEN_ATTR, NULL, 1);
855                        normalScreen = getIntegerAttribute(tree,
856                                NORMAL_SCREEN_ATTR, NULL, 1);
857                        largeScreen = getIntegerAttribute(tree,
858                                LARGE_SCREEN_ATTR, NULL, 1);
859                        xlargeScreen = getIntegerAttribute(tree,
860                                XLARGE_SCREEN_ATTR, NULL, 1);
861                        anyDensity = getIntegerAttribute(tree,
862                                ANY_DENSITY_ATTR, NULL, 1);
863                        requiresSmallestWidthDp = getIntegerAttribute(tree,
864                                REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
865                        compatibleWidthLimitDp = getIntegerAttribute(tree,
866                                COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
867                        largestWidthLimitDp = getIntegerAttribute(tree,
868                                LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
869                    } else if (tag == "uses-feature") {
870                        String8 name = getAttribute(tree, NAME_ATTR, &error);
871
872                        if (name != "" && error == "") {
873                            int req = getIntegerAttribute(tree,
874                                    REQUIRED_ATTR, NULL, 1);
875
876                            if (name == "android.hardware.camera") {
877                                specCameraFeature = true;
878                            } else if (name == "android.hardware.camera.autofocus") {
879                                // these have no corresponding permission to check for,
880                                // but should imply the foundational camera permission
881                                reqCameraAutofocusFeature = reqCameraAutofocusFeature || req;
882                                specCameraAutofocusFeature = true;
883                            } else if (req && (name == "android.hardware.camera.flash")) {
884                                // these have no corresponding permission to check for,
885                                // but should imply the foundational camera permission
886                                reqCameraFlashFeature = true;
887                            } else if (name == "android.hardware.location") {
888                                specLocationFeature = true;
889                            } else if (name == "android.hardware.location.network") {
890                                specNetworkLocFeature = true;
891                                reqNetworkLocFeature = reqNetworkLocFeature || req;
892                            } else if (name == "android.hardware.location.gps") {
893                                specGpsFeature = true;
894                                reqGpsFeature = reqGpsFeature || req;
895                            } else if (name == "android.hardware.bluetooth") {
896                                specBluetoothFeature = true;
897                            } else if (name == "android.hardware.touchscreen") {
898                                specTouchscreenFeature = true;
899                            } else if (name == "android.hardware.touchscreen.multitouch") {
900                                specMultitouchFeature = true;
901                            } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
902                                reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req;
903                            } else if (name == "android.hardware.microphone") {
904                                specMicrophoneFeature = true;
905                            } else if (name == "android.hardware.wifi") {
906                                specWiFiFeature = true;
907                            } else if (name == "android.hardware.telephony") {
908                                specTelephonyFeature = true;
909                            } else if (req && (name == "android.hardware.telephony.gsm" ||
910                                               name == "android.hardware.telephony.cdma")) {
911                                // these have no corresponding permission to check for,
912                                // but should imply the foundational telephony permission
913                                reqTelephonySubFeature = true;
914                            } else if (name == "android.hardware.screen.portrait") {
915                                specScreenPortraitFeature = true;
916                            } else if (name == "android.hardware.screen.landscape") {
917                                specScreenLandscapeFeature = true;
918                            }
919                            printf("uses-feature%s:'%s'\n",
920                                    req ? "" : "-not-required", name.string());
921                        } else {
922                            int vers = getIntegerAttribute(tree,
923                                    GL_ES_VERSION_ATTR, &error);
924                            if (error == "") {
925                                printf("uses-gl-es:'0x%x'\n", vers);
926                            }
927                        }
928                    } else if (tag == "uses-permission") {
929                        String8 name = getAttribute(tree, NAME_ATTR, &error);
930                        if (name != "" && error == "") {
931                            if (name == "android.permission.CAMERA") {
932                                hasCameraPermission = true;
933                            } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
934                                hasGpsPermission = true;
935                            } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
936                                hasMockLocPermission = true;
937                            } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
938                                hasCoarseLocPermission = true;
939                            } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
940                                       name == "android.permission.INSTALL_LOCATION_PROVIDER") {
941                                hasGeneralLocPermission = true;
942                            } else if (name == "android.permission.BLUETOOTH" ||
943                                       name == "android.permission.BLUETOOTH_ADMIN") {
944                                hasBluetoothPermission = true;
945                            } else if (name == "android.permission.RECORD_AUDIO") {
946                                hasRecordAudioPermission = true;
947                            } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
948                                       name == "android.permission.CHANGE_WIFI_STATE" ||
949                                       name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
950                                hasWiFiPermission = true;
951                            } else if (name == "android.permission.CALL_PHONE" ||
952                                       name == "android.permission.CALL_PRIVILEGED" ||
953                                       name == "android.permission.MODIFY_PHONE_STATE" ||
954                                       name == "android.permission.PROCESS_OUTGOING_CALLS" ||
955                                       name == "android.permission.READ_SMS" ||
956                                       name == "android.permission.RECEIVE_SMS" ||
957                                       name == "android.permission.RECEIVE_MMS" ||
958                                       name == "android.permission.RECEIVE_WAP_PUSH" ||
959                                       name == "android.permission.SEND_SMS" ||
960                                       name == "android.permission.WRITE_APN_SETTINGS" ||
961                                       name == "android.permission.WRITE_SMS") {
962                                hasTelephonyPermission = true;
963                            }
964                            printf("uses-permission:'%s'\n", name.string());
965                        } else {
966                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
967                                    error.string());
968                            goto bail;
969                        }
970                    } else if (tag == "uses-package") {
971                        String8 name = getAttribute(tree, NAME_ATTR, &error);
972                        if (name != "" && error == "") {
973                            printf("uses-package:'%s'\n", name.string());
974                        } else {
975                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
976                                    error.string());
977                                goto bail;
978                        }
979                    } else if (tag == "original-package") {
980                        String8 name = getAttribute(tree, NAME_ATTR, &error);
981                        if (name != "" && error == "") {
982                            printf("original-package:'%s'\n", name.string());
983                        } else {
984                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
985                                    error.string());
986                                goto bail;
987                        }
988                    } else if (tag == "supports-gl-texture") {
989                        String8 name = getAttribute(tree, NAME_ATTR, &error);
990                        if (name != "" && error == "") {
991                            printf("supports-gl-texture:'%s'\n", name.string());
992                        } else {
993                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
994                                    error.string());
995                                goto bail;
996                        }
997                    } else if (tag == "compatible-screens") {
998                        printCompatibleScreens(tree);
999                        depth--;
1000                    }
1001                } else if (depth == 3 && withinApplication) {
1002                    withinActivity = false;
1003                    withinReceiver = false;
1004                    withinService = false;
1005                    hasIntentFilter = false;
1006                    if(tag == "activity") {
1007                        withinActivity = true;
1008                        activityName = getAttribute(tree, NAME_ATTR, &error);
1009                        if (error != "") {
1010                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1011                            goto bail;
1012                        }
1013
1014                        activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1015                        if (error != "") {
1016                            fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string());
1017                            goto bail;
1018                        }
1019
1020                        activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1021                        if (error != "") {
1022                            fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
1023                            goto bail;
1024                        }
1025                    } else if (tag == "uses-library") {
1026                        String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1027                        if (error != "") {
1028                            fprintf(stderr, "ERROR getting 'android:name' attribute for uses-library: %s\n", error.string());
1029                            goto bail;
1030                        }
1031                        int req = getIntegerAttribute(tree,
1032                                REQUIRED_ATTR, NULL, 1);
1033                        printf("uses-library%s:'%s'\n",
1034                                req ? "" : "-not-required", libraryName.string());
1035                    } else if (tag == "receiver") {
1036                        withinReceiver = true;
1037                        receiverName = getAttribute(tree, NAME_ATTR, &error);
1038
1039                        if (error != "") {
1040                            fprintf(stderr, "ERROR getting 'android:name' attribute for receiver: %s\n", error.string());
1041                            goto bail;
1042                        }
1043                    } else if (tag == "service") {
1044                        withinService = true;
1045                        serviceName = getAttribute(tree, NAME_ATTR, &error);
1046
1047                        if (error != "") {
1048                            fprintf(stderr, "ERROR getting 'android:name' attribute for service: %s\n", error.string());
1049                            goto bail;
1050                        }
1051                    }
1052                } else if ((depth == 4) && (tag == "intent-filter")) {
1053                    hasIntentFilter = true;
1054                    withinIntentFilter = true;
1055                    actMainActivity = actWidgetReceivers = actImeService = actWallpaperService = false;
1056                } else if ((depth == 5) && withinIntentFilter){
1057                    String8 action;
1058                    if (tag == "action") {
1059                        action = getAttribute(tree, NAME_ATTR, &error);
1060                        if (error != "") {
1061                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1062                            goto bail;
1063                        }
1064                        if (withinActivity) {
1065                            if (action == "android.intent.action.MAIN") {
1066                                isMainActivity = true;
1067                                actMainActivity = true;
1068                            }
1069                        } else if (withinReceiver) {
1070                            if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1071                                actWidgetReceivers = true;
1072                            }
1073                        } else if (withinService) {
1074                            if (action == "android.view.InputMethod") {
1075                                actImeService = true;
1076                            } else if (action == "android.service.wallpaper.WallpaperService") {
1077                                actWallpaperService = true;
1078                            }
1079                        }
1080                        if (action == "android.intent.action.SEARCH") {
1081                            isSearchable = true;
1082                        }
1083                    }
1084
1085                    if (tag == "category") {
1086                        String8 category = getAttribute(tree, NAME_ATTR, &error);
1087                        if (error != "") {
1088                            fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string());
1089                            goto bail;
1090                        }
1091                        if (withinActivity) {
1092                            if (category == "android.intent.category.LAUNCHER") {
1093                                isLauncherActivity = true;
1094                            }
1095                        }
1096                    }
1097                }
1098            }
1099
1100            /* The following blocks handle printing "inferred" uses-features, based
1101             * on whether related features or permissions are used by the app.
1102             * Note that the various spec*Feature variables denote whether the
1103             * relevant tag was *present* in the AndroidManfest, not that it was
1104             * present and set to true.
1105             */
1106            // Camera-related back-compatibility logic
1107            if (!specCameraFeature) {
1108                if (reqCameraFlashFeature || reqCameraAutofocusFeature) {
1109                    // if app requested a sub-feature (autofocus or flash) and didn't
1110                    // request the base camera feature, we infer that it meant to
1111                    printf("uses-feature:'android.hardware.camera'\n");
1112                } else if (hasCameraPermission) {
1113                    // if app wants to use camera but didn't request the feature, we infer
1114                    // that it meant to, and further that it wants autofocus
1115                    // (which was the 1.0 - 1.5 behavior)
1116                    printf("uses-feature:'android.hardware.camera'\n");
1117                    if (!specCameraAutofocusFeature) {
1118                        printf("uses-feature:'android.hardware.camera.autofocus'\n");
1119                    }
1120                }
1121            }
1122
1123            // Location-related back-compatibility logic
1124            if (!specLocationFeature &&
1125                (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission ||
1126                 hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) {
1127                // if app either takes a location-related permission or requests one of the
1128                // sub-features, we infer that it also meant to request the base location feature
1129                printf("uses-feature:'android.hardware.location'\n");
1130            }
1131            if (!specGpsFeature && hasGpsPermission) {
1132                // if app takes GPS (FINE location) perm but does not request the GPS
1133                // feature, we infer that it meant to
1134                printf("uses-feature:'android.hardware.location.gps'\n");
1135            }
1136            if (!specNetworkLocFeature && hasCoarseLocPermission) {
1137                // if app takes Network location (COARSE location) perm but does not request the
1138                // network location feature, we infer that it meant to
1139                printf("uses-feature:'android.hardware.location.network'\n");
1140            }
1141
1142            // Bluetooth-related compatibility logic
1143            if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
1144                // if app takes a Bluetooth permission but does not request the Bluetooth
1145                // feature, we infer that it meant to
1146                printf("uses-feature:'android.hardware.bluetooth'\n");
1147            }
1148
1149            // Microphone-related compatibility logic
1150            if (!specMicrophoneFeature && hasRecordAudioPermission) {
1151                // if app takes the record-audio permission but does not request the microphone
1152                // feature, we infer that it meant to
1153                printf("uses-feature:'android.hardware.microphone'\n");
1154            }
1155
1156            // WiFi-related compatibility logic
1157            if (!specWiFiFeature && hasWiFiPermission) {
1158                // if app takes one of the WiFi permissions but does not request the WiFi
1159                // feature, we infer that it meant to
1160                printf("uses-feature:'android.hardware.wifi'\n");
1161            }
1162
1163            // Telephony-related compatibility logic
1164            if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) {
1165                // if app takes one of the telephony permissions or requests a sub-feature but
1166                // does not request the base telephony feature, we infer that it meant to
1167                printf("uses-feature:'android.hardware.telephony'\n");
1168            }
1169
1170            // Touchscreen-related back-compatibility logic
1171            if (!specTouchscreenFeature) { // not a typo!
1172                // all apps are presumed to require a touchscreen, unless they explicitly say
1173                // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
1174                // Note that specTouchscreenFeature is true if the tag is present, regardless
1175                // of whether its value is true or false, so this is safe
1176                printf("uses-feature:'android.hardware.touchscreen'\n");
1177            }
1178            if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
1179                // if app takes one of the telephony permissions or requests a sub-feature but
1180                // does not request the base telephony feature, we infer that it meant to
1181                printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
1182            }
1183
1184            // Landscape/portrait-related compatibility logic
1185            if (!specScreenLandscapeFeature && !specScreenPortraitFeature && (targetSdk < 13)) {
1186                // If app has not specified whether it requires portrait or landscape
1187                // and is targeting an API before Honeycomb MR2, then assume it requires
1188                // both.
1189                printf("uses-feature:'android.hardware.screen.portrait'\n");
1190                printf("uses-feature:'android.hardware.screen.landscape'\n");
1191            }
1192
1193            if (hasMainActivity) {
1194                printf("main\n");
1195            }
1196            if (hasWidgetReceivers) {
1197                printf("app-widget\n");
1198            }
1199            if (hasImeService) {
1200                printf("ime\n");
1201            }
1202            if (hasWallpaperService) {
1203                printf("wallpaper\n");
1204            }
1205            if (hasOtherActivities) {
1206                printf("other-activities\n");
1207            }
1208            if (isSearchable) {
1209                printf("search\n");
1210            }
1211            if (hasOtherReceivers) {
1212                printf("other-receivers\n");
1213            }
1214            if (hasOtherServices) {
1215                printf("other-services\n");
1216            }
1217
1218            // For modern apps, if screen size buckets haven't been specified
1219            // but the new width ranges have, then infer the buckets from them.
1220            if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1221                    && requiresSmallestWidthDp > 0) {
1222                int compatWidth = compatibleWidthLimitDp;
1223                if (compatWidth <= 0) compatWidth = requiresSmallestWidthDp;
1224                if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1225                    smallScreen = -1;
1226                } else {
1227                    smallScreen = 0;
1228                }
1229                if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1230                    normalScreen = -1;
1231                } else {
1232                    normalScreen = 0;
1233                }
1234                if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1235                    largeScreen = -1;
1236                } else {
1237                    largeScreen = 0;
1238                }
1239                if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1240                    xlargeScreen = -1;
1241                } else {
1242                    xlargeScreen = 0;
1243                }
1244            }
1245
1246            // Determine default values for any unspecified screen sizes,
1247            // based on the target SDK of the package.  As of 4 (donut)
1248            // the screen size support was introduced, so all default to
1249            // enabled.
1250            if (smallScreen > 0) {
1251                smallScreen = targetSdk >= 4 ? -1 : 0;
1252            }
1253            if (normalScreen > 0) {
1254                normalScreen = -1;
1255            }
1256            if (largeScreen > 0) {
1257                largeScreen = targetSdk >= 4 ? -1 : 0;
1258            }
1259            if (xlargeScreen > 0) {
1260                // Introduced in Gingerbread.
1261                xlargeScreen = targetSdk >= 9 ? -1 : 0;
1262            }
1263            if (anyDensity > 0) {
1264                anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1265                        || compatibleWidthLimitDp > 0) ? -1 : 0;
1266            }
1267            printf("supports-screens:");
1268            if (smallScreen != 0) printf(" 'small'");
1269            if (normalScreen != 0) printf(" 'normal'");
1270            if (largeScreen != 0) printf(" 'large'");
1271            if (xlargeScreen != 0) printf(" 'xlarge'");
1272            printf("\n");
1273            printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
1274            if (requiresSmallestWidthDp > 0) {
1275                printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1276            }
1277            if (compatibleWidthLimitDp > 0) {
1278                printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1279            }
1280            if (largestWidthLimitDp > 0) {
1281                printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1282            }
1283
1284            printf("locales:");
1285            const size_t NL = locales.size();
1286            for (size_t i=0; i<NL; i++) {
1287                const char* localeStr =  locales[i].string();
1288                if (localeStr == NULL || strlen(localeStr) == 0) {
1289                    localeStr = "--_--";
1290                }
1291                printf(" '%s'", localeStr);
1292            }
1293            printf("\n");
1294
1295            printf("densities:");
1296            const size_t ND = densities.size();
1297            for (size_t i=0; i<ND; i++) {
1298                printf(" '%d'", densities[i]);
1299            }
1300            printf("\n");
1301
1302            AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1303            if (dir != NULL) {
1304                if (dir->getFileCount() > 0) {
1305                    printf("native-code:");
1306                    for (size_t i=0; i<dir->getFileCount(); i++) {
1307                        printf(" '%s'", dir->getFileName(i).string());
1308                    }
1309                    printf("\n");
1310                }
1311                delete dir;
1312            }
1313        } else if (strcmp("configurations", option) == 0) {
1314            Vector<ResTable_config> configs;
1315            res.getConfigurations(&configs);
1316            const size_t N = configs.size();
1317            for (size_t i=0; i<N; i++) {
1318                printf("%s\n", configs[i].toString().string());
1319            }
1320        } else {
1321            fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
1322            goto bail;
1323        }
1324    }
1325
1326    result = NO_ERROR;
1327
1328bail:
1329    if (asset) {
1330        delete asset;
1331    }
1332    return (result != NO_ERROR);
1333}
1334
1335
1336/*
1337 * Handle the "add" command, which wants to add files to a new or
1338 * pre-existing archive.
1339 */
1340int doAdd(Bundle* bundle)
1341{
1342    ZipFile* zip = NULL;
1343    status_t result = UNKNOWN_ERROR;
1344    const char* zipFileName;
1345
1346    if (bundle->getUpdate()) {
1347        /* avoid confusion */
1348        fprintf(stderr, "ERROR: can't use '-u' with add\n");
1349        goto bail;
1350    }
1351
1352    if (bundle->getFileSpecCount() < 1) {
1353        fprintf(stderr, "ERROR: must specify zip file name\n");
1354        goto bail;
1355    }
1356    zipFileName = bundle->getFileSpecEntry(0);
1357
1358    if (bundle->getFileSpecCount() < 2) {
1359        fprintf(stderr, "NOTE: nothing to do\n");
1360        goto bail;
1361    }
1362
1363    zip = openReadWrite(zipFileName, true);
1364    if (zip == NULL) {
1365        fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
1366        goto bail;
1367    }
1368
1369    for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1370        const char* fileName = bundle->getFileSpecEntry(i);
1371
1372        if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
1373            printf(" '%s'... (from gzip)\n", fileName);
1374            result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
1375        } else {
1376            if (bundle->getJunkPath()) {
1377                String8 storageName = String8(fileName).getPathLeaf();
1378                printf(" '%s' as '%s'...\n", fileName, storageName.string());
1379                result = zip->add(fileName, storageName.string(),
1380                                  bundle->getCompressionMethod(), NULL);
1381            } else {
1382                printf(" '%s'...\n", fileName);
1383                result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
1384            }
1385        }
1386        if (result != NO_ERROR) {
1387            fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
1388            if (result == NAME_NOT_FOUND)
1389                fprintf(stderr, ": file not found\n");
1390            else if (result == ALREADY_EXISTS)
1391                fprintf(stderr, ": already exists in archive\n");
1392            else
1393                fprintf(stderr, "\n");
1394            goto bail;
1395        }
1396    }
1397
1398    result = NO_ERROR;
1399
1400bail:
1401    delete zip;
1402    return (result != NO_ERROR);
1403}
1404
1405
1406/*
1407 * Delete files from an existing archive.
1408 */
1409int doRemove(Bundle* bundle)
1410{
1411    ZipFile* zip = NULL;
1412    status_t result = UNKNOWN_ERROR;
1413    const char* zipFileName;
1414
1415    if (bundle->getFileSpecCount() < 1) {
1416        fprintf(stderr, "ERROR: must specify zip file name\n");
1417        goto bail;
1418    }
1419    zipFileName = bundle->getFileSpecEntry(0);
1420
1421    if (bundle->getFileSpecCount() < 2) {
1422        fprintf(stderr, "NOTE: nothing to do\n");
1423        goto bail;
1424    }
1425
1426    zip = openReadWrite(zipFileName, false);
1427    if (zip == NULL) {
1428        fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
1429            zipFileName);
1430        goto bail;
1431    }
1432
1433    for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1434        const char* fileName = bundle->getFileSpecEntry(i);
1435        ZipEntry* entry;
1436
1437        entry = zip->getEntryByName(fileName);
1438        if (entry == NULL) {
1439            printf(" '%s' NOT FOUND\n", fileName);
1440            continue;
1441        }
1442
1443        result = zip->remove(entry);
1444
1445        if (result != NO_ERROR) {
1446            fprintf(stderr, "Unable to delete '%s' from '%s'\n",
1447                bundle->getFileSpecEntry(i), zipFileName);
1448            goto bail;
1449        }
1450    }
1451
1452    /* update the archive */
1453    zip->flush();
1454
1455bail:
1456    delete zip;
1457    return (result != NO_ERROR);
1458}
1459
1460
1461/*
1462 * Package up an asset directory and associated application files.
1463 */
1464int doPackage(Bundle* bundle)
1465{
1466    const char* outputAPKFile;
1467    int retVal = 1;
1468    status_t err;
1469    sp<AaptAssets> assets;
1470    int N;
1471
1472    // -c zz_ZZ means do pseudolocalization
1473    ResourceFilter filter;
1474    err = filter.parse(bundle->getConfigurations());
1475    if (err != NO_ERROR) {
1476        goto bail;
1477    }
1478    if (filter.containsPseudo()) {
1479        bundle->setPseudolocalize(true);
1480    }
1481
1482    N = bundle->getFileSpecCount();
1483    if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
1484            && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDir() == NULL) {
1485        fprintf(stderr, "ERROR: no input files\n");
1486        goto bail;
1487    }
1488
1489    outputAPKFile = bundle->getOutputAPKFile();
1490
1491    // Make sure the filenames provided exist and are of the appropriate type.
1492    if (outputAPKFile) {
1493        FileType type;
1494        type = getFileType(outputAPKFile);
1495        if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
1496            fprintf(stderr,
1497                "ERROR: output file '%s' exists but is not regular file\n",
1498                outputAPKFile);
1499            goto bail;
1500        }
1501    }
1502
1503    // Load the assets.
1504    assets = new AaptAssets();
1505    err = assets->slurpFromArgs(bundle);
1506    if (err < 0) {
1507        goto bail;
1508    }
1509
1510    if (bundle->getVerbose()) {
1511        assets->print();
1512    }
1513
1514    // If they asked for any files that need to be compiled, do so.
1515    if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
1516        err = buildResources(bundle, assets);
1517        if (err != 0) {
1518            goto bail;
1519        }
1520    }
1521
1522    // At this point we've read everything and processed everything.  From here
1523    // on out it's just writing output files.
1524    if (SourcePos::hasErrors()) {
1525        goto bail;
1526    }
1527
1528    // Write out R.java constants
1529    if (assets->getPackage() == assets->getSymbolsPrivatePackage()) {
1530        if (bundle->getCustomPackage() == NULL) {
1531            err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
1532        } else {
1533            const String8 customPkg(bundle->getCustomPackage());
1534            err = writeResourceSymbols(bundle, assets, customPkg, true);
1535        }
1536        if (err < 0) {
1537            goto bail;
1538        }
1539    } else {
1540        err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
1541        if (err < 0) {
1542            goto bail;
1543        }
1544        err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
1545        if (err < 0) {
1546            goto bail;
1547        }
1548    }
1549
1550    // Write out the ProGuard file
1551    err = writeProguardFile(bundle, assets);
1552    if (err < 0) {
1553        goto bail;
1554    }
1555
1556    // Write the apk
1557    if (outputAPKFile) {
1558        err = writeAPK(bundle, assets, String8(outputAPKFile));
1559        if (err != NO_ERROR) {
1560            fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
1561            goto bail;
1562        }
1563    }
1564
1565    retVal = 0;
1566bail:
1567    if (SourcePos::hasErrors()) {
1568        SourcePos::printErrors(stderr);
1569    }
1570    return retVal;
1571}
1572