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