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