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