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