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