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