Command.cpp revision 1644c6d7f4931d0b4fe7ea77c63b016af01a46d3
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
483    } else if (strcmp("strings", option) == 0) {
484        const ResStringPool* pool = res.getTableStringBlock(0);
485        printStringPool(pool);
486
487    } else if (strcmp("xmltree", option) == 0) {
488        if (bundle->getFileSpecCount() < 3) {
489            fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
490            goto bail;
491        }
492
493        for (int i=2; i<bundle->getFileSpecCount(); i++) {
494            const char* resname = bundle->getFileSpecEntry(i);
495            ResXMLTree tree;
496            asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
497            if (asset == NULL) {
498                fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
499                goto bail;
500            }
501
502            if (tree.setTo(asset->getBuffer(true),
503                           asset->getLength()) != NO_ERROR) {
504                fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
505                goto bail;
506            }
507            tree.restart();
508            printXMLBlock(&tree);
509            tree.uninit();
510            delete asset;
511            asset = NULL;
512        }
513
514    } else if (strcmp("xmlstrings", option) == 0) {
515        if (bundle->getFileSpecCount() < 3) {
516            fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
517            goto bail;
518        }
519
520        for (int i=2; i<bundle->getFileSpecCount(); i++) {
521            const char* resname = bundle->getFileSpecEntry(i);
522            ResXMLTree tree;
523            asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
524            if (asset == NULL) {
525                fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
526                goto bail;
527            }
528
529            if (tree.setTo(asset->getBuffer(true),
530                           asset->getLength()) != NO_ERROR) {
531                fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
532                goto bail;
533            }
534            printStringPool(&tree.getStrings());
535            delete asset;
536            asset = NULL;
537        }
538
539    } else {
540        ResXMLTree tree;
541        asset = assets.openNonAsset("AndroidManifest.xml",
542                                            Asset::ACCESS_BUFFER);
543        if (asset == NULL) {
544            fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
545            goto bail;
546        }
547
548        if (tree.setTo(asset->getBuffer(true),
549                       asset->getLength()) != NO_ERROR) {
550            fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
551            goto bail;
552        }
553        tree.restart();
554
555        if (strcmp("permissions", option) == 0) {
556            size_t len;
557            ResXMLTree::event_code_t code;
558            int depth = 0;
559            while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
560                if (code == ResXMLTree::END_TAG) {
561                    depth--;
562                    continue;
563                }
564                if (code != ResXMLTree::START_TAG) {
565                    continue;
566                }
567                depth++;
568                String8 tag(tree.getElementName(&len));
569                //printf("Depth %d tag %s\n", depth, tag.string());
570                if (depth == 1) {
571                    if (tag != "manifest") {
572                        fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
573                        goto bail;
574                    }
575                    String8 pkg = getAttribute(tree, NULL, "package", NULL);
576                    printf("package: %s\n", pkg.string());
577                } else if (depth == 2 && tag == "permission") {
578                    String8 error;
579                    String8 name = getAttribute(tree, NAME_ATTR, &error);
580                    if (error != "") {
581                        fprintf(stderr, "ERROR: %s\n", error.string());
582                        goto bail;
583                    }
584                    printf("permission: %s\n", name.string());
585                } else if (depth == 2 && tag == "uses-permission") {
586                    String8 error;
587                    String8 name = getAttribute(tree, NAME_ATTR, &error);
588                    if (error != "") {
589                        fprintf(stderr, "ERROR: %s\n", error.string());
590                        goto bail;
591                    }
592                    printf("uses-permission: %s\n", name.string());
593                }
594            }
595        } else if (strcmp("badging", option) == 0) {
596            Vector<String8> locales;
597            res.getLocales(&locales);
598
599            Vector<ResTable_config> configs;
600            res.getConfigurations(&configs);
601            SortedVector<int> densities;
602            const size_t NC = configs.size();
603            for (size_t i=0; i<NC; i++) {
604                int dens = configs[i].density;
605                if (dens == 0) dens = 160;
606                densities.add(dens);
607            }
608
609            size_t len;
610            ResXMLTree::event_code_t code;
611            int depth = 0;
612            String8 error;
613            bool withinActivity = false;
614            bool isMainActivity = false;
615            bool isLauncherActivity = false;
616            bool isSearchable = false;
617            bool withinApplication = false;
618            bool withinReceiver = false;
619            bool withinService = false;
620            bool withinIntentFilter = false;
621            bool hasMainActivity = false;
622            bool hasOtherActivities = false;
623            bool hasOtherReceivers = false;
624            bool hasOtherServices = false;
625            bool hasWallpaperService = false;
626            bool hasImeService = false;
627            bool hasWidgetReceivers = false;
628            bool hasIntentFilter = false;
629            bool actMainActivity = false;
630            bool actWidgetReceivers = false;
631            bool actImeService = false;
632            bool actWallpaperService = false;
633
634            // These two implement the implicit permissions that are granted
635            // to pre-1.6 applications.
636            bool hasWriteExternalStoragePermission = false;
637            bool hasReadPhoneStatePermission = false;
638
639            // This next group of variables is used to implement a group of
640            // backward-compatibility heuristics necessitated by the addition of
641            // some new uses-feature constants in 2.1 and 2.2. In most cases, the
642            // heuristic is "if an app requests a permission but doesn't explicitly
643            // request the corresponding <uses-feature>, presume it's there anyway".
644            bool specCameraFeature = false; // camera-related
645            bool specCameraAutofocusFeature = false;
646            bool reqCameraAutofocusFeature = false;
647            bool reqCameraFlashFeature = false;
648            bool hasCameraPermission = false;
649            bool specLocationFeature = false; // location-related
650            bool specNetworkLocFeature = false;
651            bool reqNetworkLocFeature = false;
652            bool specGpsFeature = false;
653            bool reqGpsFeature = false;
654            bool hasMockLocPermission = false;
655            bool hasCoarseLocPermission = false;
656            bool hasGpsPermission = false;
657            bool hasGeneralLocPermission = false;
658            bool specBluetoothFeature = false; // Bluetooth API-related
659            bool hasBluetoothPermission = false;
660            bool specMicrophoneFeature = false; // microphone-related
661            bool hasRecordAudioPermission = false;
662            bool specWiFiFeature = false;
663            bool hasWiFiPermission = false;
664            bool specTelephonyFeature = false; // telephony-related
665            bool reqTelephonySubFeature = false;
666            bool hasTelephonyPermission = false;
667            bool specTouchscreenFeature = false; // touchscreen-related
668            bool specMultitouchFeature = false;
669            bool reqDistinctMultitouchFeature = false;
670            bool specScreenPortraitFeature = false;
671            bool specScreenLandscapeFeature = false;
672            bool reqScreenPortraitFeature = false;
673            bool reqScreenLandscapeFeature = false;
674            // 2.2 also added some other features that apps can request, but that
675            // have no corresponding permission, so we cannot implement any
676            // back-compatibility heuristic for them. The below are thus unnecessary
677            // (but are retained here for documentary purposes.)
678            //bool specCompassFeature = false;
679            //bool specAccelerometerFeature = false;
680            //bool specProximityFeature = false;
681            //bool specAmbientLightFeature = false;
682            //bool specLiveWallpaperFeature = false;
683
684            int targetSdk = 0;
685            int smallScreen = 1;
686            int normalScreen = 1;
687            int largeScreen = 1;
688            int xlargeScreen = 1;
689            int anyDensity = 1;
690            int requiresSmallestWidthDp = 0;
691            int compatibleWidthLimitDp = 0;
692            int largestWidthLimitDp = 0;
693            String8 pkg;
694            String8 activityName;
695            String8 activityLabel;
696            String8 activityIcon;
697            String8 receiverName;
698            String8 serviceName;
699            while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
700                if (code == ResXMLTree::END_TAG) {
701                    depth--;
702                    if (depth < 2) {
703                        withinApplication = false;
704                    } else if (depth < 3) {
705                        if (withinActivity && isMainActivity && isLauncherActivity) {
706                            const char *aName = getComponentName(pkg, activityName);
707                            printf("launchable-activity:");
708                            if (aName != NULL) {
709                                printf(" name='%s' ", aName);
710                            }
711                            printf(" label='%s' icon='%s'\n",
712                                    activityLabel.string(),
713                                    activityIcon.string());
714                        }
715                        if (!hasIntentFilter) {
716                            hasOtherActivities |= withinActivity;
717                            hasOtherReceivers |= withinReceiver;
718                            hasOtherServices |= withinService;
719                        }
720                        withinActivity = false;
721                        withinService = false;
722                        withinReceiver = false;
723                        hasIntentFilter = false;
724                        isMainActivity = isLauncherActivity = false;
725                    } else if (depth < 4) {
726                        if (withinIntentFilter) {
727                            if (withinActivity) {
728                                hasMainActivity |= actMainActivity;
729                                hasOtherActivities |= !actMainActivity;
730                            } else if (withinReceiver) {
731                                hasWidgetReceivers |= actWidgetReceivers;
732                                hasOtherReceivers |= !actWidgetReceivers;
733                            } else if (withinService) {
734                                hasImeService |= actImeService;
735                                hasWallpaperService |= actWallpaperService;
736                                hasOtherServices |= (!actImeService && !actWallpaperService);
737                            }
738                        }
739                        withinIntentFilter = false;
740                    }
741                    continue;
742                }
743                if (code != ResXMLTree::START_TAG) {
744                    continue;
745                }
746                depth++;
747                String8 tag(tree.getElementName(&len));
748                //printf("Depth %d,  %s\n", depth, tag.string());
749                if (depth == 1) {
750                    if (tag != "manifest") {
751                        fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
752                        goto bail;
753                    }
754                    pkg = getAttribute(tree, NULL, "package", NULL);
755                    printf("package: name='%s' ", pkg.string());
756                    int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
757                    if (error != "") {
758                        fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
759                        goto bail;
760                    }
761                    if (versionCode > 0) {
762                        printf("versionCode='%d' ", versionCode);
763                    } else {
764                        printf("versionCode='' ");
765                    }
766                    String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
767                    if (error != "") {
768                        fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
769                        goto bail;
770                    }
771                    printf("versionName='%s'\n", versionName.string());
772                } else if (depth == 2) {
773                    withinApplication = false;
774                    if (tag == "application") {
775                        withinApplication = true;
776
777                        String8 label;
778                        const size_t NL = locales.size();
779                        for (size_t i=0; i<NL; i++) {
780                            const char* localeStr =  locales[i].string();
781                            assets.setLocale(localeStr != NULL ? localeStr : "");
782                            String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
783                            if (llabel != "") {
784                                if (localeStr == NULL || strlen(localeStr) == 0) {
785                                    label = llabel;
786                                    printf("application-label:'%s'\n", llabel.string());
787                                } else {
788                                    if (label == "") {
789                                        label = llabel;
790                                    }
791                                    printf("application-label-%s:'%s'\n", localeStr,
792                                            llabel.string());
793                                }
794                            }
795                        }
796
797                        ResTable_config tmpConfig = config;
798                        const size_t ND = densities.size();
799                        for (size_t i=0; i<ND; i++) {
800                            tmpConfig.density = densities[i];
801                            assets.setConfiguration(tmpConfig);
802                            String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
803                            if (icon != "") {
804                                printf("application-icon-%d:'%s'\n", densities[i], icon.string());
805                            }
806                        }
807                        assets.setConfiguration(config);
808
809                        String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
810                        if (error != "") {
811                            fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
812                            goto bail;
813                        }
814                        int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
815                        if (error != "") {
816                            fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
817                            goto bail;
818                        }
819                        printf("application: label='%s' ", label.string());
820                        printf("icon='%s'\n", icon.string());
821                        if (testOnly != 0) {
822                            printf("testOnly='%d'\n", testOnly);
823                        }
824                    } else if (tag == "uses-sdk") {
825                        int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
826                        if (error != "") {
827                            error = "";
828                            String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
829                            if (error != "") {
830                                fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
831                                        error.string());
832                                goto bail;
833                            }
834                            if (name == "Donut") targetSdk = 4;
835                            printf("sdkVersion:'%s'\n", name.string());
836                        } else if (code != -1) {
837                            targetSdk = code;
838                            printf("sdkVersion:'%d'\n", code);
839                        }
840                        code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
841                        if (code != -1) {
842                            printf("maxSdkVersion:'%d'\n", code);
843                        }
844                        code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
845                        if (error != "") {
846                            error = "";
847                            String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
848                            if (error != "") {
849                                fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
850                                        error.string());
851                                goto bail;
852                            }
853                            if (name == "Donut" && targetSdk < 4) targetSdk = 4;
854                            printf("targetSdkVersion:'%s'\n", name.string());
855                        } else if (code != -1) {
856                            if (targetSdk < code) {
857                                targetSdk = code;
858                            }
859                            printf("targetSdkVersion:'%d'\n", code);
860                        }
861                    } else if (tag == "uses-configuration") {
862                        int32_t reqTouchScreen = getIntegerAttribute(tree,
863                                REQ_TOUCH_SCREEN_ATTR, NULL, 0);
864                        int32_t reqKeyboardType = getIntegerAttribute(tree,
865                                REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
866                        int32_t reqHardKeyboard = getIntegerAttribute(tree,
867                                REQ_HARD_KEYBOARD_ATTR, NULL, 0);
868                        int32_t reqNavigation = getIntegerAttribute(tree,
869                                REQ_NAVIGATION_ATTR, NULL, 0);
870                        int32_t reqFiveWayNav = getIntegerAttribute(tree,
871                                REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
872                        printf("uses-configuration:");
873                        if (reqTouchScreen != 0) {
874                            printf(" reqTouchScreen='%d'", reqTouchScreen);
875                        }
876                        if (reqKeyboardType != 0) {
877                            printf(" reqKeyboardType='%d'", reqKeyboardType);
878                        }
879                        if (reqHardKeyboard != 0) {
880                            printf(" reqHardKeyboard='%d'", reqHardKeyboard);
881                        }
882                        if (reqNavigation != 0) {
883                            printf(" reqNavigation='%d'", reqNavigation);
884                        }
885                        if (reqFiveWayNav != 0) {
886                            printf(" reqFiveWayNav='%d'", reqFiveWayNav);
887                        }
888                        printf("\n");
889                    } else if (tag == "supports-screens") {
890                        smallScreen = getIntegerAttribute(tree,
891                                SMALL_SCREEN_ATTR, NULL, 1);
892                        normalScreen = getIntegerAttribute(tree,
893                                NORMAL_SCREEN_ATTR, NULL, 1);
894                        largeScreen = getIntegerAttribute(tree,
895                                LARGE_SCREEN_ATTR, NULL, 1);
896                        xlargeScreen = getIntegerAttribute(tree,
897                                XLARGE_SCREEN_ATTR, NULL, 1);
898                        anyDensity = getIntegerAttribute(tree,
899                                ANY_DENSITY_ATTR, NULL, 1);
900                        requiresSmallestWidthDp = getIntegerAttribute(tree,
901                                REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
902                        compatibleWidthLimitDp = getIntegerAttribute(tree,
903                                COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
904                        largestWidthLimitDp = getIntegerAttribute(tree,
905                                LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
906                    } else if (tag == "uses-feature") {
907                        String8 name = getAttribute(tree, NAME_ATTR, &error);
908
909                        if (name != "" && error == "") {
910                            int req = getIntegerAttribute(tree,
911                                    REQUIRED_ATTR, NULL, 1);
912
913                            if (name == "android.hardware.camera") {
914                                specCameraFeature = true;
915                            } else if (name == "android.hardware.camera.autofocus") {
916                                // these have no corresponding permission to check for,
917                                // but should imply the foundational camera permission
918                                reqCameraAutofocusFeature = reqCameraAutofocusFeature || req;
919                                specCameraAutofocusFeature = true;
920                            } else if (req && (name == "android.hardware.camera.flash")) {
921                                // these have no corresponding permission to check for,
922                                // but should imply the foundational camera permission
923                                reqCameraFlashFeature = true;
924                            } else if (name == "android.hardware.location") {
925                                specLocationFeature = true;
926                            } else if (name == "android.hardware.location.network") {
927                                specNetworkLocFeature = true;
928                                reqNetworkLocFeature = reqNetworkLocFeature || req;
929                            } else if (name == "android.hardware.location.gps") {
930                                specGpsFeature = true;
931                                reqGpsFeature = reqGpsFeature || req;
932                            } else if (name == "android.hardware.bluetooth") {
933                                specBluetoothFeature = true;
934                            } else if (name == "android.hardware.touchscreen") {
935                                specTouchscreenFeature = true;
936                            } else if (name == "android.hardware.touchscreen.multitouch") {
937                                specMultitouchFeature = true;
938                            } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
939                                reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req;
940                            } else if (name == "android.hardware.microphone") {
941                                specMicrophoneFeature = true;
942                            } else if (name == "android.hardware.wifi") {
943                                specWiFiFeature = true;
944                            } else if (name == "android.hardware.telephony") {
945                                specTelephonyFeature = true;
946                            } else if (req && (name == "android.hardware.telephony.gsm" ||
947                                               name == "android.hardware.telephony.cdma")) {
948                                // these have no corresponding permission to check for,
949                                // but should imply the foundational telephony permission
950                                reqTelephonySubFeature = true;
951                            } else if (name == "android.hardware.screen.portrait") {
952                                specScreenPortraitFeature = true;
953                            } else if (name == "android.hardware.screen.landscape") {
954                                specScreenLandscapeFeature = true;
955                            }
956                            printf("uses-feature%s:'%s'\n",
957                                    req ? "" : "-not-required", name.string());
958                        } else {
959                            int vers = getIntegerAttribute(tree,
960                                    GL_ES_VERSION_ATTR, &error);
961                            if (error == "") {
962                                printf("uses-gl-es:'0x%x'\n", vers);
963                            }
964                        }
965                    } else if (tag == "uses-permission") {
966                        String8 name = getAttribute(tree, NAME_ATTR, &error);
967                        if (name != "" && error == "") {
968                            if (name == "android.permission.CAMERA") {
969                                hasCameraPermission = true;
970                            } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
971                                hasGpsPermission = true;
972                            } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
973                                hasMockLocPermission = true;
974                            } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
975                                hasCoarseLocPermission = true;
976                            } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
977                                       name == "android.permission.INSTALL_LOCATION_PROVIDER") {
978                                hasGeneralLocPermission = true;
979                            } else if (name == "android.permission.BLUETOOTH" ||
980                                       name == "android.permission.BLUETOOTH_ADMIN") {
981                                hasBluetoothPermission = true;
982                            } else if (name == "android.permission.RECORD_AUDIO") {
983                                hasRecordAudioPermission = true;
984                            } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
985                                       name == "android.permission.CHANGE_WIFI_STATE" ||
986                                       name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
987                                hasWiFiPermission = true;
988                            } else if (name == "android.permission.CALL_PHONE" ||
989                                       name == "android.permission.CALL_PRIVILEGED" ||
990                                       name == "android.permission.MODIFY_PHONE_STATE" ||
991                                       name == "android.permission.PROCESS_OUTGOING_CALLS" ||
992                                       name == "android.permission.READ_SMS" ||
993                                       name == "android.permission.RECEIVE_SMS" ||
994                                       name == "android.permission.RECEIVE_MMS" ||
995                                       name == "android.permission.RECEIVE_WAP_PUSH" ||
996                                       name == "android.permission.SEND_SMS" ||
997                                       name == "android.permission.WRITE_APN_SETTINGS" ||
998                                       name == "android.permission.WRITE_SMS") {
999                                hasTelephonyPermission = true;
1000                            } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1001                                hasWriteExternalStoragePermission = true;
1002                            } else if (name == "android.permission.READ_PHONE_STATE") {
1003                                hasReadPhoneStatePermission = true;
1004                            }
1005                            printf("uses-permission:'%s'\n", name.string());
1006                        } else {
1007                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1008                                    error.string());
1009                            goto bail;
1010                        }
1011                    } else if (tag == "uses-package") {
1012                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1013                        if (name != "" && error == "") {
1014                            printf("uses-package:'%s'\n", name.string());
1015                        } else {
1016                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1017                                    error.string());
1018                                goto bail;
1019                        }
1020                    } else if (tag == "original-package") {
1021                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1022                        if (name != "" && error == "") {
1023                            printf("original-package:'%s'\n", name.string());
1024                        } else {
1025                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1026                                    error.string());
1027                                goto bail;
1028                        }
1029                    } else if (tag == "supports-gl-texture") {
1030                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1031                        if (name != "" && error == "") {
1032                            printf("supports-gl-texture:'%s'\n", name.string());
1033                        } else {
1034                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1035                                    error.string());
1036                                goto bail;
1037                        }
1038                    } else if (tag == "compatible-screens") {
1039                        printCompatibleScreens(tree);
1040                        depth--;
1041                    } else if (tag == "package-verifier") {
1042                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1043                        if (name != "" && error == "") {
1044                            String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
1045                            if (publicKey != "" && error == "") {
1046                                printf("package-verifier: name='%s' publicKey='%s'\n",
1047                                        name.string(), publicKey.string());
1048                            }
1049                        }
1050                    }
1051                } else if (depth == 3 && withinApplication) {
1052                    withinActivity = false;
1053                    withinReceiver = false;
1054                    withinService = false;
1055                    hasIntentFilter = false;
1056                    if(tag == "activity") {
1057                        withinActivity = true;
1058                        activityName = getAttribute(tree, NAME_ATTR, &error);
1059                        if (error != "") {
1060                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1061                            goto bail;
1062                        }
1063
1064                        activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1065                        if (error != "") {
1066                            fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string());
1067                            goto bail;
1068                        }
1069
1070                        activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1071                        if (error != "") {
1072                            fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
1073                            goto bail;
1074                        }
1075
1076                        int32_t orien = getResolvedIntegerAttribute(&res, tree,
1077                                SCREEN_ORIENTATION_ATTR, &error);
1078                        if (error == "") {
1079                            if (orien == 0 || orien == 6 || orien == 8) {
1080                                // Requests landscape, sensorLandscape, or reverseLandscape.
1081                                reqScreenLandscapeFeature = true;
1082                            } else if (orien == 1 || orien == 7 || orien == 9) {
1083                                // Requests portrait, sensorPortrait, or reversePortrait.
1084                                reqScreenPortraitFeature = true;
1085                            }
1086                        }
1087                    } else if (tag == "uses-library") {
1088                        String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1089                        if (error != "") {
1090                            fprintf(stderr, "ERROR getting 'android:name' attribute for uses-library: %s\n", error.string());
1091                            goto bail;
1092                        }
1093                        int req = getIntegerAttribute(tree,
1094                                REQUIRED_ATTR, NULL, 1);
1095                        printf("uses-library%s:'%s'\n",
1096                                req ? "" : "-not-required", libraryName.string());
1097                    } else if (tag == "receiver") {
1098                        withinReceiver = true;
1099                        receiverName = getAttribute(tree, NAME_ATTR, &error);
1100
1101                        if (error != "") {
1102                            fprintf(stderr, "ERROR getting 'android:name' attribute for receiver: %s\n", error.string());
1103                            goto bail;
1104                        }
1105                    } else if (tag == "service") {
1106                        withinService = true;
1107                        serviceName = getAttribute(tree, NAME_ATTR, &error);
1108
1109                        if (error != "") {
1110                            fprintf(stderr, "ERROR getting 'android:name' attribute for service: %s\n", error.string());
1111                            goto bail;
1112                        }
1113                    }
1114                } else if ((depth == 4) && (tag == "intent-filter")) {
1115                    hasIntentFilter = true;
1116                    withinIntentFilter = true;
1117                    actMainActivity = actWidgetReceivers = actImeService = actWallpaperService = false;
1118                } else if ((depth == 5) && withinIntentFilter){
1119                    String8 action;
1120                    if (tag == "action") {
1121                        action = getAttribute(tree, NAME_ATTR, &error);
1122                        if (error != "") {
1123                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1124                            goto bail;
1125                        }
1126                        if (withinActivity) {
1127                            if (action == "android.intent.action.MAIN") {
1128                                isMainActivity = true;
1129                                actMainActivity = true;
1130                            }
1131                        } else if (withinReceiver) {
1132                            if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1133                                actWidgetReceivers = true;
1134                            }
1135                        } else if (withinService) {
1136                            if (action == "android.view.InputMethod") {
1137                                actImeService = true;
1138                            } else if (action == "android.service.wallpaper.WallpaperService") {
1139                                actWallpaperService = true;
1140                            }
1141                        }
1142                        if (action == "android.intent.action.SEARCH") {
1143                            isSearchable = true;
1144                        }
1145                    }
1146
1147                    if (tag == "category") {
1148                        String8 category = getAttribute(tree, NAME_ATTR, &error);
1149                        if (error != "") {
1150                            fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string());
1151                            goto bail;
1152                        }
1153                        if (withinActivity) {
1154                            if (category == "android.intent.category.LAUNCHER") {
1155                                isLauncherActivity = true;
1156                            }
1157                        }
1158                    }
1159                }
1160            }
1161
1162            // Pre-1.6 implicitly granted permission compatibility logic
1163            if (targetSdk < 4) {
1164                if (!hasWriteExternalStoragePermission) {
1165                    printf("uses-permission:'android.permission.WRITE_EXTERNAL_STORAGE'\n");
1166                }
1167                if (!hasReadPhoneStatePermission) {
1168                    printf("uses-permission:'android.permission.READ_PHONE_STATE'\n");
1169                }
1170            }
1171
1172            /* The following blocks handle printing "inferred" uses-features, based
1173             * on whether related features or permissions are used by the app.
1174             * Note that the various spec*Feature variables denote whether the
1175             * relevant tag was *present* in the AndroidManfest, not that it was
1176             * present and set to true.
1177             */
1178            // Camera-related back-compatibility logic
1179            if (!specCameraFeature) {
1180                if (reqCameraFlashFeature || reqCameraAutofocusFeature) {
1181                    // if app requested a sub-feature (autofocus or flash) and didn't
1182                    // request the base camera feature, we infer that it meant to
1183                    printf("uses-feature:'android.hardware.camera'\n");
1184                } else if (hasCameraPermission) {
1185                    // if app wants to use camera but didn't request the feature, we infer
1186                    // that it meant to, and further that it wants autofocus
1187                    // (which was the 1.0 - 1.5 behavior)
1188                    printf("uses-feature:'android.hardware.camera'\n");
1189                    if (!specCameraAutofocusFeature) {
1190                        printf("uses-feature:'android.hardware.camera.autofocus'\n");
1191                    }
1192                }
1193            }
1194
1195            // Location-related back-compatibility logic
1196            if (!specLocationFeature &&
1197                (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission ||
1198                 hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) {
1199                // if app either takes a location-related permission or requests one of the
1200                // sub-features, we infer that it also meant to request the base location feature
1201                printf("uses-feature:'android.hardware.location'\n");
1202            }
1203            if (!specGpsFeature && hasGpsPermission) {
1204                // if app takes GPS (FINE location) perm but does not request the GPS
1205                // feature, we infer that it meant to
1206                printf("uses-feature:'android.hardware.location.gps'\n");
1207            }
1208            if (!specNetworkLocFeature && hasCoarseLocPermission) {
1209                // if app takes Network location (COARSE location) perm but does not request the
1210                // network location feature, we infer that it meant to
1211                printf("uses-feature:'android.hardware.location.network'\n");
1212            }
1213
1214            // Bluetooth-related compatibility logic
1215            if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
1216                // if app takes a Bluetooth permission but does not request the Bluetooth
1217                // feature, we infer that it meant to
1218                printf("uses-feature:'android.hardware.bluetooth'\n");
1219            }
1220
1221            // Microphone-related compatibility logic
1222            if (!specMicrophoneFeature && hasRecordAudioPermission) {
1223                // if app takes the record-audio permission but does not request the microphone
1224                // feature, we infer that it meant to
1225                printf("uses-feature:'android.hardware.microphone'\n");
1226            }
1227
1228            // WiFi-related compatibility logic
1229            if (!specWiFiFeature && hasWiFiPermission) {
1230                // if app takes one of the WiFi permissions but does not request the WiFi
1231                // feature, we infer that it meant to
1232                printf("uses-feature:'android.hardware.wifi'\n");
1233            }
1234
1235            // Telephony-related compatibility logic
1236            if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) {
1237                // if app takes one of the telephony permissions or requests a sub-feature but
1238                // does not request the base telephony feature, we infer that it meant to
1239                printf("uses-feature:'android.hardware.telephony'\n");
1240            }
1241
1242            // Touchscreen-related back-compatibility logic
1243            if (!specTouchscreenFeature) { // not a typo!
1244                // all apps are presumed to require a touchscreen, unless they explicitly say
1245                // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
1246                // Note that specTouchscreenFeature is true if the tag is present, regardless
1247                // of whether its value is true or false, so this is safe
1248                printf("uses-feature:'android.hardware.touchscreen'\n");
1249            }
1250            if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
1251                // if app takes one of the telephony permissions or requests a sub-feature but
1252                // does not request the base telephony feature, we infer that it meant to
1253                printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
1254            }
1255
1256            // Landscape/portrait-related compatibility logic
1257            if (!specScreenLandscapeFeature && !specScreenPortraitFeature) {
1258                // If the app has specified any activities in its manifest
1259                // that request a specific orientation, then assume that
1260                // orientation is required.
1261                if (reqScreenLandscapeFeature) {
1262                    printf("uses-feature:'android.hardware.screen.landscape'\n");
1263                }
1264                if (reqScreenPortraitFeature) {
1265                    printf("uses-feature:'android.hardware.screen.portrait'\n");
1266                }
1267            }
1268
1269            if (hasMainActivity) {
1270                printf("main\n");
1271            }
1272            if (hasWidgetReceivers) {
1273                printf("app-widget\n");
1274            }
1275            if (hasImeService) {
1276                printf("ime\n");
1277            }
1278            if (hasWallpaperService) {
1279                printf("wallpaper\n");
1280            }
1281            if (hasOtherActivities) {
1282                printf("other-activities\n");
1283            }
1284            if (isSearchable) {
1285                printf("search\n");
1286            }
1287            if (hasOtherReceivers) {
1288                printf("other-receivers\n");
1289            }
1290            if (hasOtherServices) {
1291                printf("other-services\n");
1292            }
1293
1294            // For modern apps, if screen size buckets haven't been specified
1295            // but the new width ranges have, then infer the buckets from them.
1296            if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1297                    && requiresSmallestWidthDp > 0) {
1298                int compatWidth = compatibleWidthLimitDp;
1299                if (compatWidth <= 0) compatWidth = requiresSmallestWidthDp;
1300                if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1301                    smallScreen = -1;
1302                } else {
1303                    smallScreen = 0;
1304                }
1305                if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1306                    normalScreen = -1;
1307                } else {
1308                    normalScreen = 0;
1309                }
1310                if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1311                    largeScreen = -1;
1312                } else {
1313                    largeScreen = 0;
1314                }
1315                if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1316                    xlargeScreen = -1;
1317                } else {
1318                    xlargeScreen = 0;
1319                }
1320            }
1321
1322            // Determine default values for any unspecified screen sizes,
1323            // based on the target SDK of the package.  As of 4 (donut)
1324            // the screen size support was introduced, so all default to
1325            // enabled.
1326            if (smallScreen > 0) {
1327                smallScreen = targetSdk >= 4 ? -1 : 0;
1328            }
1329            if (normalScreen > 0) {
1330                normalScreen = -1;
1331            }
1332            if (largeScreen > 0) {
1333                largeScreen = targetSdk >= 4 ? -1 : 0;
1334            }
1335            if (xlargeScreen > 0) {
1336                // Introduced in Gingerbread.
1337                xlargeScreen = targetSdk >= 9 ? -1 : 0;
1338            }
1339            if (anyDensity > 0) {
1340                anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1341                        || compatibleWidthLimitDp > 0) ? -1 : 0;
1342            }
1343            printf("supports-screens:");
1344            if (smallScreen != 0) printf(" 'small'");
1345            if (normalScreen != 0) printf(" 'normal'");
1346            if (largeScreen != 0) printf(" 'large'");
1347            if (xlargeScreen != 0) printf(" 'xlarge'");
1348            printf("\n");
1349            printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
1350            if (requiresSmallestWidthDp > 0) {
1351                printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1352            }
1353            if (compatibleWidthLimitDp > 0) {
1354                printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1355            }
1356            if (largestWidthLimitDp > 0) {
1357                printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1358            }
1359
1360            printf("locales:");
1361            const size_t NL = locales.size();
1362            for (size_t i=0; i<NL; i++) {
1363                const char* localeStr =  locales[i].string();
1364                if (localeStr == NULL || strlen(localeStr) == 0) {
1365                    localeStr = "--_--";
1366                }
1367                printf(" '%s'", localeStr);
1368            }
1369            printf("\n");
1370
1371            printf("densities:");
1372            const size_t ND = densities.size();
1373            for (size_t i=0; i<ND; i++) {
1374                printf(" '%d'", densities[i]);
1375            }
1376            printf("\n");
1377
1378            AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1379            if (dir != NULL) {
1380                if (dir->getFileCount() > 0) {
1381                    printf("native-code:");
1382                    for (size_t i=0; i<dir->getFileCount(); i++) {
1383                        printf(" '%s'", dir->getFileName(i).string());
1384                    }
1385                    printf("\n");
1386                }
1387                delete dir;
1388            }
1389        } else if (strcmp("badger", option) == 0) {
1390            printf("%s", CONSOLE_DATA);
1391        } else if (strcmp("configurations", option) == 0) {
1392            Vector<ResTable_config> configs;
1393            res.getConfigurations(&configs);
1394            const size_t N = configs.size();
1395            for (size_t i=0; i<N; i++) {
1396                printf("%s\n", configs[i].toString().string());
1397            }
1398        } else {
1399            fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
1400            goto bail;
1401        }
1402    }
1403
1404    result = NO_ERROR;
1405
1406bail:
1407    if (asset) {
1408        delete asset;
1409    }
1410    return (result != NO_ERROR);
1411}
1412
1413
1414/*
1415 * Handle the "add" command, which wants to add files to a new or
1416 * pre-existing archive.
1417 */
1418int doAdd(Bundle* bundle)
1419{
1420    ZipFile* zip = NULL;
1421    status_t result = UNKNOWN_ERROR;
1422    const char* zipFileName;
1423
1424    if (bundle->getUpdate()) {
1425        /* avoid confusion */
1426        fprintf(stderr, "ERROR: can't use '-u' with add\n");
1427        goto bail;
1428    }
1429
1430    if (bundle->getFileSpecCount() < 1) {
1431        fprintf(stderr, "ERROR: must specify zip file name\n");
1432        goto bail;
1433    }
1434    zipFileName = bundle->getFileSpecEntry(0);
1435
1436    if (bundle->getFileSpecCount() < 2) {
1437        fprintf(stderr, "NOTE: nothing to do\n");
1438        goto bail;
1439    }
1440
1441    zip = openReadWrite(zipFileName, true);
1442    if (zip == NULL) {
1443        fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
1444        goto bail;
1445    }
1446
1447    for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1448        const char* fileName = bundle->getFileSpecEntry(i);
1449
1450        if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
1451            printf(" '%s'... (from gzip)\n", fileName);
1452            result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
1453        } else {
1454            if (bundle->getJunkPath()) {
1455                String8 storageName = String8(fileName).getPathLeaf();
1456                printf(" '%s' as '%s'...\n", fileName, storageName.string());
1457                result = zip->add(fileName, storageName.string(),
1458                                  bundle->getCompressionMethod(), NULL);
1459            } else {
1460                printf(" '%s'...\n", fileName);
1461                result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
1462            }
1463        }
1464        if (result != NO_ERROR) {
1465            fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
1466            if (result == NAME_NOT_FOUND)
1467                fprintf(stderr, ": file not found\n");
1468            else if (result == ALREADY_EXISTS)
1469                fprintf(stderr, ": already exists in archive\n");
1470            else
1471                fprintf(stderr, "\n");
1472            goto bail;
1473        }
1474    }
1475
1476    result = NO_ERROR;
1477
1478bail:
1479    delete zip;
1480    return (result != NO_ERROR);
1481}
1482
1483
1484/*
1485 * Delete files from an existing archive.
1486 */
1487int doRemove(Bundle* bundle)
1488{
1489    ZipFile* zip = NULL;
1490    status_t result = UNKNOWN_ERROR;
1491    const char* zipFileName;
1492
1493    if (bundle->getFileSpecCount() < 1) {
1494        fprintf(stderr, "ERROR: must specify zip file name\n");
1495        goto bail;
1496    }
1497    zipFileName = bundle->getFileSpecEntry(0);
1498
1499    if (bundle->getFileSpecCount() < 2) {
1500        fprintf(stderr, "NOTE: nothing to do\n");
1501        goto bail;
1502    }
1503
1504    zip = openReadWrite(zipFileName, false);
1505    if (zip == NULL) {
1506        fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
1507            zipFileName);
1508        goto bail;
1509    }
1510
1511    for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1512        const char* fileName = bundle->getFileSpecEntry(i);
1513        ZipEntry* entry;
1514
1515        entry = zip->getEntryByName(fileName);
1516        if (entry == NULL) {
1517            printf(" '%s' NOT FOUND\n", fileName);
1518            continue;
1519        }
1520
1521        result = zip->remove(entry);
1522
1523        if (result != NO_ERROR) {
1524            fprintf(stderr, "Unable to delete '%s' from '%s'\n",
1525                bundle->getFileSpecEntry(i), zipFileName);
1526            goto bail;
1527        }
1528    }
1529
1530    /* update the archive */
1531    zip->flush();
1532
1533bail:
1534    delete zip;
1535    return (result != NO_ERROR);
1536}
1537
1538
1539/*
1540 * Package up an asset directory and associated application files.
1541 */
1542int doPackage(Bundle* bundle)
1543{
1544    const char* outputAPKFile;
1545    int retVal = 1;
1546    status_t err;
1547    sp<AaptAssets> assets;
1548    int N;
1549    FILE* fp;
1550    String8 dependencyFile;
1551
1552    // -c zz_ZZ means do pseudolocalization
1553    ResourceFilter filter;
1554    err = filter.parse(bundle->getConfigurations());
1555    if (err != NO_ERROR) {
1556        goto bail;
1557    }
1558    if (filter.containsPseudo()) {
1559        bundle->setPseudolocalize(true);
1560    }
1561
1562    N = bundle->getFileSpecCount();
1563    if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
1564            && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDir() == NULL) {
1565        fprintf(stderr, "ERROR: no input files\n");
1566        goto bail;
1567    }
1568
1569    outputAPKFile = bundle->getOutputAPKFile();
1570
1571    // Make sure the filenames provided exist and are of the appropriate type.
1572    if (outputAPKFile) {
1573        FileType type;
1574        type = getFileType(outputAPKFile);
1575        if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
1576            fprintf(stderr,
1577                "ERROR: output file '%s' exists but is not regular file\n",
1578                outputAPKFile);
1579            goto bail;
1580        }
1581    }
1582
1583    // Load the assets.
1584    assets = new AaptAssets();
1585
1586    // Set up the resource gathering in assets if we're going to generate
1587    // dependency files. Every time we encounter a resource while slurping
1588    // the tree, we'll add it to these stores so we have full resource paths
1589    // to write to a dependency file.
1590    if (bundle->getGenDependencies()) {
1591        sp<FilePathStore> resPathStore = new FilePathStore;
1592        assets->setFullResPaths(resPathStore);
1593        sp<FilePathStore> assetPathStore = new FilePathStore;
1594        assets->setFullAssetPaths(assetPathStore);
1595    }
1596
1597    err = assets->slurpFromArgs(bundle);
1598    if (err < 0) {
1599        goto bail;
1600    }
1601
1602    if (bundle->getVerbose()) {
1603        assets->print(String8());
1604    }
1605
1606    // If they asked for any fileAs that need to be compiled, do so.
1607    if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
1608        err = buildResources(bundle, assets);
1609        if (err != 0) {
1610            goto bail;
1611        }
1612    }
1613
1614    // At this point we've read everything and processed everything.  From here
1615    // on out it's just writing output files.
1616    if (SourcePos::hasErrors()) {
1617        goto bail;
1618    }
1619
1620    // Update symbols with information about which ones are needed as Java symbols.
1621    assets->applyJavaSymbols();
1622    if (SourcePos::hasErrors()) {
1623        goto bail;
1624    }
1625
1626    // If we've been asked to generate a dependency file, do that here
1627    if (bundle->getGenDependencies()) {
1628        // If this is the packaging step, generate the dependency file next to
1629        // the output apk (e.g. bin/resources.ap_.d)
1630        if (outputAPKFile) {
1631            dependencyFile = String8(outputAPKFile);
1632            // Add the .d extension to the dependency file.
1633            dependencyFile.append(".d");
1634        } else {
1635            // Else if this is the R.java dependency generation step,
1636            // generate the dependency file in the R.java package subdirectory
1637            // e.g. gen/com/foo/app/R.java.d
1638            dependencyFile = String8(bundle->getRClassDir());
1639            dependencyFile.appendPath("R.java.d");
1640        }
1641        // Make sure we have a clean dependency file to start with
1642        fp = fopen(dependencyFile, "w");
1643        fclose(fp);
1644    }
1645
1646    // Write out R.java constants
1647    if (!assets->havePrivateSymbols()) {
1648        if (bundle->getCustomPackage() == NULL) {
1649            // Write the R.java file into the appropriate class directory
1650            // e.g. gen/com/foo/app/R.java
1651            err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
1652            // If we have library files, we're going to write our R.java file into
1653            // the appropriate class directory for those libraries as well.
1654            // e.g. gen/com/foo/app/lib/R.java
1655            if (bundle->getExtraPackages() != NULL) {
1656                // Split on colon
1657                String8 libs(bundle->getExtraPackages());
1658                char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
1659                while (packageString != NULL) {
1660                    // Write the R.java file out with the correct package name
1661                    err = writeResourceSymbols(bundle, assets, String8(packageString), true);
1662                    packageString = strtok(NULL, ":");
1663                }
1664                libs.unlockBuffer();
1665            }
1666        } else {
1667            const String8 customPkg(bundle->getCustomPackage());
1668            err = writeResourceSymbols(bundle, assets, customPkg, true);
1669        }
1670        if (err < 0) {
1671            goto bail;
1672        }
1673    } else {
1674        err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
1675        if (err < 0) {
1676            goto bail;
1677        }
1678        err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
1679        if (err < 0) {
1680            goto bail;
1681        }
1682    }
1683
1684    // Write out the ProGuard file
1685    err = writeProguardFile(bundle, assets);
1686    if (err < 0) {
1687        goto bail;
1688    }
1689
1690    // Write the apk
1691    if (outputAPKFile) {
1692        err = writeAPK(bundle, assets, String8(outputAPKFile));
1693        if (err != NO_ERROR) {
1694            fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
1695            goto bail;
1696        }
1697    }
1698
1699    // If we've been asked to generate a dependency file, we need to finish up here.
1700    // the writeResourceSymbols and writeAPK functions have already written the target
1701    // half of the dependency file, now we need to write the prerequisites. (files that
1702    // the R.java file or .ap_ file depend on)
1703    if (bundle->getGenDependencies()) {
1704        // Now that writeResourceSymbols or writeAPK has taken care of writing
1705        // the targets to our dependency file, we'll write the prereqs
1706        fp = fopen(dependencyFile, "a+");
1707        fprintf(fp, " : ");
1708        bool includeRaw = (outputAPKFile != NULL);
1709        err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
1710        // Also manually add the AndroidManifeset since it's not under res/ or assets/
1711        // and therefore was not added to our pathstores during slurping
1712        fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
1713        fclose(fp);
1714    }
1715
1716    retVal = 0;
1717bail:
1718    if (SourcePos::hasErrors()) {
1719        SourcePos::printErrors(stderr);
1720    }
1721    return retVal;
1722}
1723
1724/*
1725 * Do PNG Crunching
1726 * PRECONDITIONS
1727 *  -S flag points to a source directory containing drawable* folders
1728 *  -C flag points to destination directory. The folder structure in the
1729 *     source directory will be mirrored to the destination (cache) directory
1730 *
1731 * POSTCONDITIONS
1732 *  Destination directory will be updated to match the PNG files in
1733 *  the source directory.
1734 */
1735int doCrunch(Bundle* bundle)
1736{
1737    fprintf(stdout, "Crunching PNG Files in ");
1738    fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
1739    fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
1740
1741    updatePreProcessedCache(bundle);
1742
1743    return NO_ERROR;
1744}
1745
1746char CONSOLE_DATA[2925] = {
1747    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1748    32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1749    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1750    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1751    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
1752    86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
1753    62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1754    32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1755    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
1756    81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
1757    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1758    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1759    32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
1760    59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1761    32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1762    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
1763    59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
1764    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1765    10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1766    32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
1767    58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
1768    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
1769    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
1770    47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
1771    121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1772    32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1773    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
1774    81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
1775    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
1776    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1777    32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
1778    59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1779    32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1780    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
1781    59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
1782    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1783    32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1784    32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
1785    70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
1786    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
1787    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1788    32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
1789    81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1790    32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1791    32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
1792    81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
1793    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
1794    32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
1795    59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
1796    60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1797    32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
1798    61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
1799    61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
1800    46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1801    32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
1802    59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
1803    109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
1804    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
1805    67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
1806    59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
1807    61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
1808    32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
1809    59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
1810    73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
1811    59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
1812    32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
1813    59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
1814    46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
1815    97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
1816    46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
1817    119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
1818    59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
1819    32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1820    32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
1821    119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
1822    59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
1823    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1824    32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
1825    81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
1826    87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1827    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
1828    81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
1829    45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
1830    32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1831    32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
1832    81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
1833    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1834    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
1835    59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
1836    59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1837    32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1838    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
1839    81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
1840    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1841    10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1842    32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
1843    81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1844    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
1845    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
1846    81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
1847    32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1848    32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1849    32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
1850    81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
1851    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
1852    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1853    58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
1854    61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1855    32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1856    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
1857    81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
1858    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1859    32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1860    32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
1861    81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1862    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
1863    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
1864    59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
1865    59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1866    32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1867    32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
1868    58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
1869    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
1870    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
1871    61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
1872    59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
1873    32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
1874    32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
1875    32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
1876    61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1877    32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1878    32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
1879    59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
1880    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
1881    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
1882    59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
1883    59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1884    32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1885    32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
1886    32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
1887    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
1888    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1889    46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
1890    46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1891    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1892    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
1893    59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
1894    59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1895    32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1896    32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
1897    32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
1898    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1899    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
1900    59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
1901    59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1902    32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1903    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
1904    32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
1905    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1906    10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1907    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1908    32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1909    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
1910  };
1911