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