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