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