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