Command.cpp revision 681c4f45bf373f84c5b80a4c8dda871ae6fcfcbd
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 withinReceiver = false;
666            bool withinService = false;
667            bool withinIntentFilter = false;
668            bool hasMainActivity = false;
669            bool hasOtherActivities = false;
670            bool hasOtherReceivers = false;
671            bool hasOtherServices = false;
672            bool hasWallpaperService = false;
673            bool hasImeService = false;
674            bool hasWidgetReceivers = false;
675            bool hasIntentFilter = false;
676            bool actMainActivity = false;
677            bool actWidgetReceivers = false;
678            bool actImeService = false;
679            bool actWallpaperService = false;
680
681            // These two implement the implicit permissions that are granted
682            // to pre-1.6 applications.
683            bool hasWriteExternalStoragePermission = false;
684            bool hasReadPhoneStatePermission = false;
685
686            // If an app requests write storage, they will also get read storage.
687            bool hasReadExternalStoragePermission = false;
688
689            // Implement transition to read and write call log.
690            bool hasReadContactsPermission = false;
691            bool hasWriteContactsPermission = false;
692            bool hasReadCallLogPermission = false;
693            bool hasWriteCallLogPermission = false;
694
695            // This next group of variables is used to implement a group of
696            // backward-compatibility heuristics necessitated by the addition of
697            // some new uses-feature constants in 2.1 and 2.2. In most cases, the
698            // heuristic is "if an app requests a permission but doesn't explicitly
699            // request the corresponding <uses-feature>, presume it's there anyway".
700            bool specCameraFeature = false; // camera-related
701            bool specCameraAutofocusFeature = false;
702            bool reqCameraAutofocusFeature = false;
703            bool reqCameraFlashFeature = false;
704            bool hasCameraPermission = false;
705            bool specLocationFeature = false; // location-related
706            bool specNetworkLocFeature = false;
707            bool reqNetworkLocFeature = false;
708            bool specGpsFeature = false;
709            bool reqGpsFeature = false;
710            bool hasMockLocPermission = false;
711            bool hasCoarseLocPermission = false;
712            bool hasGpsPermission = false;
713            bool hasGeneralLocPermission = false;
714            bool specBluetoothFeature = false; // Bluetooth API-related
715            bool hasBluetoothPermission = false;
716            bool specMicrophoneFeature = false; // microphone-related
717            bool hasRecordAudioPermission = false;
718            bool specWiFiFeature = false;
719            bool hasWiFiPermission = false;
720            bool specTelephonyFeature = false; // telephony-related
721            bool reqTelephonySubFeature = false;
722            bool hasTelephonyPermission = false;
723            bool specTouchscreenFeature = false; // touchscreen-related
724            bool specMultitouchFeature = false;
725            bool reqDistinctMultitouchFeature = false;
726            bool specScreenPortraitFeature = false;
727            bool specScreenLandscapeFeature = false;
728            bool reqScreenPortraitFeature = false;
729            bool reqScreenLandscapeFeature = false;
730            // 2.2 also added some other features that apps can request, but that
731            // have no corresponding permission, so we cannot implement any
732            // back-compatibility heuristic for them. The below are thus unnecessary
733            // (but are retained here for documentary purposes.)
734            //bool specCompassFeature = false;
735            //bool specAccelerometerFeature = false;
736            //bool specProximityFeature = false;
737            //bool specAmbientLightFeature = false;
738            //bool specLiveWallpaperFeature = false;
739
740            int targetSdk = 0;
741            int smallScreen = 1;
742            int normalScreen = 1;
743            int largeScreen = 1;
744            int xlargeScreen = 1;
745            int anyDensity = 1;
746            int requiresSmallestWidthDp = 0;
747            int compatibleWidthLimitDp = 0;
748            int largestWidthLimitDp = 0;
749            String8 pkg;
750            String8 activityName;
751            String8 activityLabel;
752            String8 activityIcon;
753            String8 receiverName;
754            String8 serviceName;
755            while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
756                if (code == ResXMLTree::END_TAG) {
757                    depth--;
758                    if (depth < 2) {
759                        withinApplication = false;
760                    } else if (depth < 3) {
761                        if (withinActivity && isMainActivity && isLauncherActivity) {
762                            const char *aName = getComponentName(pkg, activityName);
763                            printf("launchable-activity:");
764                            if (aName != NULL) {
765                                printf(" name='%s' ", aName);
766                            }
767                            printf(" label='%s' icon='%s'\n",
768                                    activityLabel.string(),
769                                    activityIcon.string());
770                        }
771                        if (!hasIntentFilter) {
772                            hasOtherActivities |= withinActivity;
773                            hasOtherReceivers |= withinReceiver;
774                            hasOtherServices |= withinService;
775                        }
776                        withinActivity = false;
777                        withinService = false;
778                        withinReceiver = false;
779                        hasIntentFilter = false;
780                        isMainActivity = isLauncherActivity = false;
781                    } else if (depth < 4) {
782                        if (withinIntentFilter) {
783                            if (withinActivity) {
784                                hasMainActivity |= actMainActivity;
785                                hasOtherActivities |= !actMainActivity;
786                            } else if (withinReceiver) {
787                                hasWidgetReceivers |= actWidgetReceivers;
788                                hasOtherReceivers |= !actWidgetReceivers;
789                            } else if (withinService) {
790                                hasImeService |= actImeService;
791                                hasWallpaperService |= actWallpaperService;
792                                hasOtherServices |= (!actImeService && !actWallpaperService);
793                            }
794                        }
795                        withinIntentFilter = false;
796                    }
797                    continue;
798                }
799                if (code != ResXMLTree::START_TAG) {
800                    continue;
801                }
802                depth++;
803                String8 tag(tree.getElementName(&len));
804                //printf("Depth %d,  %s\n", depth, tag.string());
805                if (depth == 1) {
806                    if (tag != "manifest") {
807                        fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
808                        goto bail;
809                    }
810                    pkg = getAttribute(tree, NULL, "package", NULL);
811                    printf("package: name='%s' ", pkg.string());
812                    int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
813                    if (error != "") {
814                        fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
815                        goto bail;
816                    }
817                    if (versionCode > 0) {
818                        printf("versionCode='%d' ", versionCode);
819                    } else {
820                        printf("versionCode='' ");
821                    }
822                    String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
823                    if (error != "") {
824                        fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
825                        goto bail;
826                    }
827                    printf("versionName='%s'\n", versionName.string());
828                } else if (depth == 2) {
829                    withinApplication = false;
830                    if (tag == "application") {
831                        withinApplication = true;
832
833                        String8 label;
834                        const size_t NL = locales.size();
835                        for (size_t i=0; i<NL; i++) {
836                            const char* localeStr =  locales[i].string();
837                            assets.setLocale(localeStr != NULL ? localeStr : "");
838                            String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
839                            if (llabel != "") {
840                                if (localeStr == NULL || strlen(localeStr) == 0) {
841                                    label = llabel;
842                                    printf("application-label:'%s'\n", llabel.string());
843                                } else {
844                                    if (label == "") {
845                                        label = llabel;
846                                    }
847                                    printf("application-label-%s:'%s'\n", localeStr,
848                                            llabel.string());
849                                }
850                            }
851                        }
852
853                        ResTable_config tmpConfig = config;
854                        const size_t ND = densities.size();
855                        for (size_t i=0; i<ND; i++) {
856                            tmpConfig.density = densities[i];
857                            assets.setConfiguration(tmpConfig);
858                            String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
859                            if (icon != "") {
860                                printf("application-icon-%d:'%s'\n", densities[i], icon.string());
861                            }
862                        }
863                        assets.setConfiguration(config);
864
865                        String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
866                        if (error != "") {
867                            fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
868                            goto bail;
869                        }
870                        int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
871                        if (error != "") {
872                            fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
873                            goto bail;
874                        }
875                        printf("application: label='%s' ", label.string());
876                        printf("icon='%s'\n", icon.string());
877                        if (testOnly != 0) {
878                            printf("testOnly='%d'\n", testOnly);
879                        }
880
881                        int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0);
882                        if (error != "") {
883                            fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string());
884                            goto bail;
885                        }
886                        if (debuggable != 0) {
887                            printf("application-debuggable\n");
888                        }
889                    } else if (tag == "uses-sdk") {
890                        int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
891                        if (error != "") {
892                            error = "";
893                            String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
894                            if (error != "") {
895                                fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
896                                        error.string());
897                                goto bail;
898                            }
899                            if (name == "Donut") targetSdk = 4;
900                            printf("sdkVersion:'%s'\n", name.string());
901                        } else if (code != -1) {
902                            targetSdk = code;
903                            printf("sdkVersion:'%d'\n", code);
904                        }
905                        code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
906                        if (code != -1) {
907                            printf("maxSdkVersion:'%d'\n", code);
908                        }
909                        code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
910                        if (error != "") {
911                            error = "";
912                            String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
913                            if (error != "") {
914                                fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
915                                        error.string());
916                                goto bail;
917                            }
918                            if (name == "Donut" && targetSdk < 4) targetSdk = 4;
919                            printf("targetSdkVersion:'%s'\n", name.string());
920                        } else if (code != -1) {
921                            if (targetSdk < code) {
922                                targetSdk = code;
923                            }
924                            printf("targetSdkVersion:'%d'\n", code);
925                        }
926                    } else if (tag == "uses-configuration") {
927                        int32_t reqTouchScreen = getIntegerAttribute(tree,
928                                REQ_TOUCH_SCREEN_ATTR, NULL, 0);
929                        int32_t reqKeyboardType = getIntegerAttribute(tree,
930                                REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
931                        int32_t reqHardKeyboard = getIntegerAttribute(tree,
932                                REQ_HARD_KEYBOARD_ATTR, NULL, 0);
933                        int32_t reqNavigation = getIntegerAttribute(tree,
934                                REQ_NAVIGATION_ATTR, NULL, 0);
935                        int32_t reqFiveWayNav = getIntegerAttribute(tree,
936                                REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
937                        printf("uses-configuration:");
938                        if (reqTouchScreen != 0) {
939                            printf(" reqTouchScreen='%d'", reqTouchScreen);
940                        }
941                        if (reqKeyboardType != 0) {
942                            printf(" reqKeyboardType='%d'", reqKeyboardType);
943                        }
944                        if (reqHardKeyboard != 0) {
945                            printf(" reqHardKeyboard='%d'", reqHardKeyboard);
946                        }
947                        if (reqNavigation != 0) {
948                            printf(" reqNavigation='%d'", reqNavigation);
949                        }
950                        if (reqFiveWayNav != 0) {
951                            printf(" reqFiveWayNav='%d'", reqFiveWayNav);
952                        }
953                        printf("\n");
954                    } else if (tag == "supports-screens") {
955                        smallScreen = getIntegerAttribute(tree,
956                                SMALL_SCREEN_ATTR, NULL, 1);
957                        normalScreen = getIntegerAttribute(tree,
958                                NORMAL_SCREEN_ATTR, NULL, 1);
959                        largeScreen = getIntegerAttribute(tree,
960                                LARGE_SCREEN_ATTR, NULL, 1);
961                        xlargeScreen = getIntegerAttribute(tree,
962                                XLARGE_SCREEN_ATTR, NULL, 1);
963                        anyDensity = getIntegerAttribute(tree,
964                                ANY_DENSITY_ATTR, NULL, 1);
965                        requiresSmallestWidthDp = getIntegerAttribute(tree,
966                                REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
967                        compatibleWidthLimitDp = getIntegerAttribute(tree,
968                                COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
969                        largestWidthLimitDp = getIntegerAttribute(tree,
970                                LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
971                    } else if (tag == "uses-feature") {
972                        String8 name = getAttribute(tree, NAME_ATTR, &error);
973
974                        if (name != "" && error == "") {
975                            int req = getIntegerAttribute(tree,
976                                    REQUIRED_ATTR, NULL, 1);
977
978                            if (name == "android.hardware.camera") {
979                                specCameraFeature = true;
980                            } else if (name == "android.hardware.camera.autofocus") {
981                                // these have no corresponding permission to check for,
982                                // but should imply the foundational camera permission
983                                reqCameraAutofocusFeature = reqCameraAutofocusFeature || req;
984                                specCameraAutofocusFeature = true;
985                            } else if (req && (name == "android.hardware.camera.flash")) {
986                                // these have no corresponding permission to check for,
987                                // but should imply the foundational camera permission
988                                reqCameraFlashFeature = true;
989                            } else if (name == "android.hardware.location") {
990                                specLocationFeature = true;
991                            } else if (name == "android.hardware.location.network") {
992                                specNetworkLocFeature = true;
993                                reqNetworkLocFeature = reqNetworkLocFeature || req;
994                            } else if (name == "android.hardware.location.gps") {
995                                specGpsFeature = true;
996                                reqGpsFeature = reqGpsFeature || req;
997                            } else if (name == "android.hardware.bluetooth") {
998                                specBluetoothFeature = true;
999                            } else if (name == "android.hardware.touchscreen") {
1000                                specTouchscreenFeature = true;
1001                            } else if (name == "android.hardware.touchscreen.multitouch") {
1002                                specMultitouchFeature = true;
1003                            } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
1004                                reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req;
1005                            } else if (name == "android.hardware.microphone") {
1006                                specMicrophoneFeature = true;
1007                            } else if (name == "android.hardware.wifi") {
1008                                specWiFiFeature = true;
1009                            } else if (name == "android.hardware.telephony") {
1010                                specTelephonyFeature = true;
1011                            } else if (req && (name == "android.hardware.telephony.gsm" ||
1012                                               name == "android.hardware.telephony.cdma")) {
1013                                // these have no corresponding permission to check for,
1014                                // but should imply the foundational telephony permission
1015                                reqTelephonySubFeature = true;
1016                            } else if (name == "android.hardware.screen.portrait") {
1017                                specScreenPortraitFeature = true;
1018                            } else if (name == "android.hardware.screen.landscape") {
1019                                specScreenLandscapeFeature = true;
1020                            }
1021                            printf("uses-feature%s:'%s'\n",
1022                                    req ? "" : "-not-required", name.string());
1023                        } else {
1024                            int vers = getIntegerAttribute(tree,
1025                                    GL_ES_VERSION_ATTR, &error);
1026                            if (error == "") {
1027                                printf("uses-gl-es:'0x%x'\n", vers);
1028                            }
1029                        }
1030                    } else if (tag == "uses-permission") {
1031                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1032                        if (name != "" && error == "") {
1033                            if (name == "android.permission.CAMERA") {
1034                                hasCameraPermission = true;
1035                            } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
1036                                hasGpsPermission = true;
1037                            } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
1038                                hasMockLocPermission = true;
1039                            } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
1040                                hasCoarseLocPermission = true;
1041                            } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
1042                                       name == "android.permission.INSTALL_LOCATION_PROVIDER") {
1043                                hasGeneralLocPermission = true;
1044                            } else if (name == "android.permission.BLUETOOTH" ||
1045                                       name == "android.permission.BLUETOOTH_ADMIN") {
1046                                hasBluetoothPermission = true;
1047                            } else if (name == "android.permission.RECORD_AUDIO") {
1048                                hasRecordAudioPermission = true;
1049                            } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1050                                       name == "android.permission.CHANGE_WIFI_STATE" ||
1051                                       name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
1052                                hasWiFiPermission = true;
1053                            } else if (name == "android.permission.CALL_PHONE" ||
1054                                       name == "android.permission.CALL_PRIVILEGED" ||
1055                                       name == "android.permission.MODIFY_PHONE_STATE" ||
1056                                       name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1057                                       name == "android.permission.READ_SMS" ||
1058                                       name == "android.permission.RECEIVE_SMS" ||
1059                                       name == "android.permission.RECEIVE_MMS" ||
1060                                       name == "android.permission.RECEIVE_WAP_PUSH" ||
1061                                       name == "android.permission.SEND_SMS" ||
1062                                       name == "android.permission.WRITE_APN_SETTINGS" ||
1063                                       name == "android.permission.WRITE_SMS") {
1064                                hasTelephonyPermission = true;
1065                            } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1066                                hasWriteExternalStoragePermission = true;
1067                            } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1068                                hasReadExternalStoragePermission = true;
1069                            } else if (name == "android.permission.READ_PHONE_STATE") {
1070                                hasReadPhoneStatePermission = true;
1071                            } else if (name == "android.permission.READ_CONTACTS") {
1072                                hasReadContactsPermission = true;
1073                            } else if (name == "android.permission.WRITE_CONTACTS") {
1074                                hasWriteContactsPermission = true;
1075                            } else if (name == "android.permission.READ_CALL_LOG") {
1076                                hasReadCallLogPermission = true;
1077                            } else if (name == "android.permission.WRITE_CALL_LOG") {
1078                                hasWriteCallLogPermission = true;
1079                            }
1080                            printf("uses-permission:'%s'\n", name.string());
1081                            int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
1082                            if (!req) {
1083                                printf("optional-permission:'%s'\n", name.string());
1084                            }
1085                        } else {
1086                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1087                                    error.string());
1088                            goto bail;
1089                        }
1090                    } else if (tag == "uses-package") {
1091                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1092                        if (name != "" && error == "") {
1093                            printf("uses-package:'%s'\n", name.string());
1094                        } else {
1095                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1096                                    error.string());
1097                                goto bail;
1098                        }
1099                    } else if (tag == "original-package") {
1100                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1101                        if (name != "" && error == "") {
1102                            printf("original-package:'%s'\n", name.string());
1103                        } else {
1104                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1105                                    error.string());
1106                                goto bail;
1107                        }
1108                    } else if (tag == "supports-gl-texture") {
1109                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1110                        if (name != "" && error == "") {
1111                            printf("supports-gl-texture:'%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 == "compatible-screens") {
1118                        printCompatibleScreens(tree);
1119                        depth--;
1120                    } else if (tag == "package-verifier") {
1121                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1122                        if (name != "" && error == "") {
1123                            String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
1124                            if (publicKey != "" && error == "") {
1125                                printf("package-verifier: name='%s' publicKey='%s'\n",
1126                                        name.string(), publicKey.string());
1127                            }
1128                        }
1129                    }
1130                } else if (depth == 3 && withinApplication) {
1131                    withinActivity = false;
1132                    withinReceiver = false;
1133                    withinService = false;
1134                    hasIntentFilter = false;
1135                    if(tag == "activity") {
1136                        withinActivity = true;
1137                        activityName = getAttribute(tree, NAME_ATTR, &error);
1138                        if (error != "") {
1139                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1140                            goto bail;
1141                        }
1142
1143                        activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1144                        if (error != "") {
1145                            fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string());
1146                            goto bail;
1147                        }
1148
1149                        activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1150                        if (error != "") {
1151                            fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
1152                            goto bail;
1153                        }
1154
1155                        int32_t orien = getResolvedIntegerAttribute(&res, tree,
1156                                SCREEN_ORIENTATION_ATTR, &error);
1157                        if (error == "") {
1158                            if (orien == 0 || orien == 6 || orien == 8) {
1159                                // Requests landscape, sensorLandscape, or reverseLandscape.
1160                                reqScreenLandscapeFeature = true;
1161                            } else if (orien == 1 || orien == 7 || orien == 9) {
1162                                // Requests portrait, sensorPortrait, or reversePortrait.
1163                                reqScreenPortraitFeature = true;
1164                            }
1165                        }
1166                    } else if (tag == "uses-library") {
1167                        String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1168                        if (error != "") {
1169                            fprintf(stderr, "ERROR getting 'android:name' attribute for uses-library: %s\n", error.string());
1170                            goto bail;
1171                        }
1172                        int req = getIntegerAttribute(tree,
1173                                REQUIRED_ATTR, NULL, 1);
1174                        printf("uses-library%s:'%s'\n",
1175                                req ? "" : "-not-required", libraryName.string());
1176                    } else if (tag == "receiver") {
1177                        withinReceiver = true;
1178                        receiverName = getAttribute(tree, NAME_ATTR, &error);
1179
1180                        if (error != "") {
1181                            fprintf(stderr, "ERROR getting 'android:name' attribute for "
1182                                    "receiver:%s\n", error.string());
1183                            goto bail;
1184                        }
1185                    } else if (tag == "service") {
1186                        withinService = true;
1187                        serviceName = getAttribute(tree, NAME_ATTR, &error);
1188
1189                        if (error != "") {
1190                            fprintf(stderr, "ERROR getting 'android:name' attribute for "
1191                                    "service:%s\n", error.string());
1192                            goto bail;
1193                        }
1194                    } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
1195                        String8 metaDataName = getAttribute(tree, NAME_ATTR, &error);
1196                        if (error != "") {
1197                            fprintf(stderr, "ERROR getting 'android:name' attribute for "
1198                                    "meta-data:%s\n", error.string());
1199                            goto bail;
1200                        }
1201                        printf("meta-data: name='%s' ", metaDataName.string());
1202                        Res_value value;
1203                        getResolvedResourceAttribute(&value, &res, tree, VALUE_ATTR, &error);
1204                        if (error != "") {
1205                            fprintf(stderr, "ERROR getting 'android:value' attribute for "
1206                                    "meta-data:%s\n", error.string());
1207                            goto bail;
1208                        }
1209                        if (value.dataType == Res_value::TYPE_STRING) {
1210                            String8 metaDataValue = getAttribute(tree, value.data, &error);
1211                            if (error != "") {
1212                                fprintf(stderr, "ERROR getting 'android:value' attribute for "
1213                                        "meta-data: %s\n", error.string());
1214                                goto bail;
1215                            }
1216                            printf("value='%s'\n", metaDataValue.string());
1217                        } else if (Res_value::TYPE_FIRST_INT <= value.dataType &&
1218                                value.dataType <= Res_value::TYPE_LAST_INT) {
1219                            printf("value='%d'\n", value.data);
1220                        } else {
1221                            printf("value=(type 0x%x)0x%x", (int)value.dataType, (int)value.data);
1222                        }
1223                    }
1224                } else if ((depth == 4) && (tag == "intent-filter")) {
1225                    hasIntentFilter = true;
1226                    withinIntentFilter = true;
1227                    actMainActivity = actWidgetReceivers = actImeService = actWallpaperService =
1228                            false;
1229                } else if ((depth == 5) && withinIntentFilter) {
1230                    String8 action;
1231                    if (tag == "action") {
1232                        action = getAttribute(tree, NAME_ATTR, &error);
1233                        if (error != "") {
1234                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1235                                    error.string());
1236                            goto bail;
1237                        }
1238                        if (withinActivity) {
1239                            if (action == "android.intent.action.MAIN") {
1240                                isMainActivity = true;
1241                                actMainActivity = true;
1242                            }
1243                        } else if (withinReceiver) {
1244                            if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1245                                actWidgetReceivers = true;
1246                            }
1247                        } else if (withinService) {
1248                            if (action == "android.view.InputMethod") {
1249                                actImeService = true;
1250                            } else if (action == "android.service.wallpaper.WallpaperService") {
1251                                actWallpaperService = true;
1252                            }
1253                        }
1254                        if (action == "android.intent.action.SEARCH") {
1255                            isSearchable = true;
1256                        }
1257                    }
1258
1259                    if (tag == "category") {
1260                        String8 category = getAttribute(tree, NAME_ATTR, &error);
1261                        if (error != "") {
1262                            fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string());
1263                            goto bail;
1264                        }
1265                        if (withinActivity) {
1266                            if (category == "android.intent.category.LAUNCHER") {
1267                                isLauncherActivity = true;
1268                            }
1269                        }
1270                    }
1271                }
1272            }
1273
1274            // Pre-1.6 implicitly granted permission compatibility logic
1275            if (targetSdk < 4) {
1276                if (!hasWriteExternalStoragePermission) {
1277                    printf("uses-permission:'android.permission.WRITE_EXTERNAL_STORAGE'\n");
1278                    printf("uses-implied-permission:'android.permission.WRITE_EXTERNAL_STORAGE'," \
1279                            "'targetSdkVersion < 4'\n");
1280                    hasWriteExternalStoragePermission = true;
1281                }
1282                if (!hasReadPhoneStatePermission) {
1283                    printf("uses-permission:'android.permission.READ_PHONE_STATE'\n");
1284                    printf("uses-implied-permission:'android.permission.READ_PHONE_STATE'," \
1285                            "'targetSdkVersion < 4'\n");
1286                }
1287            }
1288
1289            // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1290            // force them to always take READ_EXTERNAL_STORAGE as well.  We always
1291            // do this (regardless of target API version) because we can't have
1292            // an app with write permission but not read permission.
1293            if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
1294                printf("uses-permission:'android.permission.READ_EXTERNAL_STORAGE'\n");
1295                printf("uses-implied-permission:'android.permission.READ_EXTERNAL_STORAGE'," \
1296                        "'requested WRITE_EXTERNAL_STORAGE'\n");
1297            }
1298
1299            // Pre-JellyBean call log permission compatibility.
1300            if (targetSdk < 16) {
1301                if (!hasReadCallLogPermission && hasReadContactsPermission) {
1302                    printf("uses-permission:'android.permission.READ_CALL_LOG'\n");
1303                    printf("uses-implied-permission:'android.permission.READ_CALL_LOG'," \
1304                            "'targetSdkVersion < 16 and requested READ_CONTACTS'\n");
1305                }
1306                if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
1307                    printf("uses-permission:'android.permission.WRITE_CALL_LOG'\n");
1308                    printf("uses-implied-permission:'android.permission.WRITE_CALL_LOG'," \
1309                            "'targetSdkVersion < 16 and requested WRITE_CONTACTS'\n");
1310                }
1311            }
1312
1313            /* The following blocks handle printing "inferred" uses-features, based
1314             * on whether related features or permissions are used by the app.
1315             * Note that the various spec*Feature variables denote whether the
1316             * relevant tag was *present* in the AndroidManfest, not that it was
1317             * present and set to true.
1318             */
1319            // Camera-related back-compatibility logic
1320            if (!specCameraFeature) {
1321                if (reqCameraFlashFeature) {
1322                    // if app requested a sub-feature (autofocus or flash) and didn't
1323                    // request the base camera feature, we infer that it meant to
1324                    printf("uses-feature:'android.hardware.camera'\n");
1325                    printf("uses-implied-feature:'android.hardware.camera'," \
1326                            "'requested android.hardware.camera.flash feature'\n");
1327                } else if (reqCameraAutofocusFeature) {
1328                    // if app requested a sub-feature (autofocus or flash) and didn't
1329                    // request the base camera feature, we infer that it meant to
1330                    printf("uses-feature:'android.hardware.camera'\n");
1331                    printf("uses-implied-feature:'android.hardware.camera'," \
1332                            "'requested android.hardware.camera.autofocus feature'\n");
1333                } else if (hasCameraPermission) {
1334                    // if app wants to use camera but didn't request the feature, we infer
1335                    // that it meant to, and further that it wants autofocus
1336                    // (which was the 1.0 - 1.5 behavior)
1337                    printf("uses-feature:'android.hardware.camera'\n");
1338                    if (!specCameraAutofocusFeature) {
1339                        printf("uses-feature:'android.hardware.camera.autofocus'\n");
1340                        printf("uses-implied-feature:'android.hardware.camera.autofocus'," \
1341                                "'requested android.permission.CAMERA permission'\n");
1342                    }
1343                }
1344            }
1345
1346            // Location-related back-compatibility logic
1347            if (!specLocationFeature &&
1348                (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission ||
1349                 hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) {
1350                // if app either takes a location-related permission or requests one of the
1351                // sub-features, we infer that it also meant to request the base location feature
1352                printf("uses-feature:'android.hardware.location'\n");
1353                printf("uses-implied-feature:'android.hardware.location'," \
1354                        "'requested a location access permission'\n");
1355            }
1356            if (!specGpsFeature && hasGpsPermission) {
1357                // if app takes GPS (FINE location) perm but does not request the GPS
1358                // feature, we infer that it meant to
1359                printf("uses-feature:'android.hardware.location.gps'\n");
1360                printf("uses-implied-feature:'android.hardware.location.gps'," \
1361                        "'requested android.permission.ACCESS_FINE_LOCATION permission'\n");
1362            }
1363            if (!specNetworkLocFeature && hasCoarseLocPermission) {
1364                // if app takes Network location (COARSE location) perm but does not request the
1365                // network location feature, we infer that it meant to
1366                printf("uses-feature:'android.hardware.location.network'\n");
1367                printf("uses-implied-feature:'android.hardware.location.network'," \
1368                        "'requested android.permission.ACCESS_COARSE_LOCATION permission'\n");
1369            }
1370
1371            // Bluetooth-related compatibility logic
1372            if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
1373                // if app takes a Bluetooth permission but does not request the Bluetooth
1374                // feature, we infer that it meant to
1375                printf("uses-feature:'android.hardware.bluetooth'\n");
1376                printf("uses-implied-feature:'android.hardware.bluetooth'," \
1377                        "'requested android.permission.BLUETOOTH or android.permission.BLUETOOTH_ADMIN " \
1378                        "permission and targetSdkVersion > 4'\n");
1379            }
1380
1381            // Microphone-related compatibility logic
1382            if (!specMicrophoneFeature && hasRecordAudioPermission) {
1383                // if app takes the record-audio permission but does not request the microphone
1384                // feature, we infer that it meant to
1385                printf("uses-feature:'android.hardware.microphone'\n");
1386                printf("uses-implied-feature:'android.hardware.microphone'," \
1387                        "'requested android.permission.RECORD_AUDIO permission'\n");
1388            }
1389
1390            // WiFi-related compatibility logic
1391            if (!specWiFiFeature && hasWiFiPermission) {
1392                // if app takes one of the WiFi permissions but does not request the WiFi
1393                // feature, we infer that it meant to
1394                printf("uses-feature:'android.hardware.wifi'\n");
1395                printf("uses-implied-feature:'android.hardware.wifi'," \
1396                        "'requested android.permission.ACCESS_WIFI_STATE, " \
1397                        "android.permission.CHANGE_WIFI_STATE, or " \
1398                        "android.permission.CHANGE_WIFI_MULTICAST_STATE permission'\n");
1399            }
1400
1401            // Telephony-related compatibility logic
1402            if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) {
1403                // if app takes one of the telephony permissions or requests a sub-feature but
1404                // does not request the base telephony feature, we infer that it meant to
1405                printf("uses-feature:'android.hardware.telephony'\n");
1406                printf("uses-implied-feature:'android.hardware.telephony'," \
1407                        "'requested a telephony-related permission or feature'\n");
1408            }
1409
1410            // Touchscreen-related back-compatibility logic
1411            if (!specTouchscreenFeature) { // not a typo!
1412                // all apps are presumed to require a touchscreen, unless they explicitly say
1413                // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
1414                // Note that specTouchscreenFeature is true if the tag is present, regardless
1415                // of whether its value is true or false, so this is safe
1416                printf("uses-feature:'android.hardware.touchscreen'\n");
1417                printf("uses-implied-feature:'android.hardware.touchscreen'," \
1418                        "'assumed you require a touch screen unless explicitly made optional'\n");
1419            }
1420            if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
1421                // if app takes one of the telephony permissions or requests a sub-feature but
1422                // does not request the base telephony feature, we infer that it meant to
1423                printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
1424                printf("uses-implied-feature:'android.hardware.touchscreen.multitouch'," \
1425                        "'requested android.hardware.touchscreen.multitouch.distinct feature'\n");
1426            }
1427
1428            // Landscape/portrait-related compatibility logic
1429            if (!specScreenLandscapeFeature && !specScreenPortraitFeature) {
1430                // If the app has specified any activities in its manifest
1431                // that request a specific orientation, then assume that
1432                // orientation is required.
1433                if (reqScreenLandscapeFeature) {
1434                    printf("uses-feature:'android.hardware.screen.landscape'\n");
1435                    printf("uses-implied-feature:'android.hardware.screen.landscape'," \
1436                            "'one or more activities have specified a landscape orientation'\n");
1437                }
1438                if (reqScreenPortraitFeature) {
1439                    printf("uses-feature:'android.hardware.screen.portrait'\n");
1440                    printf("uses-implied-feature:'android.hardware.screen.portrait'," \
1441                            "'one or more activities have specified a portrait orientation'\n");
1442                }
1443            }
1444
1445            if (hasMainActivity) {
1446                printf("main\n");
1447            }
1448            if (hasWidgetReceivers) {
1449                printf("app-widget\n");
1450            }
1451            if (hasImeService) {
1452                printf("ime\n");
1453            }
1454            if (hasWallpaperService) {
1455                printf("wallpaper\n");
1456            }
1457            if (hasOtherActivities) {
1458                printf("other-activities\n");
1459            }
1460            if (isSearchable) {
1461                printf("search\n");
1462            }
1463            if (hasOtherReceivers) {
1464                printf("other-receivers\n");
1465            }
1466            if (hasOtherServices) {
1467                printf("other-services\n");
1468            }
1469
1470            // For modern apps, if screen size buckets haven't been specified
1471            // but the new width ranges have, then infer the buckets from them.
1472            if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1473                    && requiresSmallestWidthDp > 0) {
1474                int compatWidth = compatibleWidthLimitDp;
1475                if (compatWidth <= 0) {
1476                    compatWidth = requiresSmallestWidthDp;
1477                }
1478                if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1479                    smallScreen = -1;
1480                } else {
1481                    smallScreen = 0;
1482                }
1483                if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1484                    normalScreen = -1;
1485                } else {
1486                    normalScreen = 0;
1487                }
1488                if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1489                    largeScreen = -1;
1490                } else {
1491                    largeScreen = 0;
1492                }
1493                if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1494                    xlargeScreen = -1;
1495                } else {
1496                    xlargeScreen = 0;
1497                }
1498            }
1499
1500            // Determine default values for any unspecified screen sizes,
1501            // based on the target SDK of the package.  As of 4 (donut)
1502            // the screen size support was introduced, so all default to
1503            // enabled.
1504            if (smallScreen > 0) {
1505                smallScreen = targetSdk >= 4 ? -1 : 0;
1506            }
1507            if (normalScreen > 0) {
1508                normalScreen = -1;
1509            }
1510            if (largeScreen > 0) {
1511                largeScreen = targetSdk >= 4 ? -1 : 0;
1512            }
1513            if (xlargeScreen > 0) {
1514                // Introduced in Gingerbread.
1515                xlargeScreen = targetSdk >= 9 ? -1 : 0;
1516            }
1517            if (anyDensity > 0) {
1518                anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1519                        || compatibleWidthLimitDp > 0) ? -1 : 0;
1520            }
1521            printf("supports-screens:");
1522            if (smallScreen != 0) {
1523                printf(" 'small'");
1524            }
1525            if (normalScreen != 0) {
1526                printf(" 'normal'");
1527            }
1528            if (largeScreen != 0) {
1529                printf(" 'large'");
1530            }
1531            if (xlargeScreen != 0) {
1532                printf(" 'xlarge'");
1533            }
1534            printf("\n");
1535            printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
1536            if (requiresSmallestWidthDp > 0) {
1537                printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1538            }
1539            if (compatibleWidthLimitDp > 0) {
1540                printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1541            }
1542            if (largestWidthLimitDp > 0) {
1543                printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1544            }
1545
1546            printf("locales:");
1547            const size_t NL = locales.size();
1548            for (size_t i=0; i<NL; i++) {
1549                const char* localeStr =  locales[i].string();
1550                if (localeStr == NULL || strlen(localeStr) == 0) {
1551                    localeStr = "--_--";
1552                }
1553                printf(" '%s'", localeStr);
1554            }
1555            printf("\n");
1556
1557            printf("densities:");
1558            const size_t ND = densities.size();
1559            for (size_t i=0; i<ND; i++) {
1560                printf(" '%d'", densities[i]);
1561            }
1562            printf("\n");
1563
1564            AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1565            if (dir != NULL) {
1566                if (dir->getFileCount() > 0) {
1567                    printf("native-code:");
1568                    for (size_t i=0; i<dir->getFileCount(); i++) {
1569                        printf(" '%s'", dir->getFileName(i).string());
1570                    }
1571                    printf("\n");
1572                }
1573                delete dir;
1574            }
1575        } else if (strcmp("badger", option) == 0) {
1576            printf("%s", CONSOLE_DATA);
1577        } else if (strcmp("configurations", option) == 0) {
1578            Vector<ResTable_config> configs;
1579            res.getConfigurations(&configs);
1580            const size_t N = configs.size();
1581            for (size_t i=0; i<N; i++) {
1582                printf("%s\n", configs[i].toString().string());
1583            }
1584        } else {
1585            fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
1586            goto bail;
1587        }
1588    }
1589
1590    result = NO_ERROR;
1591
1592bail:
1593    if (asset) {
1594        delete asset;
1595    }
1596    return (result != NO_ERROR);
1597}
1598
1599
1600/*
1601 * Handle the "add" command, which wants to add files to a new or
1602 * pre-existing archive.
1603 */
1604int doAdd(Bundle* bundle)
1605{
1606    ZipFile* zip = NULL;
1607    status_t result = UNKNOWN_ERROR;
1608    const char* zipFileName;
1609
1610    if (bundle->getUpdate()) {
1611        /* avoid confusion */
1612        fprintf(stderr, "ERROR: can't use '-u' with add\n");
1613        goto bail;
1614    }
1615
1616    if (bundle->getFileSpecCount() < 1) {
1617        fprintf(stderr, "ERROR: must specify zip file name\n");
1618        goto bail;
1619    }
1620    zipFileName = bundle->getFileSpecEntry(0);
1621
1622    if (bundle->getFileSpecCount() < 2) {
1623        fprintf(stderr, "NOTE: nothing to do\n");
1624        goto bail;
1625    }
1626
1627    zip = openReadWrite(zipFileName, true);
1628    if (zip == NULL) {
1629        fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
1630        goto bail;
1631    }
1632
1633    for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1634        const char* fileName = bundle->getFileSpecEntry(i);
1635
1636        if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
1637            printf(" '%s'... (from gzip)\n", fileName);
1638            result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
1639        } else {
1640            if (bundle->getJunkPath()) {
1641                String8 storageName = String8(fileName).getPathLeaf();
1642                printf(" '%s' as '%s'...\n", fileName, storageName.string());
1643                result = zip->add(fileName, storageName.string(),
1644                                  bundle->getCompressionMethod(), NULL);
1645            } else {
1646                printf(" '%s'...\n", fileName);
1647                result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
1648            }
1649        }
1650        if (result != NO_ERROR) {
1651            fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
1652            if (result == NAME_NOT_FOUND) {
1653                fprintf(stderr, ": file not found\n");
1654            } else if (result == ALREADY_EXISTS) {
1655                fprintf(stderr, ": already exists in archive\n");
1656            } else {
1657                fprintf(stderr, "\n");
1658            }
1659            goto bail;
1660        }
1661    }
1662
1663    result = NO_ERROR;
1664
1665bail:
1666    delete zip;
1667    return (result != NO_ERROR);
1668}
1669
1670
1671/*
1672 * Delete files from an existing archive.
1673 */
1674int doRemove(Bundle* bundle)
1675{
1676    ZipFile* zip = NULL;
1677    status_t result = UNKNOWN_ERROR;
1678    const char* zipFileName;
1679
1680    if (bundle->getFileSpecCount() < 1) {
1681        fprintf(stderr, "ERROR: must specify zip file name\n");
1682        goto bail;
1683    }
1684    zipFileName = bundle->getFileSpecEntry(0);
1685
1686    if (bundle->getFileSpecCount() < 2) {
1687        fprintf(stderr, "NOTE: nothing to do\n");
1688        goto bail;
1689    }
1690
1691    zip = openReadWrite(zipFileName, false);
1692    if (zip == NULL) {
1693        fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
1694            zipFileName);
1695        goto bail;
1696    }
1697
1698    for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1699        const char* fileName = bundle->getFileSpecEntry(i);
1700        ZipEntry* entry;
1701
1702        entry = zip->getEntryByName(fileName);
1703        if (entry == NULL) {
1704            printf(" '%s' NOT FOUND\n", fileName);
1705            continue;
1706        }
1707
1708        result = zip->remove(entry);
1709
1710        if (result != NO_ERROR) {
1711            fprintf(stderr, "Unable to delete '%s' from '%s'\n",
1712                bundle->getFileSpecEntry(i), zipFileName);
1713            goto bail;
1714        }
1715    }
1716
1717    /* update the archive */
1718    zip->flush();
1719
1720bail:
1721    delete zip;
1722    return (result != NO_ERROR);
1723}
1724
1725
1726/*
1727 * Package up an asset directory and associated application files.
1728 */
1729int doPackage(Bundle* bundle)
1730{
1731    const char* outputAPKFile;
1732    int retVal = 1;
1733    status_t err;
1734    sp<AaptAssets> assets;
1735    int N;
1736    FILE* fp;
1737    String8 dependencyFile;
1738
1739    // -c zz_ZZ means do pseudolocalization
1740    ResourceFilter filter;
1741    err = filter.parse(bundle->getConfigurations());
1742    if (err != NO_ERROR) {
1743        goto bail;
1744    }
1745    if (filter.containsPseudo()) {
1746        bundle->setPseudolocalize(true);
1747    }
1748
1749    N = bundle->getFileSpecCount();
1750    if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
1751            && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDir() == NULL) {
1752        fprintf(stderr, "ERROR: no input files\n");
1753        goto bail;
1754    }
1755
1756    outputAPKFile = bundle->getOutputAPKFile();
1757
1758    // Make sure the filenames provided exist and are of the appropriate type.
1759    if (outputAPKFile) {
1760        FileType type;
1761        type = getFileType(outputAPKFile);
1762        if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
1763            fprintf(stderr,
1764                "ERROR: output file '%s' exists but is not regular file\n",
1765                outputAPKFile);
1766            goto bail;
1767        }
1768    }
1769
1770    // Load the assets.
1771    assets = new AaptAssets();
1772
1773    // Set up the resource gathering in assets if we're going to generate
1774    // dependency files. Every time we encounter a resource while slurping
1775    // the tree, we'll add it to these stores so we have full resource paths
1776    // to write to a dependency file.
1777    if (bundle->getGenDependencies()) {
1778        sp<FilePathStore> resPathStore = new FilePathStore;
1779        assets->setFullResPaths(resPathStore);
1780        sp<FilePathStore> assetPathStore = new FilePathStore;
1781        assets->setFullAssetPaths(assetPathStore);
1782    }
1783
1784    err = assets->slurpFromArgs(bundle);
1785    if (err < 0) {
1786        goto bail;
1787    }
1788
1789    if (bundle->getVerbose()) {
1790        assets->print(String8());
1791    }
1792
1793    // If they asked for any fileAs that need to be compiled, do so.
1794    if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
1795        err = buildResources(bundle, assets);
1796        if (err != 0) {
1797            goto bail;
1798        }
1799    }
1800
1801    // At this point we've read everything and processed everything.  From here
1802    // on out it's just writing output files.
1803    if (SourcePos::hasErrors()) {
1804        goto bail;
1805    }
1806
1807    // Update symbols with information about which ones are needed as Java symbols.
1808    assets->applyJavaSymbols();
1809    if (SourcePos::hasErrors()) {
1810        goto bail;
1811    }
1812
1813    // If we've been asked to generate a dependency file, do that here
1814    if (bundle->getGenDependencies()) {
1815        // If this is the packaging step, generate the dependency file next to
1816        // the output apk (e.g. bin/resources.ap_.d)
1817        if (outputAPKFile) {
1818            dependencyFile = String8(outputAPKFile);
1819            // Add the .d extension to the dependency file.
1820            dependencyFile.append(".d");
1821        } else {
1822            // Else if this is the R.java dependency generation step,
1823            // generate the dependency file in the R.java package subdirectory
1824            // e.g. gen/com/foo/app/R.java.d
1825            dependencyFile = String8(bundle->getRClassDir());
1826            dependencyFile.appendPath("R.java.d");
1827        }
1828        // Make sure we have a clean dependency file to start with
1829        fp = fopen(dependencyFile, "w");
1830        fclose(fp);
1831    }
1832
1833    // Write out R.java constants
1834    if (!assets->havePrivateSymbols()) {
1835        if (bundle->getCustomPackage() == NULL) {
1836            // Write the R.java file into the appropriate class directory
1837            // e.g. gen/com/foo/app/R.java
1838            err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
1839        } else {
1840            const String8 customPkg(bundle->getCustomPackage());
1841            err = writeResourceSymbols(bundle, assets, customPkg, true);
1842        }
1843        if (err < 0) {
1844            goto bail;
1845        }
1846        // If we have library files, we're going to write our R.java file into
1847        // the appropriate class directory for those libraries as well.
1848        // e.g. gen/com/foo/app/lib/R.java
1849        if (bundle->getExtraPackages() != NULL) {
1850            // Split on colon
1851            String8 libs(bundle->getExtraPackages());
1852            char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
1853            while (packageString != NULL) {
1854                // Write the R.java file out with the correct package name
1855                err = writeResourceSymbols(bundle, assets, String8(packageString), true);
1856                if (err < 0) {
1857                    goto bail;
1858                }
1859                packageString = strtok(NULL, ":");
1860            }
1861            libs.unlockBuffer();
1862        }
1863    } else {
1864        err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
1865        if (err < 0) {
1866            goto bail;
1867        }
1868        err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
1869        if (err < 0) {
1870            goto bail;
1871        }
1872    }
1873
1874    // Write out the ProGuard file
1875    err = writeProguardFile(bundle, assets);
1876    if (err < 0) {
1877        goto bail;
1878    }
1879
1880    // Write the apk
1881    if (outputAPKFile) {
1882        err = writeAPK(bundle, assets, String8(outputAPKFile));
1883        if (err != NO_ERROR) {
1884            fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
1885            goto bail;
1886        }
1887    }
1888
1889    // If we've been asked to generate a dependency file, we need to finish up here.
1890    // the writeResourceSymbols and writeAPK functions have already written the target
1891    // half of the dependency file, now we need to write the prerequisites. (files that
1892    // the R.java file or .ap_ file depend on)
1893    if (bundle->getGenDependencies()) {
1894        // Now that writeResourceSymbols or writeAPK has taken care of writing
1895        // the targets to our dependency file, we'll write the prereqs
1896        fp = fopen(dependencyFile, "a+");
1897        fprintf(fp, " : ");
1898        bool includeRaw = (outputAPKFile != NULL);
1899        err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
1900        // Also manually add the AndroidManifeset since it's not under res/ or assets/
1901        // and therefore was not added to our pathstores during slurping
1902        fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
1903        fclose(fp);
1904    }
1905
1906    retVal = 0;
1907bail:
1908    if (SourcePos::hasErrors()) {
1909        SourcePos::printErrors(stderr);
1910    }
1911    return retVal;
1912}
1913
1914/*
1915 * Do PNG Crunching
1916 * PRECONDITIONS
1917 *  -S flag points to a source directory containing drawable* folders
1918 *  -C flag points to destination directory. The folder structure in the
1919 *     source directory will be mirrored to the destination (cache) directory
1920 *
1921 * POSTCONDITIONS
1922 *  Destination directory will be updated to match the PNG files in
1923 *  the source directory.
1924 */
1925int doCrunch(Bundle* bundle)
1926{
1927    fprintf(stdout, "Crunching PNG Files in ");
1928    fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
1929    fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
1930
1931    updatePreProcessedCache(bundle);
1932
1933    return NO_ERROR;
1934}
1935
1936/*
1937 * Do PNG Crunching on a single flag
1938 *  -i points to a single png file
1939 *  -o points to a single png output file
1940 */
1941int doSingleCrunch(Bundle* bundle)
1942{
1943    fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
1944    fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
1945
1946    String8 input(bundle->getSingleCrunchInputFile());
1947    String8 output(bundle->getSingleCrunchOutputFile());
1948
1949    if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
1950        // we can't return the status_t as it gets truncate to the lower 8 bits.
1951        return 42;
1952    }
1953
1954    return NO_ERROR;
1955}
1956
1957char CONSOLE_DATA[2925] = {
1958    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1959    32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1960    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1961    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1962    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
1963    86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
1964    62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1965    32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1966    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
1967    81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
1968    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1969    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1970    32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
1971    59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1972    32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1973    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
1974    59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
1975    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1976    10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1977    32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
1978    58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
1979    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
1980    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
1981    47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
1982    121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1983    32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1984    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
1985    81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
1986    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
1987    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1988    32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
1989    59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1990    32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1991    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
1992    59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
1993    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1994    32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1995    32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
1996    70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
1997    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
1998    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1999    32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2000    81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2001    32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2002    32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2003    81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2004    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2005    32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2006    59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2007    60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2008    32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2009    61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2010    61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2011    46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2012    32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2013    59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2014    109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2015    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2016    67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2017    59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2018    61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2019    32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2020    59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2021    73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2022    59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2023    32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2024    59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2025    46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2026    97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2027    46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2028    119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2029    59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2030    32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2031    32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2032    119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2033    59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2034    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2035    32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2036    81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2037    87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2038    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2039    81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2040    45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2041    32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2042    32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2043    81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2044    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2045    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2046    59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2047    59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2048    32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2049    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2050    81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2051    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2052    10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2053    32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2054    81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2055    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2056    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2057    81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2058    32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2059    32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2060    32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2061    81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2062    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2063    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2064    58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2065    61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2066    32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2067    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2068    81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2069    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2070    32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2071    32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2072    81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2073    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2074    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2075    59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2076    59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2077    32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2078    32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2079    58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2080    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2081    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2082    61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2083    59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2084    32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2085    32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2086    32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2087    61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2088    32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2089    32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2090    59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2091    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2092    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2093    59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2094    59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2095    32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2096    32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2097    32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2098    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2099    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2100    46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2101    46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2102    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2103    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2104    59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2105    59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2106    32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2107    32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2108    32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2109    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2110    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2111    59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2112    59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2113    32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2114    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2115    32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2116    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2117    10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2118    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2119    32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2120    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2121  };
2122