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