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