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