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