Command.cpp revision 25e9d55e964c180ec6e57ba1d977d6c2e1115f5a
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    } else if (res.getError() != NO_ERROR) {
638        fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
639        goto bail;
640    }
641
642    if (strcmp("resources", option) == 0) {
643#ifndef HAVE_ANDROID_OS
644        res.print(bundle->getValues());
645#endif
646
647    } else if (strcmp("strings", option) == 0) {
648        const ResStringPool* pool = res.getTableStringBlock(0);
649        printStringPool(pool);
650
651    } else if (strcmp("xmltree", option) == 0) {
652        if (bundle->getFileSpecCount() < 3) {
653            fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
654            goto bail;
655        }
656
657        for (int i=2; i<bundle->getFileSpecCount(); i++) {
658            const char* resname = bundle->getFileSpecEntry(i);
659            ResXMLTree tree;
660            asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
661            if (asset == NULL) {
662                fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
663                goto bail;
664            }
665
666            if (tree.setTo(asset->getBuffer(true),
667                           asset->getLength()) != NO_ERROR) {
668                fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
669                goto bail;
670            }
671            tree.restart();
672            printXMLBlock(&tree);
673            tree.uninit();
674            delete asset;
675            asset = NULL;
676        }
677
678    } else if (strcmp("xmlstrings", option) == 0) {
679        if (bundle->getFileSpecCount() < 3) {
680            fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
681            goto bail;
682        }
683
684        for (int i=2; i<bundle->getFileSpecCount(); i++) {
685            const char* resname = bundle->getFileSpecEntry(i);
686            ResXMLTree tree;
687            asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
688            if (asset == NULL) {
689                fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
690                goto bail;
691            }
692
693            if (tree.setTo(asset->getBuffer(true),
694                           asset->getLength()) != NO_ERROR) {
695                fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
696                goto bail;
697            }
698            printStringPool(&tree.getStrings());
699            delete asset;
700            asset = NULL;
701        }
702
703    } else {
704        ResXMLTree tree;
705        asset = assets.openNonAsset("AndroidManifest.xml",
706                                            Asset::ACCESS_BUFFER);
707        if (asset == NULL) {
708            fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
709            goto bail;
710        }
711
712        if (tree.setTo(asset->getBuffer(true),
713                       asset->getLength()) != NO_ERROR) {
714            fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
715            goto bail;
716        }
717        tree.restart();
718
719        if (strcmp("permissions", option) == 0) {
720            size_t len;
721            ResXMLTree::event_code_t code;
722            int depth = 0;
723            while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
724                if (code == ResXMLTree::END_TAG) {
725                    depth--;
726                    continue;
727                }
728                if (code != ResXMLTree::START_TAG) {
729                    continue;
730                }
731                depth++;
732                const char16_t* ctag16 = tree.getElementName(&len);
733                if (ctag16 == NULL) {
734                    fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
735                    goto bail;
736                }
737                String8 tag(ctag16);
738                //printf("Depth %d tag %s\n", depth, tag.string());
739                if (depth == 1) {
740                    if (tag != "manifest") {
741                        fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
742                        goto bail;
743                    }
744                    String8 pkg = getAttribute(tree, NULL, "package", NULL);
745                    printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
746                } else if (depth == 2 && tag == "permission") {
747                    String8 error;
748                    String8 name = getAttribute(tree, NAME_ATTR, &error);
749                    if (error != "") {
750                        fprintf(stderr, "ERROR: %s\n", error.string());
751                        goto bail;
752                    }
753                    printf("permission: %s\n",
754                            ResTable::normalizeForOutput(name.string()).string());
755                } else if (depth == 2 && tag == "uses-permission") {
756                    String8 error;
757                    String8 name = getAttribute(tree, NAME_ATTR, &error);
758                    if (error != "") {
759                        fprintf(stderr, "ERROR: %s\n", error.string());
760                        goto bail;
761                    }
762                    printUsesPermission(name,
763                            getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1) == 0,
764                            getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1));
765                }
766            }
767        } else if (strcmp("badging", option) == 0) {
768            Vector<String8> locales;
769            res.getLocales(&locales);
770
771            Vector<ResTable_config> configs;
772            res.getConfigurations(&configs);
773            SortedVector<int> densities;
774            const size_t NC = configs.size();
775            for (size_t i=0; i<NC; i++) {
776                int dens = configs[i].density;
777                if (dens == 0) {
778                    dens = 160;
779                }
780                densities.add(dens);
781            }
782
783            size_t len;
784            ResXMLTree::event_code_t code;
785            int depth = 0;
786            String8 error;
787            bool withinActivity = false;
788            bool isMainActivity = false;
789            bool isLauncherActivity = false;
790            bool isLeanbackLauncherActivity = false;
791            bool isSearchable = false;
792            bool withinApplication = false;
793            bool withinSupportsInput = false;
794            bool withinReceiver = false;
795            bool withinService = false;
796            bool withinIntentFilter = false;
797            bool hasMainActivity = false;
798            bool hasOtherActivities = false;
799            bool hasOtherReceivers = false;
800            bool hasOtherServices = false;
801            bool hasWallpaperService = false;
802            bool hasImeService = false;
803            bool hasAccessibilityService = false;
804            bool hasPrintService = false;
805            bool hasWidgetReceivers = false;
806            bool hasDeviceAdminReceiver = false;
807            bool hasIntentFilter = false;
808            bool hasPaymentService = false;
809            bool actMainActivity = false;
810            bool actWidgetReceivers = false;
811            bool actDeviceAdminEnabled = false;
812            bool actImeService = false;
813            bool actWallpaperService = false;
814            bool actAccessibilityService = false;
815            bool actPrintService = false;
816            bool actHostApduService = false;
817            bool actOffHostApduService = false;
818            bool hasMetaHostPaymentCategory = false;
819            bool hasMetaOffHostPaymentCategory = false;
820
821            // These permissions are required by services implementing services
822            // the system binds to (IME, Accessibility, PrintServices, etc.)
823            bool hasBindDeviceAdminPermission = false;
824            bool hasBindInputMethodPermission = false;
825            bool hasBindAccessibilityServicePermission = false;
826            bool hasBindPrintServicePermission = false;
827            bool hasBindNfcServicePermission = false;
828
829            // These two implement the implicit permissions that are granted
830            // to pre-1.6 applications.
831            bool hasWriteExternalStoragePermission = false;
832            bool hasReadPhoneStatePermission = false;
833
834            // If an app requests write storage, they will also get read storage.
835            bool hasReadExternalStoragePermission = false;
836
837            // Implement transition to read and write call log.
838            bool hasReadContactsPermission = false;
839            bool hasWriteContactsPermission = false;
840            bool hasReadCallLogPermission = false;
841            bool hasWriteCallLogPermission = false;
842
843            // This next group of variables is used to implement a group of
844            // backward-compatibility heuristics necessitated by the addition of
845            // some new uses-feature constants in 2.1 and 2.2. In most cases, the
846            // heuristic is "if an app requests a permission but doesn't explicitly
847            // request the corresponding <uses-feature>, presume it's there anyway".
848            bool specCameraFeature = false; // camera-related
849            bool specCameraAutofocusFeature = false;
850            bool reqCameraAutofocusFeature = false;
851            bool reqCameraFlashFeature = false;
852            bool hasCameraPermission = false;
853            bool specLocationFeature = false; // location-related
854            bool specNetworkLocFeature = false;
855            bool reqNetworkLocFeature = false;
856            bool specGpsFeature = false;
857            bool reqGpsFeature = false;
858            bool hasMockLocPermission = false;
859            bool hasCoarseLocPermission = false;
860            bool hasGpsPermission = false;
861            bool hasGeneralLocPermission = false;
862            bool specBluetoothFeature = false; // Bluetooth API-related
863            bool hasBluetoothPermission = false;
864            bool specMicrophoneFeature = false; // microphone-related
865            bool hasRecordAudioPermission = false;
866            bool specWiFiFeature = false;
867            bool hasWiFiPermission = false;
868            bool specTelephonyFeature = false; // telephony-related
869            bool reqTelephonySubFeature = false;
870            bool hasTelephonyPermission = false;
871            bool specTouchscreenFeature = false; // touchscreen-related
872            bool specMultitouchFeature = false;
873            bool reqDistinctMultitouchFeature = false;
874            bool specScreenPortraitFeature = false;
875            bool specScreenLandscapeFeature = false;
876            bool reqScreenPortraitFeature = false;
877            bool reqScreenLandscapeFeature = false;
878            // 2.2 also added some other features that apps can request, but that
879            // have no corresponding permission, so we cannot implement any
880            // back-compatibility heuristic for them. The below are thus unnecessary
881            // (but are retained here for documentary purposes.)
882            //bool specCompassFeature = false;
883            //bool specAccelerometerFeature = false;
884            //bool specProximityFeature = false;
885            //bool specAmbientLightFeature = false;
886            //bool specLiveWallpaperFeature = false;
887
888            int targetSdk = 0;
889            int smallScreen = 1;
890            int normalScreen = 1;
891            int largeScreen = 1;
892            int xlargeScreen = 1;
893            int anyDensity = 1;
894            int requiresSmallestWidthDp = 0;
895            int compatibleWidthLimitDp = 0;
896            int largestWidthLimitDp = 0;
897            String8 pkg;
898            String8 activityName;
899            String8 activityLabel;
900            String8 activityIcon;
901            String8 activityBanner;
902            String8 receiverName;
903            String8 serviceName;
904            Vector<String8> supportedInput;
905            while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
906                if (code == ResXMLTree::END_TAG) {
907                    depth--;
908                    if (depth < 2) {
909                        if (withinSupportsInput && !supportedInput.isEmpty()) {
910                            printf("supports-input: '");
911                            const size_t N = supportedInput.size();
912                            for (size_t i=0; i<N; i++) {
913                                printf("%s", ResTable::normalizeForOutput(
914                                        supportedInput[i].string()).string());
915                                if (i != N - 1) {
916                                    printf("' '");
917                                } else {
918                                    printf("'\n");
919                                }
920                            }
921                            supportedInput.clear();
922                        }
923                        withinApplication = false;
924                        withinSupportsInput = false;
925                    } else if (depth < 3) {
926                        if (withinActivity && isMainActivity) {
927                            String8 aName(getComponentName(pkg, activityName));
928                            if (isLauncherActivity) {
929                                printf("launchable-activity:");
930                                if (aName.length() > 0) {
931                                    printf(" name='%s' ",
932                                            ResTable::normalizeForOutput(aName.string()).string());
933                                }
934                                printf(" label='%s' icon='%s'\n",
935                                        ResTable::normalizeForOutput(activityLabel.string()).string(),
936                                        ResTable::normalizeForOutput(activityIcon.string()).string());
937                            }
938                            if (isLeanbackLauncherActivity) {
939                                printf("leanback-launchable-activity:");
940                                if (aName.length() > 0) {
941                                    printf(" name='%s' ",
942                                            ResTable::normalizeForOutput(aName.string()).string());
943                                }
944                                printf(" label='%s' icon='%s' banner='%s'\n",
945                                        ResTable::normalizeForOutput(activityLabel.string()).string(),
946                                        ResTable::normalizeForOutput(activityIcon.string()).string(),
947                                        ResTable::normalizeForOutput(activityBanner.string()).string());
948                            }
949                        }
950                        if (!hasIntentFilter) {
951                            hasOtherActivities |= withinActivity;
952                            hasOtherReceivers |= withinReceiver;
953                            hasOtherServices |= withinService;
954                        } else {
955                            if (withinService) {
956                                hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
957                                        hasBindNfcServicePermission);
958                                hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
959                                        hasBindNfcServicePermission);
960                            }
961                        }
962                        withinActivity = false;
963                        withinService = false;
964                        withinReceiver = false;
965                        hasIntentFilter = false;
966                        isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
967                    } else if (depth < 4) {
968                        if (withinIntentFilter) {
969                            if (withinActivity) {
970                                hasMainActivity |= actMainActivity;
971                                hasOtherActivities |= !actMainActivity;
972                            } else if (withinReceiver) {
973                                hasWidgetReceivers |= actWidgetReceivers;
974                                hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
975                                        hasBindDeviceAdminPermission);
976                                hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled);
977                            } else if (withinService) {
978                                hasImeService |= actImeService;
979                                hasWallpaperService |= actWallpaperService;
980                                hasAccessibilityService |= (actAccessibilityService &&
981                                        hasBindAccessibilityServicePermission);
982                                hasPrintService |= (actPrintService && hasBindPrintServicePermission);
983                                hasOtherServices |= (!actImeService && !actWallpaperService &&
984                                        !actAccessibilityService && !actPrintService &&
985                                        !actHostApduService && !actOffHostApduService);
986                            }
987                        }
988                        withinIntentFilter = false;
989                    }
990                    continue;
991                }
992                if (code != ResXMLTree::START_TAG) {
993                    continue;
994                }
995                depth++;
996
997                const char16_t* ctag16 = tree.getElementName(&len);
998                if (ctag16 == NULL) {
999                    fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
1000                    goto bail;
1001                }
1002                String8 tag(ctag16);
1003                //printf("Depth %d,  %s\n", depth, tag.string());
1004                if (depth == 1) {
1005                    if (tag != "manifest") {
1006                        fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
1007                        goto bail;
1008                    }
1009                    pkg = getAttribute(tree, NULL, "package", NULL);
1010                    printf("package: name='%s' ",
1011                            ResTable::normalizeForOutput(pkg.string()).string());
1012                    int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
1013                    if (error != "") {
1014                        fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
1015                        goto bail;
1016                    }
1017                    if (versionCode > 0) {
1018                        printf("versionCode='%d' ", versionCode);
1019                    } else {
1020                        printf("versionCode='' ");
1021                    }
1022                    String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
1023                    if (error != "") {
1024                        fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
1025                        goto bail;
1026                    }
1027                    printf("versionName='%s'\n",
1028                            ResTable::normalizeForOutput(versionName.string()).string());
1029                } else if (depth == 2) {
1030                    withinApplication = false;
1031                    if (tag == "application") {
1032                        withinApplication = true;
1033
1034                        String8 label;
1035                        const size_t NL = locales.size();
1036                        for (size_t i=0; i<NL; i++) {
1037                            const char* localeStr =  locales[i].string();
1038                            assets.setLocale(localeStr != NULL ? localeStr : "");
1039                            String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1040                            if (llabel != "") {
1041                                if (localeStr == NULL || strlen(localeStr) == 0) {
1042                                    label = llabel;
1043                                    printf("application-label:'%s'\n",
1044                                            ResTable::normalizeForOutput(llabel.string()).string());
1045                                } else {
1046                                    if (label == "") {
1047                                        label = llabel;
1048                                    }
1049                                    printf("application-label-%s:'%s'\n", localeStr,
1050                                           ResTable::normalizeForOutput(llabel.string()).string());
1051                                }
1052                            }
1053                        }
1054
1055                        ResTable_config tmpConfig = config;
1056                        const size_t ND = densities.size();
1057                        for (size_t i=0; i<ND; i++) {
1058                            tmpConfig.density = densities[i];
1059                            assets.setConfiguration(tmpConfig);
1060                            String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1061                            if (icon != "") {
1062                                printf("application-icon-%d:'%s'\n", densities[i],
1063                                        ResTable::normalizeForOutput(icon.string()).string());
1064                            }
1065                        }
1066                        assets.setConfiguration(config);
1067
1068                        String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1069                        if (error != "") {
1070                            fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
1071                            goto bail;
1072                        }
1073                        int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
1074                        if (error != "") {
1075                            fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
1076                            goto bail;
1077                        }
1078                        printf("application: label='%s' ",
1079                                ResTable::normalizeForOutput(label.string()).string());
1080                        printf("icon='%s'\n", ResTable::normalizeForOutput(icon.string()).string());
1081                        if (testOnly != 0) {
1082                            printf("testOnly='%d'\n", testOnly);
1083                        }
1084
1085                        int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0);
1086                        if (error != "") {
1087                            fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string());
1088                            goto bail;
1089                        }
1090                        if (debuggable != 0) {
1091                            printf("application-debuggable\n");
1092                        }
1093                    } else if (tag == "uses-sdk") {
1094                        int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
1095                        if (error != "") {
1096                            error = "";
1097                            String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
1098                            if (error != "") {
1099                                fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
1100                                        error.string());
1101                                goto bail;
1102                            }
1103                            if (name == "Donut") targetSdk = 4;
1104                            printf("sdkVersion:'%s'\n",
1105                                    ResTable::normalizeForOutput(name.string()).string());
1106                        } else if (code != -1) {
1107                            targetSdk = code;
1108                            printf("sdkVersion:'%d'\n", code);
1109                        }
1110                        code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
1111                        if (code != -1) {
1112                            printf("maxSdkVersion:'%d'\n", code);
1113                        }
1114                        code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
1115                        if (error != "") {
1116                            error = "";
1117                            String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
1118                            if (error != "") {
1119                                fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
1120                                        error.string());
1121                                goto bail;
1122                            }
1123                            if (name == "Donut" && targetSdk < 4) targetSdk = 4;
1124                            printf("targetSdkVersion:'%s'\n",
1125                                    ResTable::normalizeForOutput(name.string()).string());
1126                        } else if (code != -1) {
1127                            if (targetSdk < code) {
1128                                targetSdk = code;
1129                            }
1130                            printf("targetSdkVersion:'%d'\n", code);
1131                        }
1132                    } else if (tag == "uses-configuration") {
1133                        int32_t reqTouchScreen = getIntegerAttribute(tree,
1134                                REQ_TOUCH_SCREEN_ATTR, NULL, 0);
1135                        int32_t reqKeyboardType = getIntegerAttribute(tree,
1136                                REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
1137                        int32_t reqHardKeyboard = getIntegerAttribute(tree,
1138                                REQ_HARD_KEYBOARD_ATTR, NULL, 0);
1139                        int32_t reqNavigation = getIntegerAttribute(tree,
1140                                REQ_NAVIGATION_ATTR, NULL, 0);
1141                        int32_t reqFiveWayNav = getIntegerAttribute(tree,
1142                                REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
1143                        printf("uses-configuration:");
1144                        if (reqTouchScreen != 0) {
1145                            printf(" reqTouchScreen='%d'", reqTouchScreen);
1146                        }
1147                        if (reqKeyboardType != 0) {
1148                            printf(" reqKeyboardType='%d'", reqKeyboardType);
1149                        }
1150                        if (reqHardKeyboard != 0) {
1151                            printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1152                        }
1153                        if (reqNavigation != 0) {
1154                            printf(" reqNavigation='%d'", reqNavigation);
1155                        }
1156                        if (reqFiveWayNav != 0) {
1157                            printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1158                        }
1159                        printf("\n");
1160                    } else if (tag == "supports-input") {
1161                        withinSupportsInput = true;
1162                    } else if (tag == "supports-screens") {
1163                        smallScreen = getIntegerAttribute(tree,
1164                                SMALL_SCREEN_ATTR, NULL, 1);
1165                        normalScreen = getIntegerAttribute(tree,
1166                                NORMAL_SCREEN_ATTR, NULL, 1);
1167                        largeScreen = getIntegerAttribute(tree,
1168                                LARGE_SCREEN_ATTR, NULL, 1);
1169                        xlargeScreen = getIntegerAttribute(tree,
1170                                XLARGE_SCREEN_ATTR, NULL, 1);
1171                        anyDensity = getIntegerAttribute(tree,
1172                                ANY_DENSITY_ATTR, NULL, 1);
1173                        requiresSmallestWidthDp = getIntegerAttribute(tree,
1174                                REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
1175                        compatibleWidthLimitDp = getIntegerAttribute(tree,
1176                                COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
1177                        largestWidthLimitDp = getIntegerAttribute(tree,
1178                                LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
1179                    } else if (tag == "uses-feature") {
1180                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1181
1182                        if (name != "" && error == "") {
1183                            int req = getIntegerAttribute(tree,
1184                                    REQUIRED_ATTR, NULL, 1);
1185
1186                            if (name == "android.hardware.camera") {
1187                                specCameraFeature = true;
1188                            } else if (name == "android.hardware.camera.autofocus") {
1189                                // these have no corresponding permission to check for,
1190                                // but should imply the foundational camera permission
1191                                reqCameraAutofocusFeature = reqCameraAutofocusFeature || req;
1192                                specCameraAutofocusFeature = true;
1193                            } else if (req && (name == "android.hardware.camera.flash")) {
1194                                // these have no corresponding permission to check for,
1195                                // but should imply the foundational camera permission
1196                                reqCameraFlashFeature = true;
1197                            } else if (name == "android.hardware.location") {
1198                                specLocationFeature = true;
1199                            } else if (name == "android.hardware.location.network") {
1200                                specNetworkLocFeature = true;
1201                                reqNetworkLocFeature = reqNetworkLocFeature || req;
1202                            } else if (name == "android.hardware.location.gps") {
1203                                specGpsFeature = true;
1204                                reqGpsFeature = reqGpsFeature || req;
1205                            } else if (name == "android.hardware.bluetooth") {
1206                                specBluetoothFeature = true;
1207                            } else if (name == "android.hardware.touchscreen") {
1208                                specTouchscreenFeature = true;
1209                            } else if (name == "android.hardware.touchscreen.multitouch") {
1210                                specMultitouchFeature = true;
1211                            } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
1212                                reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req;
1213                            } else if (name == "android.hardware.microphone") {
1214                                specMicrophoneFeature = true;
1215                            } else if (name == "android.hardware.wifi") {
1216                                specWiFiFeature = true;
1217                            } else if (name == "android.hardware.telephony") {
1218                                specTelephonyFeature = true;
1219                            } else if (req && (name == "android.hardware.telephony.gsm" ||
1220                                               name == "android.hardware.telephony.cdma")) {
1221                                // these have no corresponding permission to check for,
1222                                // but should imply the foundational telephony permission
1223                                reqTelephonySubFeature = true;
1224                            } else if (name == "android.hardware.screen.portrait") {
1225                                specScreenPortraitFeature = true;
1226                            } else if (name == "android.hardware.screen.landscape") {
1227                                specScreenLandscapeFeature = true;
1228                            }
1229                            printf("uses-feature%s:'%s'\n",
1230                                    req ? "" : "-not-required",
1231                                            ResTable::normalizeForOutput(name.string()).string());
1232                        } else {
1233                            int vers = getIntegerAttribute(tree,
1234                                    GL_ES_VERSION_ATTR, &error);
1235                            if (error == "") {
1236                                printf("uses-gl-es:'0x%x'\n", vers);
1237                            }
1238                        }
1239                    } else if (tag == "uses-permission") {
1240                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1241                        if (name != "" && error == "") {
1242                            if (name == "android.permission.CAMERA") {
1243                                hasCameraPermission = true;
1244                            } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
1245                                hasGpsPermission = true;
1246                            } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
1247                                hasMockLocPermission = true;
1248                            } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
1249                                hasCoarseLocPermission = true;
1250                            } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
1251                                       name == "android.permission.INSTALL_LOCATION_PROVIDER") {
1252                                hasGeneralLocPermission = true;
1253                            } else if (name == "android.permission.BLUETOOTH" ||
1254                                       name == "android.permission.BLUETOOTH_ADMIN") {
1255                                hasBluetoothPermission = true;
1256                            } else if (name == "android.permission.RECORD_AUDIO") {
1257                                hasRecordAudioPermission = true;
1258                            } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1259                                       name == "android.permission.CHANGE_WIFI_STATE" ||
1260                                       name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
1261                                hasWiFiPermission = true;
1262                            } else if (name == "android.permission.CALL_PHONE" ||
1263                                       name == "android.permission.CALL_PRIVILEGED" ||
1264                                       name == "android.permission.MODIFY_PHONE_STATE" ||
1265                                       name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1266                                       name == "android.permission.READ_SMS" ||
1267                                       name == "android.permission.RECEIVE_SMS" ||
1268                                       name == "android.permission.RECEIVE_MMS" ||
1269                                       name == "android.permission.RECEIVE_WAP_PUSH" ||
1270                                       name == "android.permission.SEND_SMS" ||
1271                                       name == "android.permission.WRITE_APN_SETTINGS" ||
1272                                       name == "android.permission.WRITE_SMS") {
1273                                hasTelephonyPermission = true;
1274                            } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1275                                hasWriteExternalStoragePermission = true;
1276                            } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1277                                hasReadExternalStoragePermission = true;
1278                            } else if (name == "android.permission.READ_PHONE_STATE") {
1279                                hasReadPhoneStatePermission = true;
1280                            } else if (name == "android.permission.READ_CONTACTS") {
1281                                hasReadContactsPermission = true;
1282                            } else if (name == "android.permission.WRITE_CONTACTS") {
1283                                hasWriteContactsPermission = true;
1284                            } else if (name == "android.permission.READ_CALL_LOG") {
1285                                hasReadCallLogPermission = true;
1286                            } else if (name == "android.permission.WRITE_CALL_LOG") {
1287                                hasWriteCallLogPermission = true;
1288                            }
1289
1290                            printUsesPermission(name,
1291                                    getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1) == 0,
1292                                    getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1));
1293                       } else {
1294                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1295                                    error.string());
1296                            goto bail;
1297                        }
1298                    } else if (tag == "uses-package") {
1299                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1300                        if (name != "" && error == "") {
1301                            printf("uses-package:'%s'\n",
1302                                    ResTable::normalizeForOutput(name.string()).string());
1303                        } else {
1304                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1305                                    error.string());
1306                                goto bail;
1307                        }
1308                    } else if (tag == "original-package") {
1309                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1310                        if (name != "" && error == "") {
1311                            printf("original-package:'%s'\n",
1312                                    ResTable::normalizeForOutput(name.string()).string());
1313                        } else {
1314                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1315                                    error.string());
1316                                goto bail;
1317                        }
1318                    } else if (tag == "supports-gl-texture") {
1319                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1320                        if (name != "" && error == "") {
1321                            printf("supports-gl-texture:'%s'\n",
1322                                    ResTable::normalizeForOutput(name.string()).string());
1323                        } else {
1324                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1325                                    error.string());
1326                                goto bail;
1327                        }
1328                    } else if (tag == "compatible-screens") {
1329                        printCompatibleScreens(tree, &error);
1330                        if (error != "") {
1331                            fprintf(stderr, "ERROR getting compatible screens: %s\n",
1332                                    error.string());
1333                            goto bail;
1334                        }
1335                        depth--;
1336                    } else if (tag == "package-verifier") {
1337                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1338                        if (name != "" && error == "") {
1339                            String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
1340                            if (publicKey != "" && error == "") {
1341                                printf("package-verifier: name='%s' publicKey='%s'\n",
1342                                        ResTable::normalizeForOutput(name.string()).string(),
1343                                        ResTable::normalizeForOutput(publicKey.string()).string());
1344                            }
1345                        }
1346                    }
1347                } else if (depth == 3) {
1348                    withinActivity = false;
1349                    withinReceiver = false;
1350                    withinService = false;
1351                    hasIntentFilter = false;
1352                    hasMetaHostPaymentCategory = false;
1353                    hasMetaOffHostPaymentCategory = false;
1354                    hasBindDeviceAdminPermission = false;
1355                    hasBindInputMethodPermission = false;
1356                    hasBindAccessibilityServicePermission = false;
1357                    hasBindPrintServicePermission = false;
1358                    hasBindNfcServicePermission = false;
1359                    if (withinApplication) {
1360                        if(tag == "activity") {
1361                            withinActivity = true;
1362                            activityName = getAttribute(tree, NAME_ATTR, &error);
1363                            if (error != "") {
1364                                fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1365                                        error.string());
1366                                goto bail;
1367                            }
1368
1369                            activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1370                            if (error != "") {
1371                                fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
1372                                        error.string());
1373                                goto bail;
1374                            }
1375
1376                            activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1377                            if (error != "") {
1378                                fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1379                                        error.string());
1380                                goto bail;
1381                            }
1382
1383                            activityBanner = getResolvedAttribute(&res, tree, BANNER_ATTR, &error);
1384                            if (error != "") {
1385                                fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
1386                                        error.string());
1387                                goto bail;
1388                            }
1389
1390                            int32_t orien = getResolvedIntegerAttribute(&res, tree,
1391                                    SCREEN_ORIENTATION_ATTR, &error);
1392                            if (error == "") {
1393                                if (orien == 0 || orien == 6 || orien == 8) {
1394                                    // Requests landscape, sensorLandscape, or reverseLandscape.
1395                                    reqScreenLandscapeFeature = true;
1396                                } else if (orien == 1 || orien == 7 || orien == 9) {
1397                                    // Requests portrait, sensorPortrait, or reversePortrait.
1398                                    reqScreenPortraitFeature = true;
1399                                }
1400                            }
1401                        } else if (tag == "uses-library") {
1402                            String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1403                            if (error != "") {
1404                                fprintf(stderr,
1405                                        "ERROR getting 'android:name' attribute for uses-library"
1406                                        " %s\n", error.string());
1407                                goto bail;
1408                            }
1409                            int req = getIntegerAttribute(tree,
1410                                    REQUIRED_ATTR, NULL, 1);
1411                            printf("uses-library%s:'%s'\n",
1412                                    req ? "" : "-not-required", ResTable::normalizeForOutput(
1413                                            libraryName.string()).string());
1414                        } else if (tag == "receiver") {
1415                            withinReceiver = true;
1416                            receiverName = getAttribute(tree, NAME_ATTR, &error);
1417
1418                            if (error != "") {
1419                                fprintf(stderr,
1420                                        "ERROR getting 'android:name' attribute for receiver:"
1421                                        " %s\n", error.string());
1422                                goto bail;
1423                            }
1424
1425                            String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1426                            if (error == "") {
1427                                if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1428                                    hasBindDeviceAdminPermission = true;
1429                                }
1430                            } else {
1431                                fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1432                                        " receiver '%s': %s\n", receiverName.string(), error.string());
1433                            }
1434                        } else if (tag == "service") {
1435                            withinService = true;
1436                            serviceName = getAttribute(tree, NAME_ATTR, &error);
1437
1438                            if (error != "") {
1439                                fprintf(stderr, "ERROR getting 'android:name' attribute for "
1440                                        "service:%s\n", error.string());
1441                                goto bail;
1442                            }
1443
1444                            String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1445                            if (error == "") {
1446                                if (permission == "android.permission.BIND_INPUT_METHOD") {
1447                                    hasBindInputMethodPermission = true;
1448                                } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") {
1449                                    hasBindAccessibilityServicePermission = true;
1450                                } else if (permission == "android.permission.BIND_PRINT_SERVICE") {
1451                                    hasBindPrintServicePermission = true;
1452                                } else if (permission == "android.permission.BIND_NFC_SERVICE") {
1453                                    hasBindNfcServicePermission = true;
1454                                }
1455                            } else {
1456                                fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1457                                        " service '%s': %s\n", serviceName.string(), error.string());
1458                            }
1459                        } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
1460                            String8 metaDataName = getResolvedAttribute(&res, tree, NAME_ATTR, &error);
1461                            if (error != "") {
1462                                fprintf(stderr, "ERROR getting 'android:name' attribute for "
1463                                        "meta-data:%s\n", error.string());
1464                                goto bail;
1465                            }
1466                            printf("meta-data: name='%s' ",
1467                                    ResTable::normalizeForOutput(metaDataName.string()).string());
1468                            printResolvedResourceAttribute(&res, tree, VALUE_ATTR, String8("value"),
1469                                    &error);
1470                            if (error != "") {
1471                                // Try looking for a RESOURCE_ATTR
1472                                error = "";
1473                                printResolvedResourceAttribute(&res, tree, RESOURCE_ATTR,
1474                                        String8("resource"), &error);
1475                                if (error != "") {
1476                                    fprintf(stderr, "ERROR getting 'android:value' or "
1477                                            "'android:resource' attribute for "
1478                                            "meta-data:%s\n", error.string());
1479                                    goto bail;
1480                                }
1481                            }
1482                            printf("\n");
1483                        } else if (withinSupportsInput && tag == "input-type") {
1484                            String8 name = getAttribute(tree, NAME_ATTR, &error);
1485                            if (name != "" && error == "") {
1486                                supportedInput.add(name);
1487                            } else {
1488                                fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1489                                        error.string());
1490                                goto bail;
1491                            }
1492                        }
1493                    }
1494                } else if (depth == 4) {
1495                    if (tag == "intent-filter") {
1496                        hasIntentFilter = true;
1497                        withinIntentFilter = true;
1498                        actMainActivity = false;
1499                        actWidgetReceivers = false;
1500                        actImeService = false;
1501                        actWallpaperService = false;
1502                        actAccessibilityService = false;
1503                        actPrintService = false;
1504                        actDeviceAdminEnabled = false;
1505                        actHostApduService = false;
1506                        actOffHostApduService = false;
1507                    } else if (withinService && tag == "meta-data") {
1508                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1509                        if (error != "") {
1510                            fprintf(stderr, "ERROR getting 'android:name' attribute for"
1511                                    " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1512                            goto bail;
1513                        }
1514
1515                        if (name == "android.nfc.cardemulation.host_apdu_service" ||
1516                                name == "android.nfc.cardemulation.off_host_apdu_service") {
1517                            bool offHost = true;
1518                            if (name == "android.nfc.cardemulation.host_apdu_service") {
1519                                offHost = false;
1520                            }
1521
1522                            String8 xmlPath = getResolvedAttribute(&res, tree, RESOURCE_ATTR, &error);
1523                            if (error != "") {
1524                                fprintf(stderr, "ERROR getting 'android:resource' attribute for"
1525                                        " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1526                                goto bail;
1527                            }
1528
1529                            Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1530                                    offHost, &error);
1531                            if (error != "") {
1532                                fprintf(stderr, "ERROR getting AID category for service '%s'\n",
1533                                        serviceName.string());
1534                                goto bail;
1535                            }
1536
1537                            const size_t catLen = categories.size();
1538                            for (size_t i = 0; i < catLen; i++) {
1539                                bool paymentCategory = (categories[i] == "payment");
1540                                if (offHost) {
1541                                    hasMetaOffHostPaymentCategory |= paymentCategory;
1542                                } else {
1543                                    hasMetaHostPaymentCategory |= paymentCategory;
1544                                }
1545                            }
1546                        }
1547                    }
1548                } else if ((depth == 5) && withinIntentFilter) {
1549                    String8 action;
1550                    if (tag == "action") {
1551                        action = getAttribute(tree, NAME_ATTR, &error);
1552                        if (error != "") {
1553                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1554                                    error.string());
1555                            goto bail;
1556                        }
1557
1558                        if (withinActivity) {
1559                            if (action == "android.intent.action.MAIN") {
1560                                isMainActivity = true;
1561                                actMainActivity = true;
1562                            }
1563                        } else if (withinReceiver) {
1564                            if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1565                                actWidgetReceivers = true;
1566                            } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1567                                actDeviceAdminEnabled = true;
1568                            }
1569                        } else if (withinService) {
1570                            if (action == "android.view.InputMethod") {
1571                                actImeService = true;
1572                            } else if (action == "android.service.wallpaper.WallpaperService") {
1573                                actWallpaperService = true;
1574                            } else if (action == "android.accessibilityservice.AccessibilityService") {
1575                                actAccessibilityService = true;
1576                            } else if (action == "android.printservice.PrintService") {
1577                                actPrintService = true;
1578                            } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
1579                                actHostApduService = true;
1580                            } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
1581                                actOffHostApduService = true;
1582                            }
1583                        }
1584                        if (action == "android.intent.action.SEARCH") {
1585                            isSearchable = true;
1586                        }
1587                    }
1588
1589                    if (tag == "category") {
1590                        String8 category = getAttribute(tree, NAME_ATTR, &error);
1591                        if (error != "") {
1592                            fprintf(stderr, "ERROR getting 'name' attribute: %s\n",
1593                                    error.string());
1594                            goto bail;
1595                        }
1596                        if (withinActivity) {
1597                            if (category == "android.intent.category.LAUNCHER") {
1598                                isLauncherActivity = true;
1599                            } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
1600                                isLeanbackLauncherActivity = true;
1601                            }
1602                        }
1603                    }
1604                }
1605            }
1606
1607            // Pre-1.6 implicitly granted permission compatibility logic
1608            if (targetSdk < 4) {
1609                if (!hasWriteExternalStoragePermission) {
1610                    printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
1611                    printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
1612                            String8("targetSdkVersion < 4"));
1613                    hasWriteExternalStoragePermission = true;
1614                }
1615                if (!hasReadPhoneStatePermission) {
1616                    printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
1617                    printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
1618                            String8("targetSdkVersion < 4"));
1619                }
1620            }
1621
1622            // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1623            // force them to always take READ_EXTERNAL_STORAGE as well.  We always
1624            // do this (regardless of target API version) because we can't have
1625            // an app with write permission but not read permission.
1626            if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
1627                printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"));
1628                printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
1629                        String8("requested WRITE_EXTERNAL_STORAGE"));
1630            }
1631
1632            // Pre-JellyBean call log permission compatibility.
1633            if (targetSdk < 16) {
1634                if (!hasReadCallLogPermission && hasReadContactsPermission) {
1635                    printUsesPermission(String8("android.permission.READ_CALL_LOG"));
1636                    printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
1637                            String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
1638                }
1639                if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
1640                    printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
1641                    printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
1642                            String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
1643                }
1644            }
1645
1646            /* The following blocks handle printing "inferred" uses-features, based
1647             * on whether related features or permissions are used by the app.
1648             * Note that the various spec*Feature variables denote whether the
1649             * relevant tag was *present* in the AndroidManfest, not that it was
1650             * present and set to true.
1651             */
1652            // Camera-related back-compatibility logic
1653            if (!specCameraFeature) {
1654                if (reqCameraFlashFeature) {
1655                    // if app requested a sub-feature (autofocus or flash) and didn't
1656                    // request the base camera feature, we infer that it meant to
1657                    printf("uses-feature:'android.hardware.camera'\n");
1658                    printf("uses-implied-feature:'android.hardware.camera'," \
1659                            "'requested android.hardware.camera.flash feature'\n");
1660                } else if (reqCameraAutofocusFeature) {
1661                    // if app requested a sub-feature (autofocus or flash) and didn't
1662                    // request the base camera feature, we infer that it meant to
1663                    printf("uses-feature:'android.hardware.camera'\n");
1664                    printf("uses-implied-feature:'android.hardware.camera'," \
1665                            "'requested android.hardware.camera.autofocus feature'\n");
1666                } else if (hasCameraPermission) {
1667                    // if app wants to use camera but didn't request the feature, we infer
1668                    // that it meant to, and further that it wants autofocus
1669                    // (which was the 1.0 - 1.5 behavior)
1670                    printf("uses-feature:'android.hardware.camera'\n");
1671                    if (!specCameraAutofocusFeature) {
1672                        printf("uses-feature:'android.hardware.camera.autofocus'\n");
1673                        printf("uses-implied-feature:'android.hardware.camera.autofocus'," \
1674                                "'requested android.permission.CAMERA permission'\n");
1675                    }
1676                }
1677            }
1678
1679            // Location-related back-compatibility logic
1680            if (!specLocationFeature &&
1681                (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission ||
1682                 hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) {
1683                // if app either takes a location-related permission or requests one of the
1684                // sub-features, we infer that it also meant to request the base location feature
1685                printf("uses-feature:'android.hardware.location'\n");
1686                printf("uses-implied-feature:'android.hardware.location'," \
1687                        "'requested a location access permission'\n");
1688            }
1689            if (!specGpsFeature && hasGpsPermission) {
1690                // if app takes GPS (FINE location) perm but does not request the GPS
1691                // feature, we infer that it meant to
1692                printf("uses-feature:'android.hardware.location.gps'\n");
1693                printf("uses-implied-feature:'android.hardware.location.gps'," \
1694                        "'requested android.permission.ACCESS_FINE_LOCATION permission'\n");
1695            }
1696            if (!specNetworkLocFeature && hasCoarseLocPermission) {
1697                // if app takes Network location (COARSE location) perm but does not request the
1698                // network location feature, we infer that it meant to
1699                printf("uses-feature:'android.hardware.location.network'\n");
1700                printf("uses-implied-feature:'android.hardware.location.network'," \
1701                        "'requested android.permission.ACCESS_COARSE_LOCATION permission'\n");
1702            }
1703
1704            // Bluetooth-related compatibility logic
1705            if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
1706                // if app takes a Bluetooth permission but does not request the Bluetooth
1707                // feature, we infer that it meant to
1708                printf("uses-feature:'android.hardware.bluetooth'\n");
1709                printf("uses-implied-feature:'android.hardware.bluetooth'," \
1710                        "'requested android.permission.BLUETOOTH or android.permission.BLUETOOTH_ADMIN " \
1711                        "permission and targetSdkVersion > 4'\n");
1712            }
1713
1714            // Microphone-related compatibility logic
1715            if (!specMicrophoneFeature && hasRecordAudioPermission) {
1716                // if app takes the record-audio permission but does not request the microphone
1717                // feature, we infer that it meant to
1718                printf("uses-feature:'android.hardware.microphone'\n");
1719                printf("uses-implied-feature:'android.hardware.microphone'," \
1720                        "'requested android.permission.RECORD_AUDIO permission'\n");
1721            }
1722
1723            // WiFi-related compatibility logic
1724            if (!specWiFiFeature && hasWiFiPermission) {
1725                // if app takes one of the WiFi permissions but does not request the WiFi
1726                // feature, we infer that it meant to
1727                printf("uses-feature:'android.hardware.wifi'\n");
1728                printf("uses-implied-feature:'android.hardware.wifi'," \
1729                        "'requested android.permission.ACCESS_WIFI_STATE, " \
1730                        "android.permission.CHANGE_WIFI_STATE, or " \
1731                        "android.permission.CHANGE_WIFI_MULTICAST_STATE permission'\n");
1732            }
1733
1734            // Telephony-related compatibility logic
1735            if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) {
1736                // if app takes one of the telephony permissions or requests a sub-feature but
1737                // does not request the base telephony feature, we infer that it meant to
1738                printf("uses-feature:'android.hardware.telephony'\n");
1739                printf("uses-implied-feature:'android.hardware.telephony'," \
1740                        "'requested a telephony-related permission or feature'\n");
1741            }
1742
1743            // Touchscreen-related back-compatibility logic
1744            if (!specTouchscreenFeature) { // not a typo!
1745                // all apps are presumed to require a touchscreen, unless they explicitly say
1746                // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
1747                // Note that specTouchscreenFeature is true if the tag is present, regardless
1748                // of whether its value is true or false, so this is safe
1749                printf("uses-feature:'android.hardware.touchscreen'\n");
1750                printf("uses-implied-feature:'android.hardware.touchscreen'," \
1751                        "'assumed you require a touch screen unless explicitly made optional'\n");
1752            }
1753            if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
1754                // if app takes one of the telephony permissions or requests a sub-feature but
1755                // does not request the base telephony feature, we infer that it meant to
1756                printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
1757                printf("uses-implied-feature:'android.hardware.touchscreen.multitouch'," \
1758                        "'requested android.hardware.touchscreen.multitouch.distinct feature'\n");
1759            }
1760
1761            // Landscape/portrait-related compatibility logic
1762            if (!specScreenLandscapeFeature && !specScreenPortraitFeature) {
1763                // If the app has specified any activities in its manifest
1764                // that request a specific orientation, then assume that
1765                // orientation is required.
1766                if (reqScreenLandscapeFeature) {
1767                    printf("uses-feature:'android.hardware.screen.landscape'\n");
1768                    printf("uses-implied-feature:'android.hardware.screen.landscape'," \
1769                            "'one or more activities have specified a landscape orientation'\n");
1770                }
1771                if (reqScreenPortraitFeature) {
1772                    printf("uses-feature:'android.hardware.screen.portrait'\n");
1773                    printf("uses-implied-feature:'android.hardware.screen.portrait'," \
1774                            "'one or more activities have specified a portrait orientation'\n");
1775                }
1776            }
1777
1778            if (hasMainActivity) {
1779                printf("main\n");
1780            }
1781            if (hasWidgetReceivers) {
1782                printf("app-widget\n");
1783            }
1784            if (hasDeviceAdminReceiver) {
1785                printf("device-admin\n");
1786            }
1787            if (hasImeService) {
1788                printf("ime\n");
1789            }
1790            if (hasWallpaperService) {
1791                printf("wallpaper\n");
1792            }
1793            if (hasAccessibilityService) {
1794                printf("accessibility\n");
1795            }
1796            if (hasPrintService) {
1797                printf("print\n");
1798            }
1799            if (hasPaymentService) {
1800                printf("payment\n");
1801            }
1802            if (hasOtherActivities) {
1803                printf("other-activities\n");
1804            }
1805            if (isSearchable) {
1806                printf("search\n");
1807            }
1808            if (hasOtherReceivers) {
1809                printf("other-receivers\n");
1810            }
1811            if (hasOtherServices) {
1812                printf("other-services\n");
1813            }
1814
1815            // For modern apps, if screen size buckets haven't been specified
1816            // but the new width ranges have, then infer the buckets from them.
1817            if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1818                    && requiresSmallestWidthDp > 0) {
1819                int compatWidth = compatibleWidthLimitDp;
1820                if (compatWidth <= 0) {
1821                    compatWidth = requiresSmallestWidthDp;
1822                }
1823                if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1824                    smallScreen = -1;
1825                } else {
1826                    smallScreen = 0;
1827                }
1828                if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1829                    normalScreen = -1;
1830                } else {
1831                    normalScreen = 0;
1832                }
1833                if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1834                    largeScreen = -1;
1835                } else {
1836                    largeScreen = 0;
1837                }
1838                if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1839                    xlargeScreen = -1;
1840                } else {
1841                    xlargeScreen = 0;
1842                }
1843            }
1844
1845            // Determine default values for any unspecified screen sizes,
1846            // based on the target SDK of the package.  As of 4 (donut)
1847            // the screen size support was introduced, so all default to
1848            // enabled.
1849            if (smallScreen > 0) {
1850                smallScreen = targetSdk >= 4 ? -1 : 0;
1851            }
1852            if (normalScreen > 0) {
1853                normalScreen = -1;
1854            }
1855            if (largeScreen > 0) {
1856                largeScreen = targetSdk >= 4 ? -1 : 0;
1857            }
1858            if (xlargeScreen > 0) {
1859                // Introduced in Gingerbread.
1860                xlargeScreen = targetSdk >= 9 ? -1 : 0;
1861            }
1862            if (anyDensity > 0) {
1863                anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1864                        || compatibleWidthLimitDp > 0) ? -1 : 0;
1865            }
1866            printf("supports-screens:");
1867            if (smallScreen != 0) {
1868                printf(" 'small'");
1869            }
1870            if (normalScreen != 0) {
1871                printf(" 'normal'");
1872            }
1873            if (largeScreen != 0) {
1874                printf(" 'large'");
1875            }
1876            if (xlargeScreen != 0) {
1877                printf(" 'xlarge'");
1878            }
1879            printf("\n");
1880            printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
1881            if (requiresSmallestWidthDp > 0) {
1882                printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1883            }
1884            if (compatibleWidthLimitDp > 0) {
1885                printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1886            }
1887            if (largestWidthLimitDp > 0) {
1888                printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1889            }
1890
1891            printf("locales:");
1892            const size_t NL = locales.size();
1893            for (size_t i=0; i<NL; i++) {
1894                const char* localeStr =  locales[i].string();
1895                if (localeStr == NULL || strlen(localeStr) == 0) {
1896                    localeStr = "--_--";
1897                }
1898                printf(" '%s'", localeStr);
1899            }
1900            printf("\n");
1901
1902            printf("densities:");
1903            const size_t ND = densities.size();
1904            for (size_t i=0; i<ND; i++) {
1905                printf(" '%d'", densities[i]);
1906            }
1907            printf("\n");
1908
1909            AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1910            if (dir != NULL) {
1911                if (dir->getFileCount() > 0) {
1912                    printf("native-code:");
1913                    for (size_t i=0; i<dir->getFileCount(); i++) {
1914                        printf(" '%s'", ResTable::normalizeForOutput(
1915                                dir->getFileName(i).string()).string());
1916                    }
1917                    printf("\n");
1918                }
1919                delete dir;
1920            }
1921        } else if (strcmp("badger", option) == 0) {
1922            printf("%s", CONSOLE_DATA);
1923        } else if (strcmp("configurations", option) == 0) {
1924            Vector<ResTable_config> configs;
1925            res.getConfigurations(&configs);
1926            const size_t N = configs.size();
1927            for (size_t i=0; i<N; i++) {
1928                printf("%s\n", configs[i].toString().string());
1929            }
1930        } else {
1931            fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
1932            goto bail;
1933        }
1934    }
1935
1936    result = NO_ERROR;
1937
1938bail:
1939    if (asset) {
1940        delete asset;
1941    }
1942    return (result != NO_ERROR);
1943}
1944
1945
1946/*
1947 * Handle the "add" command, which wants to add files to a new or
1948 * pre-existing archive.
1949 */
1950int doAdd(Bundle* bundle)
1951{
1952    ZipFile* zip = NULL;
1953    status_t result = UNKNOWN_ERROR;
1954    const char* zipFileName;
1955
1956    if (bundle->getUpdate()) {
1957        /* avoid confusion */
1958        fprintf(stderr, "ERROR: can't use '-u' with add\n");
1959        goto bail;
1960    }
1961
1962    if (bundle->getFileSpecCount() < 1) {
1963        fprintf(stderr, "ERROR: must specify zip file name\n");
1964        goto bail;
1965    }
1966    zipFileName = bundle->getFileSpecEntry(0);
1967
1968    if (bundle->getFileSpecCount() < 2) {
1969        fprintf(stderr, "NOTE: nothing to do\n");
1970        goto bail;
1971    }
1972
1973    zip = openReadWrite(zipFileName, true);
1974    if (zip == NULL) {
1975        fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
1976        goto bail;
1977    }
1978
1979    for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1980        const char* fileName = bundle->getFileSpecEntry(i);
1981
1982        if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
1983            printf(" '%s'... (from gzip)\n", fileName);
1984            result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
1985        } else {
1986            if (bundle->getJunkPath()) {
1987                String8 storageName = String8(fileName).getPathLeaf();
1988                printf(" '%s' as '%s'...\n", fileName,
1989                        ResTable::normalizeForOutput(storageName.string()).string());
1990                result = zip->add(fileName, storageName.string(),
1991                                  bundle->getCompressionMethod(), NULL);
1992            } else {
1993                printf(" '%s'...\n", fileName);
1994                result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
1995            }
1996        }
1997        if (result != NO_ERROR) {
1998            fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
1999            if (result == NAME_NOT_FOUND) {
2000                fprintf(stderr, ": file not found\n");
2001            } else if (result == ALREADY_EXISTS) {
2002                fprintf(stderr, ": already exists in archive\n");
2003            } else {
2004                fprintf(stderr, "\n");
2005            }
2006            goto bail;
2007        }
2008    }
2009
2010    result = NO_ERROR;
2011
2012bail:
2013    delete zip;
2014    return (result != NO_ERROR);
2015}
2016
2017
2018/*
2019 * Delete files from an existing archive.
2020 */
2021int doRemove(Bundle* bundle)
2022{
2023    ZipFile* zip = NULL;
2024    status_t result = UNKNOWN_ERROR;
2025    const char* zipFileName;
2026
2027    if (bundle->getFileSpecCount() < 1) {
2028        fprintf(stderr, "ERROR: must specify zip file name\n");
2029        goto bail;
2030    }
2031    zipFileName = bundle->getFileSpecEntry(0);
2032
2033    if (bundle->getFileSpecCount() < 2) {
2034        fprintf(stderr, "NOTE: nothing to do\n");
2035        goto bail;
2036    }
2037
2038    zip = openReadWrite(zipFileName, false);
2039    if (zip == NULL) {
2040        fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2041            zipFileName);
2042        goto bail;
2043    }
2044
2045    for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2046        const char* fileName = bundle->getFileSpecEntry(i);
2047        ZipEntry* entry;
2048
2049        entry = zip->getEntryByName(fileName);
2050        if (entry == NULL) {
2051            printf(" '%s' NOT FOUND\n", fileName);
2052            continue;
2053        }
2054
2055        result = zip->remove(entry);
2056
2057        if (result != NO_ERROR) {
2058            fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2059                bundle->getFileSpecEntry(i), zipFileName);
2060            goto bail;
2061        }
2062    }
2063
2064    /* update the archive */
2065    zip->flush();
2066
2067bail:
2068    delete zip;
2069    return (result != NO_ERROR);
2070}
2071
2072static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
2073    const size_t numDirs = dir->getDirs().size();
2074    for (size_t i = 0; i < numDirs; i++) {
2075        bool ignore = ignoreConfig;
2076        const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2077        const char* dirStr = subDir->getLeaf().string();
2078        if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2079            ignore = true;
2080        }
2081        status_t err = addResourcesToBuilder(subDir, builder, ignore);
2082        if (err != NO_ERROR) {
2083            return err;
2084        }
2085    }
2086
2087    const size_t numFiles = dir->getFiles().size();
2088    for (size_t i = 0; i < numFiles; i++) {
2089        sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2090        const size_t numConfigs = gp->getFiles().size();
2091        for (size_t j = 0; j < numConfigs; j++) {
2092            status_t err = NO_ERROR;
2093            if (ignoreConfig) {
2094                err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2095            } else {
2096                err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2097            }
2098            if (err != NO_ERROR) {
2099                fprintf(stderr, "Failed to add %s (%s) to builder.\n",
2100                        gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
2101                return err;
2102            }
2103        }
2104    }
2105    return NO_ERROR;
2106}
2107
2108static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2109    if (split->isBase()) {
2110        return original;
2111    }
2112
2113    String8 ext(original.getPathExtension());
2114    if (ext == String8(".apk")) {
2115        return String8::format("%s_%s%s",
2116                original.getBasePath().string(),
2117                split->getDirectorySafeName().string(),
2118                ext.string());
2119    }
2120
2121    return String8::format("%s_%s", original.string(),
2122            split->getDirectorySafeName().string());
2123}
2124
2125/*
2126 * Package up an asset directory and associated application files.
2127 */
2128int doPackage(Bundle* bundle)
2129{
2130    const char* outputAPKFile;
2131    int retVal = 1;
2132    status_t err;
2133    sp<AaptAssets> assets;
2134    int N;
2135    FILE* fp;
2136    String8 dependencyFile;
2137    sp<ApkBuilder> builder;
2138
2139    // -c en_XA or/and ar_XB means do pseudolocalization
2140    sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2141    err = configFilter->parse(bundle->getConfigurations());
2142    if (err != NO_ERROR) {
2143        goto bail;
2144    }
2145    if (configFilter->containsPseudo()) {
2146        bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2147    }
2148    if (configFilter->containsPseudoBidi()) {
2149        bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
2150    }
2151
2152    N = bundle->getFileSpecCount();
2153    if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
2154            && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
2155        fprintf(stderr, "ERROR: no input files\n");
2156        goto bail;
2157    }
2158
2159    outputAPKFile = bundle->getOutputAPKFile();
2160
2161    // Make sure the filenames provided exist and are of the appropriate type.
2162    if (outputAPKFile) {
2163        FileType type;
2164        type = getFileType(outputAPKFile);
2165        if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2166            fprintf(stderr,
2167                "ERROR: output file '%s' exists but is not regular file\n",
2168                outputAPKFile);
2169            goto bail;
2170        }
2171    }
2172
2173    // Load the assets.
2174    assets = new AaptAssets();
2175
2176    // Set up the resource gathering in assets if we're going to generate
2177    // dependency files. Every time we encounter a resource while slurping
2178    // the tree, we'll add it to these stores so we have full resource paths
2179    // to write to a dependency file.
2180    if (bundle->getGenDependencies()) {
2181        sp<FilePathStore> resPathStore = new FilePathStore;
2182        assets->setFullResPaths(resPathStore);
2183        sp<FilePathStore> assetPathStore = new FilePathStore;
2184        assets->setFullAssetPaths(assetPathStore);
2185    }
2186
2187    err = assets->slurpFromArgs(bundle);
2188    if (err < 0) {
2189        goto bail;
2190    }
2191
2192    if (bundle->getVerbose()) {
2193        assets->print(String8());
2194    }
2195
2196    // Create the ApkBuilder, which will collect the compiled files
2197    // to write to the final APK (or sets of APKs if we are building
2198    // a Split APK.
2199    builder = new ApkBuilder(configFilter);
2200
2201    // If we are generating a Split APK, find out which configurations to split on.
2202    if (bundle->getSplitConfigurations().size() > 0) {
2203        const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2204        const size_t numSplits = splitStrs.size();
2205        for (size_t i = 0; i < numSplits; i++) {
2206            std::set<ConfigDescription> configs;
2207            if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2208                fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2209                goto bail;
2210            }
2211
2212            err = builder->createSplitForConfigs(configs);
2213            if (err != NO_ERROR) {
2214                goto bail;
2215            }
2216        }
2217    }
2218
2219    // If they asked for any fileAs that need to be compiled, do so.
2220    if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
2221        err = buildResources(bundle, assets, builder);
2222        if (err != 0) {
2223            goto bail;
2224        }
2225    }
2226
2227    // At this point we've read everything and processed everything.  From here
2228    // on out it's just writing output files.
2229    if (SourcePos::hasErrors()) {
2230        goto bail;
2231    }
2232
2233    // Update symbols with information about which ones are needed as Java symbols.
2234    assets->applyJavaSymbols();
2235    if (SourcePos::hasErrors()) {
2236        goto bail;
2237    }
2238
2239    // If we've been asked to generate a dependency file, do that here
2240    if (bundle->getGenDependencies()) {
2241        // If this is the packaging step, generate the dependency file next to
2242        // the output apk (e.g. bin/resources.ap_.d)
2243        if (outputAPKFile) {
2244            dependencyFile = String8(outputAPKFile);
2245            // Add the .d extension to the dependency file.
2246            dependencyFile.append(".d");
2247        } else {
2248            // Else if this is the R.java dependency generation step,
2249            // generate the dependency file in the R.java package subdirectory
2250            // e.g. gen/com/foo/app/R.java.d
2251            dependencyFile = String8(bundle->getRClassDir());
2252            dependencyFile.appendPath("R.java.d");
2253        }
2254        // Make sure we have a clean dependency file to start with
2255        fp = fopen(dependencyFile, "w");
2256        fclose(fp);
2257    }
2258
2259    // Write out R.java constants
2260    if (!assets->havePrivateSymbols()) {
2261        if (bundle->getCustomPackage() == NULL) {
2262            // Write the R.java file into the appropriate class directory
2263            // e.g. gen/com/foo/app/R.java
2264            err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
2265        } else {
2266            const String8 customPkg(bundle->getCustomPackage());
2267            err = writeResourceSymbols(bundle, assets, customPkg, true);
2268        }
2269        if (err < 0) {
2270            goto bail;
2271        }
2272        // If we have library files, we're going to write our R.java file into
2273        // the appropriate class directory for those libraries as well.
2274        // e.g. gen/com/foo/app/lib/R.java
2275        if (bundle->getExtraPackages() != NULL) {
2276            // Split on colon
2277            String8 libs(bundle->getExtraPackages());
2278            char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2279            while (packageString != NULL) {
2280                // Write the R.java file out with the correct package name
2281                err = writeResourceSymbols(bundle, assets, String8(packageString), true);
2282                if (err < 0) {
2283                    goto bail;
2284                }
2285                packageString = strtok(NULL, ":");
2286            }
2287            libs.unlockBuffer();
2288        }
2289    } else {
2290        err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
2291        if (err < 0) {
2292            goto bail;
2293        }
2294        err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
2295        if (err < 0) {
2296            goto bail;
2297        }
2298    }
2299
2300    // Write out the ProGuard file
2301    err = writeProguardFile(bundle, assets);
2302    if (err < 0) {
2303        goto bail;
2304    }
2305
2306    // Write the apk
2307    if (outputAPKFile) {
2308        // Gather all resources and add them to the APK Builder. The builder will then
2309        // figure out which Split they belong in.
2310        err = addResourcesToBuilder(assets, builder);
2311        if (err != NO_ERROR) {
2312            goto bail;
2313        }
2314
2315        const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2316        const size_t numSplits = splits.size();
2317        for (size_t i = 0; i < numSplits; i++) {
2318            const sp<ApkSplit>& split = splits[i];
2319            String8 outputPath = buildApkName(String8(outputAPKFile), split);
2320            err = writeAPK(bundle, outputPath, split);
2321            if (err != NO_ERROR) {
2322                fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2323                goto bail;
2324            }
2325        }
2326    }
2327
2328    // If we've been asked to generate a dependency file, we need to finish up here.
2329    // the writeResourceSymbols and writeAPK functions have already written the target
2330    // half of the dependency file, now we need to write the prerequisites. (files that
2331    // the R.java file or .ap_ file depend on)
2332    if (bundle->getGenDependencies()) {
2333        // Now that writeResourceSymbols or writeAPK has taken care of writing
2334        // the targets to our dependency file, we'll write the prereqs
2335        fp = fopen(dependencyFile, "a+");
2336        fprintf(fp, " : ");
2337        bool includeRaw = (outputAPKFile != NULL);
2338        err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2339        // Also manually add the AndroidManifeset since it's not under res/ or assets/
2340        // and therefore was not added to our pathstores during slurping
2341        fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2342        fclose(fp);
2343    }
2344
2345    retVal = 0;
2346bail:
2347    if (SourcePos::hasErrors()) {
2348        SourcePos::printErrors(stderr);
2349    }
2350    return retVal;
2351}
2352
2353/*
2354 * Do PNG Crunching
2355 * PRECONDITIONS
2356 *  -S flag points to a source directory containing drawable* folders
2357 *  -C flag points to destination directory. The folder structure in the
2358 *     source directory will be mirrored to the destination (cache) directory
2359 *
2360 * POSTCONDITIONS
2361 *  Destination directory will be updated to match the PNG files in
2362 *  the source directory.
2363 */
2364int doCrunch(Bundle* bundle)
2365{
2366    fprintf(stdout, "Crunching PNG Files in ");
2367    fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2368    fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2369
2370    updatePreProcessedCache(bundle);
2371
2372    return NO_ERROR;
2373}
2374
2375/*
2376 * Do PNG Crunching on a single flag
2377 *  -i points to a single png file
2378 *  -o points to a single png output file
2379 */
2380int doSingleCrunch(Bundle* bundle)
2381{
2382    fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2383    fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2384
2385    String8 input(bundle->getSingleCrunchInputFile());
2386    String8 output(bundle->getSingleCrunchOutputFile());
2387
2388    if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2389        // we can't return the status_t as it gets truncate to the lower 8 bits.
2390        return 42;
2391    }
2392
2393    return NO_ERROR;
2394}
2395
2396char CONSOLE_DATA[2925] = {
2397    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2398    32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2399    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2400    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2401    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2402    86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2403    62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2404    32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2405    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2406    81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2407    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2408    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2409    32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2410    59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2411    32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2412    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2413    59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2414    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2415    10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2416    32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2417    58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2418    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2419    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2420    47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2421    121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2422    32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2423    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2424    81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2425    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2426    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2427    32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2428    59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2429    32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2430    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2431    59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2432    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2433    32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2434    32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2435    70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2436    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2437    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2438    32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2439    81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2440    32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2441    32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2442    81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2443    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2444    32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2445    59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2446    60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2447    32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2448    61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2449    61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2450    46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2451    32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2452    59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2453    109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2454    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2455    67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2456    59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2457    61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2458    32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2459    59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2460    73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2461    59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2462    32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2463    59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2464    46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2465    97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2466    46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2467    119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2468    59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2469    32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2470    32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2471    119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2472    59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2473    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2474    32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2475    81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2476    87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2477    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2478    81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2479    45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2480    32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2481    32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2482    81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2483    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2484    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2485    59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2486    59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2487    32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2488    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2489    81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2490    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2491    10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2492    32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2493    81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2494    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2495    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2496    81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2497    32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2498    32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2499    32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2500    81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2501    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2502    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2503    58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2504    61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2505    32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2506    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2507    81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2508    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2509    32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2510    32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2511    81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2512    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2513    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2514    59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2515    59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2516    32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2517    32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2518    58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2519    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2520    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2521    61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2522    59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2523    32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2524    32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2525    32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2526    61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2527    32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2528    32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2529    59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2530    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2531    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2532    59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2533    59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2534    32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2535    32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2536    32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2537    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2538    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2539    46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2540    46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2541    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2542    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2543    59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2544    59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2545    32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2546    32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2547    32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2548    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2549    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2550    59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2551    59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2552    32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2553    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2554    32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2555    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2556    10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2557    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2558    32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2559    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2560  };
2561