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