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