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