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