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