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