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