Command.cpp revision 4308c2aea45a4780b460734ee8634d74205c15c5
1//
2// Copyright 2006 The Android Open Source Project
3//
4// Android Asset Packaging Tool main entry point.
5//
6#include "AaptXml.h"
7#include "ApkBuilder.h"
8#include "Bundle.h"
9#include "Images.h"
10#include "Main.h"
11#include "ResourceFilter.h"
12#include "ResourceTable.h"
13#include "XMLNode.h"
14
15#include <utils/Errors.h>
16#include <utils/KeyedVector.h>
17#include <utils/List.h>
18#include <utils/Log.h>
19#include <utils/SortedVector.h>
20#include <utils/threads.h>
21#include <utils/Vector.h>
22
23#include <errno.h>
24#include <fcntl.h>
25
26#include <iostream>
27#include <string>
28#include <sstream>
29
30using namespace android;
31
32#ifndef AAPT_VERSION
33    #define AAPT_VERSION ""
34#endif
35
36/*
37 * Show version info.  All the cool kids do it.
38 */
39int doVersion(Bundle* bundle)
40{
41    if (bundle->getFileSpecCount() != 0) {
42        printf("(ignoring extra arguments)\n");
43    }
44    printf("Android Asset Packaging Tool, v0.2-" AAPT_VERSION "\n");
45
46    return 0;
47}
48
49
50/*
51 * Open the file read only.  The call fails if the file doesn't exist.
52 *
53 * Returns NULL on failure.
54 */
55ZipFile* openReadOnly(const char* fileName)
56{
57    ZipFile* zip;
58    status_t result;
59
60    zip = new ZipFile;
61    result = zip->open(fileName, ZipFile::kOpenReadOnly);
62    if (result != NO_ERROR) {
63        if (result == NAME_NOT_FOUND) {
64            fprintf(stderr, "ERROR: '%s' not found\n", fileName);
65        } else if (result == PERMISSION_DENIED) {
66            fprintf(stderr, "ERROR: '%s' access denied\n", fileName);
67        } else {
68            fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n",
69                fileName);
70        }
71        delete zip;
72        return NULL;
73    }
74
75    return zip;
76}
77
78/*
79 * Open the file read-write.  The file will be created if it doesn't
80 * already exist and "okayToCreate" is set.
81 *
82 * Returns NULL on failure.
83 */
84ZipFile* openReadWrite(const char* fileName, bool okayToCreate)
85{
86    ZipFile* zip = NULL;
87    status_t result;
88    int flags;
89
90    flags = ZipFile::kOpenReadWrite;
91    if (okayToCreate) {
92        flags |= ZipFile::kOpenCreate;
93    }
94
95    zip = new ZipFile;
96    result = zip->open(fileName, flags);
97    if (result != NO_ERROR) {
98        delete zip;
99        zip = NULL;
100        goto bail;
101    }
102
103bail:
104    return zip;
105}
106
107
108/*
109 * Return a short string describing the compression method.
110 */
111const char* compressionName(int method)
112{
113    if (method == ZipEntry::kCompressStored) {
114        return "Stored";
115    } else if (method == ZipEntry::kCompressDeflated) {
116        return "Deflated";
117    } else {
118        return "Unknown";
119    }
120}
121
122/*
123 * Return the percent reduction in size (0% == no compression).
124 */
125int calcPercent(long uncompressedLen, long compressedLen)
126{
127    if (!uncompressedLen) {
128        return 0;
129    } else {
130        return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5);
131    }
132}
133
134/*
135 * Handle the "list" command, which can be a simple file dump or
136 * a verbose listing.
137 *
138 * The verbose listing closely matches the output of the Info-ZIP "unzip"
139 * command.
140 */
141int doList(Bundle* bundle)
142{
143    int result = 1;
144    ZipFile* zip = NULL;
145    const ZipEntry* entry;
146    long totalUncLen, totalCompLen;
147    const char* zipFileName;
148
149    if (bundle->getFileSpecCount() != 1) {
150        fprintf(stderr, "ERROR: specify zip file name (only)\n");
151        goto bail;
152    }
153    zipFileName = bundle->getFileSpecEntry(0);
154
155    zip = openReadOnly(zipFileName);
156    if (zip == NULL) {
157        goto bail;
158    }
159
160    int count, i;
161
162    if (bundle->getVerbose()) {
163        printf("Archive:  %s\n", zipFileName);
164        printf(
165            " Length   Method    Size  Ratio   Offset      Date  Time  CRC-32    Name\n");
166        printf(
167            "--------  ------  ------- -----  -------      ----  ----  ------    ----\n");
168    }
169
170    totalUncLen = totalCompLen = 0;
171
172    count = zip->getNumEntries();
173    for (i = 0; i < count; i++) {
174        entry = zip->getEntryByIndex(i);
175        if (bundle->getVerbose()) {
176            char dateBuf[32];
177            time_t when;
178
179            when = entry->getModWhen();
180            strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M",
181                localtime(&when));
182
183            printf("%8ld  %-7.7s %7ld %3d%%  %8zd  %s  %08lx  %s\n",
184                (long) entry->getUncompressedLen(),
185                compressionName(entry->getCompressionMethod()),
186                (long) entry->getCompressedLen(),
187                calcPercent(entry->getUncompressedLen(),
188                            entry->getCompressedLen()),
189                (size_t) entry->getLFHOffset(),
190                dateBuf,
191                entry->getCRC32(),
192                entry->getFileName());
193        } else {
194            printf("%s\n", entry->getFileName());
195        }
196
197        totalUncLen += entry->getUncompressedLen();
198        totalCompLen += entry->getCompressedLen();
199    }
200
201    if (bundle->getVerbose()) {
202        printf(
203        "--------          -------  ---                            -------\n");
204        printf("%8ld          %7ld  %2d%%                            %d files\n",
205            totalUncLen,
206            totalCompLen,
207            calcPercent(totalUncLen, totalCompLen),
208            zip->getNumEntries());
209    }
210
211    if (bundle->getAndroidList()) {
212        AssetManager assets;
213        if (!assets.addAssetPath(String8(zipFileName), NULL)) {
214            fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n");
215            goto bail;
216        }
217
218#ifdef HAVE_ANDROID_OS
219        static const bool kHaveAndroidOs = true;
220#else
221        static const bool kHaveAndroidOs = false;
222#endif
223        const ResTable& res = assets.getResources(false);
224        if (!kHaveAndroidOs) {
225            printf("\nResource table:\n");
226            res.print(false);
227        }
228
229        Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
230                                                   Asset::ACCESS_BUFFER);
231        if (manifestAsset == NULL) {
232            printf("\nNo AndroidManifest.xml found.\n");
233        } else {
234            printf("\nAndroid manifest:\n");
235            ResXMLTree tree;
236            tree.setTo(manifestAsset->getBuffer(true),
237                       manifestAsset->getLength());
238            printXMLBlock(&tree);
239        }
240        delete manifestAsset;
241    }
242
243    result = 0;
244
245bail:
246    delete zip;
247    return result;
248}
249
250static void printResolvedResourceAttribute(const ResTable& resTable, const ResXMLTree& tree,
251        uint32_t attrRes, String8 attrLabel, String8* outError)
252{
253    Res_value value;
254    AaptXml::getResolvedResourceAttribute(resTable, tree, attrRes, &value, outError);
255    if (*outError != "") {
256        *outError = "error print resolved resource attribute";
257        return;
258    }
259    if (value.dataType == Res_value::TYPE_STRING) {
260        String8 result = AaptXml::getResolvedAttribute(resTable, tree, attrRes, outError);
261        printf("%s='%s'", attrLabel.string(),
262                ResTable::normalizeForOutput(result.string()).string());
263    } else if (Res_value::TYPE_FIRST_INT <= value.dataType &&
264            value.dataType <= Res_value::TYPE_LAST_INT) {
265        printf("%s='%d'", attrLabel.string(), value.data);
266    } else {
267        printf("%s='0x%x'", attrLabel.string(), (int)value.data);
268    }
269}
270
271// These are attribute resource constants for the platform, as found
272// in android.R.attr
273enum {
274    LABEL_ATTR = 0x01010001,
275    ICON_ATTR = 0x01010002,
276    NAME_ATTR = 0x01010003,
277    PERMISSION_ATTR = 0x01010006,
278    EXPORTED_ATTR = 0x01010010,
279    GRANT_URI_PERMISSIONS_ATTR = 0x0101001b,
280    RESOURCE_ATTR = 0x01010025,
281    DEBUGGABLE_ATTR = 0x0101000f,
282    VALUE_ATTR = 0x01010024,
283    VERSION_CODE_ATTR = 0x0101021b,
284    VERSION_NAME_ATTR = 0x0101021c,
285    SCREEN_ORIENTATION_ATTR = 0x0101001e,
286    MIN_SDK_VERSION_ATTR = 0x0101020c,
287    MAX_SDK_VERSION_ATTR = 0x01010271,
288    REQ_TOUCH_SCREEN_ATTR = 0x01010227,
289    REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
290    REQ_HARD_KEYBOARD_ATTR = 0x01010229,
291    REQ_NAVIGATION_ATTR = 0x0101022a,
292    REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
293    TARGET_SDK_VERSION_ATTR = 0x01010270,
294    TEST_ONLY_ATTR = 0x01010272,
295    ANY_DENSITY_ATTR = 0x0101026c,
296    GL_ES_VERSION_ATTR = 0x01010281,
297    SMALL_SCREEN_ATTR = 0x01010284,
298    NORMAL_SCREEN_ATTR = 0x01010285,
299    LARGE_SCREEN_ATTR = 0x01010286,
300    XLARGE_SCREEN_ATTR = 0x010102bf,
301    REQUIRED_ATTR = 0x0101028e,
302    INSTALL_LOCATION_ATTR = 0x010102b7,
303    SCREEN_SIZE_ATTR = 0x010102ca,
304    SCREEN_DENSITY_ATTR = 0x010102cb,
305    REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
306    COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
307    LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
308    PUBLIC_KEY_ATTR = 0x010103a6,
309    CATEGORY_ATTR = 0x010103e8,
310    BANNER_ATTR = 0x10103f2,
311    ISGAME_ATTR = 0x10103f4,
312};
313
314String8 getComponentName(String8 &pkgName, String8 &componentName) {
315    ssize_t idx = componentName.find(".");
316    String8 retStr(pkgName);
317    if (idx == 0) {
318        retStr += componentName;
319    } else if (idx < 0) {
320        retStr += ".";
321        retStr += componentName;
322    } else {
323        return componentName;
324    }
325    return retStr;
326}
327
328static void printCompatibleScreens(ResXMLTree& tree, String8* outError) {
329    size_t len;
330    ResXMLTree::event_code_t code;
331    int depth = 0;
332    bool first = true;
333    printf("compatible-screens:");
334    while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
335        if (code == ResXMLTree::END_TAG) {
336            depth--;
337            if (depth < 0) {
338                break;
339            }
340            continue;
341        }
342        if (code != ResXMLTree::START_TAG) {
343            continue;
344        }
345        depth++;
346        const char16_t* ctag16 = tree.getElementName(&len);
347        if (ctag16 == NULL) {
348            *outError = "failed to get XML element name (bad string pool)";
349            return;
350        }
351        String8 tag(ctag16);
352        if (tag == "screen") {
353            int32_t screenSize = AaptXml::getIntegerAttribute(tree,
354                    SCREEN_SIZE_ATTR);
355            int32_t screenDensity = AaptXml::getIntegerAttribute(tree,
356                    SCREEN_DENSITY_ATTR);
357            if (screenSize > 0 && screenDensity > 0) {
358                if (!first) {
359                    printf(",");
360                }
361                first = false;
362                printf("'%d/%d'", screenSize, screenDensity);
363            }
364        }
365    }
366    printf("\n");
367}
368
369static void printUsesPermission(const String8& name, bool optional=false, int maxSdkVersion=-1) {
370    printf("uses-permission: name='%s'", ResTable::normalizeForOutput(name.string()).string());
371    if (maxSdkVersion != -1) {
372         printf(" maxSdkVersion='%d'", maxSdkVersion);
373    }
374    printf("\n");
375
376    if (optional) {
377        printf("optional-permission: name='%s'",
378                ResTable::normalizeForOutput(name.string()).string());
379        if (maxSdkVersion != -1) {
380            printf(" maxSdkVersion='%d'", maxSdkVersion);
381        }
382        printf("\n");
383    }
384}
385
386static void printUsesImpliedPermission(const String8& name, const String8& reason) {
387    printf("uses-implied-permission: name='%s' reason='%s'\n",
388            ResTable::normalizeForOutput(name.string()).string(),
389            ResTable::normalizeForOutput(reason.string()).string());
390}
391
392Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool offHost,
393        String8 *outError = NULL)
394{
395    Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER);
396    if (aidAsset == NULL) {
397        if (outError != NULL) *outError = "xml resource does not exist";
398        return Vector<String8>();
399    }
400
401    const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service");
402
403    bool withinApduService = false;
404    Vector<String8> categories;
405
406    String8 error;
407    ResXMLTree tree;
408    tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength());
409
410    size_t len;
411    int depth = 0;
412    ResXMLTree::event_code_t code;
413    while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
414        if (code == ResXMLTree::END_TAG) {
415            depth--;
416            const char16_t* ctag16 = tree.getElementName(&len);
417            if (ctag16 == NULL) {
418                *outError = "failed to get XML element name (bad string pool)";
419                return Vector<String8>();
420            }
421            String8 tag(ctag16);
422
423            if (depth == 0 && tag == serviceTagName) {
424                withinApduService = false;
425            }
426
427        } else if (code == ResXMLTree::START_TAG) {
428            depth++;
429            const char16_t* ctag16 = tree.getElementName(&len);
430            if (ctag16 == NULL) {
431                *outError = "failed to get XML element name (bad string pool)";
432                return Vector<String8>();
433            }
434            String8 tag(ctag16);
435
436            if (depth == 1) {
437                if (tag == serviceTagName) {
438                    withinApduService = true;
439                }
440            } else if (depth == 2 && withinApduService) {
441                if (tag == "aid-group") {
442                    String8 category = AaptXml::getAttribute(tree, CATEGORY_ATTR, &error);
443                    if (error != "") {
444                        if (outError != NULL) *outError = error;
445                        return Vector<String8>();
446                    }
447
448                    categories.add(category);
449                }
450            }
451        }
452    }
453    aidAsset->close();
454    return categories;
455}
456
457static void printComponentPresence(const char* componentName) {
458    printf("provides-component:'%s'\n", componentName);
459}
460
461/**
462 * Represents a feature that has been automatically added due to
463 * a pre-requisite or some other reason.
464 */
465struct ImpliedFeature {
466    /**
467     * Name of the implied feature.
468     */
469    String8 name;
470
471    /**
472     * List of human-readable reasons for why this feature was implied.
473     */
474    SortedVector<String8> reasons;
475};
476
477/**
478 * Represents a <feature-group> tag in the AndroidManifest.xml
479 */
480struct FeatureGroup {
481    FeatureGroup() : openGLESVersion(-1) {}
482
483    /**
484     * Human readable label
485     */
486    String8 label;
487
488    /**
489     * Explicit features defined in the group
490     */
491    KeyedVector<String8, bool> features;
492
493    /**
494     * OpenGL ES version required
495     */
496    int openGLESVersion;
497};
498
499static void addImpliedFeature(KeyedVector<String8, ImpliedFeature>* impliedFeatures,
500        const char* name, const char* reason) {
501    String8 name8(name);
502    ssize_t idx = impliedFeatures->indexOfKey(name8);
503    if (idx < 0) {
504        idx = impliedFeatures->add(name8, ImpliedFeature());
505        impliedFeatures->editValueAt(idx).name = name8;
506    }
507    impliedFeatures->editValueAt(idx).reasons.add(String8(reason));
508}
509
510static void printFeatureGroup(const FeatureGroup& grp,
511        const KeyedVector<String8, ImpliedFeature>* impliedFeatures = NULL) {
512    printf("feature-group: label='%s'\n", grp.label.string());
513
514    if (grp.openGLESVersion > 0) {
515        printf("  uses-gl-es: '0x%x'\n", grp.openGLESVersion);
516    }
517
518    const size_t numFeatures = grp.features.size();
519    for (size_t i = 0; i < numFeatures; i++) {
520        const bool required = grp.features[i];
521
522        const String8& featureName = grp.features.keyAt(i);
523        printf("  uses-feature%s: name='%s'\n", (required ? "" : "-not-required"),
524                ResTable::normalizeForOutput(featureName.string()).string());
525    }
526
527    const size_t numImpliedFeatures =
528        (impliedFeatures != NULL) ? impliedFeatures->size() : 0;
529    for (size_t i = 0; i < numImpliedFeatures; i++) {
530        const ImpliedFeature& impliedFeature = impliedFeatures->valueAt(i);
531        if (grp.features.indexOfKey(impliedFeature.name) >= 0) {
532            // The feature is explicitly set, no need to use implied
533            // definition.
534            continue;
535        }
536
537        String8 printableFeatureName(ResTable::normalizeForOutput(
538                    impliedFeature.name.string()));
539        printf("  uses-feature: name='%s'\n", printableFeatureName.string());
540        printf("  uses-implied-feature: name='%s' reason='",
541                printableFeatureName.string());
542        const size_t numReasons = impliedFeature.reasons.size();
543        for (size_t j = 0; j < numReasons; j++) {
544            printf("%s", impliedFeature.reasons[j].string());
545            if (j + 2 < numReasons) {
546                printf(", ");
547            } else if (j + 1 < numReasons) {
548                printf(", and ");
549            }
550        }
551        printf("'\n");
552    }
553}
554
555static void addParentFeatures(FeatureGroup* grp, const String8& name) {
556    if (name == "android.hardware.camera.autofocus" ||
557            name == "android.hardware.camera.flash") {
558        grp->features.add(String8("android.hardware.camera"), true);
559    } else if (name == "android.hardware.location.gps" ||
560            name == "android.hardware.location.network") {
561        grp->features.add(String8("android.hardware.location"), true);
562    } else if (name == "android.hardware.touchscreen.multitouch") {
563        grp->features.add(String8("android.hardware.touchscreen"), true);
564    } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
565        grp->features.add(String8("android.hardware.touchscreen.multitouch"), true);
566        grp->features.add(String8("android.hardware.touchscreen"), true);
567    } else if (name == "android.hardware.opengles.aep") {
568        const int openGLESVersion31 = 0x00030001;
569        if (openGLESVersion31 > grp->openGLESVersion) {
570            grp->openGLESVersion = openGLESVersion31;
571        }
572    }
573}
574
575/*
576 * Handle the "dump" command, to extract select data from an archive.
577 */
578extern char CONSOLE_DATA[2925]; // see EOF
579int doDump(Bundle* bundle)
580{
581    status_t result = UNKNOWN_ERROR;
582
583    if (bundle->getFileSpecCount() < 1) {
584        fprintf(stderr, "ERROR: no dump option specified\n");
585        return 1;
586    }
587
588    if (bundle->getFileSpecCount() < 2) {
589        fprintf(stderr, "ERROR: no dump file specified\n");
590        return 1;
591    }
592
593    const char* option = bundle->getFileSpecEntry(0);
594    const char* filename = bundle->getFileSpecEntry(1);
595
596    AssetManager assets;
597    int32_t assetsCookie;
598    if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
599        fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
600        return 1;
601    }
602
603    // Make a dummy config for retrieving resources...  we need to supply
604    // non-default values for some configs so that we can retrieve resources
605    // in the app that don't have a default.  The most important of these is
606    // the API version because key resources like icons will have an implicit
607    // version if they are using newer config types like density.
608    ResTable_config config;
609    memset(&config, 0, sizeof(ResTable_config));
610    config.language[0] = 'e';
611    config.language[1] = 'n';
612    config.country[0] = 'U';
613    config.country[1] = 'S';
614    config.orientation = ResTable_config::ORIENTATION_PORT;
615    config.density = ResTable_config::DENSITY_MEDIUM;
616    config.sdkVersion = 10000; // Very high.
617    config.screenWidthDp = 320;
618    config.screenHeightDp = 480;
619    config.smallestScreenWidthDp = 320;
620    config.screenLayout |= ResTable_config::SCREENSIZE_NORMAL;
621    assets.setConfiguration(config);
622
623    const ResTable& res = assets.getResources(false);
624    if (res.getError() != NO_ERROR) {
625        fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
626        return 1;
627    }
628
629    // The dynamicRefTable can be null if there are no resources for this asset cookie.
630    // This fine.
631    const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie);
632
633    Asset* asset = NULL;
634
635    if (strcmp("resources", option) == 0) {
636#ifndef HAVE_ANDROID_OS
637        res.print(bundle->getValues());
638#endif
639
640    } else if (strcmp("strings", option) == 0) {
641        const ResStringPool* pool = res.getTableStringBlock(0);
642        printStringPool(pool);
643
644    } else if (strcmp("xmltree", option) == 0) {
645        if (bundle->getFileSpecCount() < 3) {
646            fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
647            goto bail;
648        }
649
650        for (int i=2; i<bundle->getFileSpecCount(); i++) {
651            const char* resname = bundle->getFileSpecEntry(i);
652            ResXMLTree tree(dynamicRefTable);
653            asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
654            if (asset == NULL) {
655                fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
656                goto bail;
657            }
658
659            if (tree.setTo(asset->getBuffer(true),
660                           asset->getLength()) != NO_ERROR) {
661                fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
662                goto bail;
663            }
664            tree.restart();
665            printXMLBlock(&tree);
666            tree.uninit();
667            delete asset;
668            asset = NULL;
669        }
670
671    } else if (strcmp("xmlstrings", option) == 0) {
672        if (bundle->getFileSpecCount() < 3) {
673            fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
674            goto bail;
675        }
676
677        for (int i=2; i<bundle->getFileSpecCount(); i++) {
678            const char* resname = bundle->getFileSpecEntry(i);
679            asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
680            if (asset == NULL) {
681                fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
682                goto bail;
683            }
684
685            ResXMLTree tree(dynamicRefTable);
686            if (tree.setTo(asset->getBuffer(true),
687                           asset->getLength()) != NO_ERROR) {
688                fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
689                goto bail;
690            }
691            printStringPool(&tree.getStrings());
692            delete asset;
693            asset = NULL;
694        }
695
696    } else {
697        asset = assets.openNonAsset(assetsCookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
698        if (asset == NULL) {
699            fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
700            goto bail;
701        }
702
703        ResXMLTree tree(dynamicRefTable);
704        if (tree.setTo(asset->getBuffer(true),
705                       asset->getLength()) != NO_ERROR) {
706            fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
707            goto bail;
708        }
709        tree.restart();
710
711        if (strcmp("permissions", option) == 0) {
712            size_t len;
713            ResXMLTree::event_code_t code;
714            int depth = 0;
715            while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
716                if (code == ResXMLTree::END_TAG) {
717                    depth--;
718                    continue;
719                }
720                if (code != ResXMLTree::START_TAG) {
721                    continue;
722                }
723                depth++;
724                const char16_t* ctag16 = tree.getElementName(&len);
725                if (ctag16 == NULL) {
726                    fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
727                    goto bail;
728                }
729                String8 tag(ctag16);
730                //printf("Depth %d tag %s\n", depth, tag.string());
731                if (depth == 1) {
732                    if (tag != "manifest") {
733                        fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
734                        goto bail;
735                    }
736                    String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
737                    printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
738                } else if (depth == 2 && tag == "permission") {
739                    String8 error;
740                    String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
741                    if (error != "") {
742                        fprintf(stderr, "ERROR: %s\n", error.string());
743                        goto bail;
744                    }
745                    printf("permission: %s\n",
746                            ResTable::normalizeForOutput(name.string()).string());
747                } else if (depth == 2 && tag == "uses-permission") {
748                    String8 error;
749                    String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
750                    if (error != "") {
751                        fprintf(stderr, "ERROR: %s\n", error.string());
752                        goto bail;
753                    }
754                    printUsesPermission(name,
755                            AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
756                            AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
757                }
758            }
759        } else if (strcmp("badging", option) == 0) {
760            Vector<String8> locales;
761            res.getLocales(&locales);
762
763            Vector<ResTable_config> configs;
764            res.getConfigurations(&configs);
765            SortedVector<int> densities;
766            const size_t NC = configs.size();
767            for (size_t i=0; i<NC; i++) {
768                int dens = configs[i].density;
769                if (dens == 0) {
770                    dens = 160;
771                }
772                densities.add(dens);
773            }
774
775            size_t len;
776            ResXMLTree::event_code_t code;
777            int depth = 0;
778            String8 error;
779            bool withinActivity = false;
780            bool isMainActivity = false;
781            bool isLauncherActivity = false;
782            bool isLeanbackLauncherActivity = false;
783            bool isSearchable = false;
784            bool withinApplication = false;
785            bool withinSupportsInput = false;
786            bool withinFeatureGroup = false;
787            bool withinReceiver = false;
788            bool withinService = false;
789            bool withinProvider = false;
790            bool withinIntentFilter = false;
791            bool hasMainActivity = false;
792            bool hasOtherActivities = false;
793            bool hasOtherReceivers = false;
794            bool hasOtherServices = false;
795            bool hasIntentFilter = false;
796
797            bool hasWallpaperService = false;
798            bool hasImeService = false;
799            bool hasAccessibilityService = false;
800            bool hasPrintService = false;
801            bool hasWidgetReceivers = false;
802            bool hasDeviceAdminReceiver = false;
803            bool hasPaymentService = false;
804            bool hasDocumentsProvider = false;
805            bool hasCameraActivity = false;
806            bool hasCameraSecureActivity = false;
807            bool hasLauncher = false;
808            bool hasNotificationListenerService = false;
809            bool hasDreamService = false;
810
811            bool actMainActivity = false;
812            bool actWidgetReceivers = false;
813            bool actDeviceAdminEnabled = false;
814            bool actImeService = false;
815            bool actWallpaperService = false;
816            bool actAccessibilityService = false;
817            bool actPrintService = false;
818            bool actHostApduService = false;
819            bool actOffHostApduService = false;
820            bool actDocumentsProvider = false;
821            bool actNotificationListenerService = false;
822            bool actDreamService = false;
823            bool actCamera = false;
824            bool actCameraSecure = false;
825            bool catLauncher = false;
826            bool hasMetaHostPaymentCategory = false;
827            bool hasMetaOffHostPaymentCategory = false;
828
829            // These permissions are required by services implementing services
830            // the system binds to (IME, Accessibility, PrintServices, etc.)
831            bool hasBindDeviceAdminPermission = false;
832            bool hasBindInputMethodPermission = false;
833            bool hasBindAccessibilityServicePermission = false;
834            bool hasBindPrintServicePermission = false;
835            bool hasBindNfcServicePermission = false;
836            bool hasRequiredSafAttributes = false;
837            bool hasBindNotificationListenerServicePermission = false;
838            bool hasBindDreamServicePermission = false;
839
840            // These two implement the implicit permissions that are granted
841            // to pre-1.6 applications.
842            bool hasWriteExternalStoragePermission = false;
843            bool hasReadPhoneStatePermission = false;
844
845            // If an app requests write storage, they will also get read storage.
846            bool hasReadExternalStoragePermission = false;
847
848            // Implement transition to read and write call log.
849            bool hasReadContactsPermission = false;
850            bool hasWriteContactsPermission = false;
851            bool hasReadCallLogPermission = false;
852            bool hasWriteCallLogPermission = false;
853
854            // If an app declares itself as multiArch, we report the
855            // native libraries differently.
856            bool hasMultiArch = false;
857
858            // This next group of variables is used to implement a group of
859            // backward-compatibility heuristics necessitated by the addition of
860            // some new uses-feature constants in 2.1 and 2.2. In most cases, the
861            // heuristic is "if an app requests a permission but doesn't explicitly
862            // request the corresponding <uses-feature>, presume it's there anyway".
863
864            // 2.2 also added some other features that apps can request, but that
865            // have no corresponding permission, so we cannot implement any
866            // back-compatibility heuristic for them. The below are thus unnecessary
867            // (but are retained here for documentary purposes.)
868            //bool specCompassFeature = false;
869            //bool specAccelerometerFeature = false;
870            //bool specProximityFeature = false;
871            //bool specAmbientLightFeature = false;
872            //bool specLiveWallpaperFeature = false;
873
874            int targetSdk = 0;
875            int smallScreen = 1;
876            int normalScreen = 1;
877            int largeScreen = 1;
878            int xlargeScreen = 1;
879            int anyDensity = 1;
880            int requiresSmallestWidthDp = 0;
881            int compatibleWidthLimitDp = 0;
882            int largestWidthLimitDp = 0;
883            String8 pkg;
884            String8 activityName;
885            String8 activityLabel;
886            String8 activityIcon;
887            String8 activityBanner;
888            String8 receiverName;
889            String8 serviceName;
890            Vector<String8> supportedInput;
891
892            FeatureGroup commonFeatures;
893            Vector<FeatureGroup> featureGroups;
894            KeyedVector<String8, ImpliedFeature> impliedFeatures;
895
896            while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
897                if (code == ResXMLTree::END_TAG) {
898                    depth--;
899                    if (depth < 2) {
900                        if (withinSupportsInput && !supportedInput.isEmpty()) {
901                            printf("supports-input: '");
902                            const size_t N = supportedInput.size();
903                            for (size_t i=0; i<N; i++) {
904                                printf("%s", ResTable::normalizeForOutput(
905                                        supportedInput[i].string()).string());
906                                if (i != N - 1) {
907                                    printf("' '");
908                                } else {
909                                    printf("'\n");
910                                }
911                            }
912                            supportedInput.clear();
913                        }
914                        withinApplication = false;
915                        withinSupportsInput = false;
916                        withinFeatureGroup = false;
917                    } else if (depth < 3) {
918                        if (withinActivity && isMainActivity) {
919                            String8 aName(getComponentName(pkg, activityName));
920                            if (isLauncherActivity) {
921                                printf("launchable-activity:");
922                                if (aName.length() > 0) {
923                                    printf(" name='%s' ",
924                                            ResTable::normalizeForOutput(aName.string()).string());
925                                }
926                                printf(" label='%s' icon='%s'\n",
927                                        ResTable::normalizeForOutput(activityLabel.string()).string(),
928                                        ResTable::normalizeForOutput(activityIcon.string()).string());
929                            }
930                            if (isLeanbackLauncherActivity) {
931                                printf("leanback-launchable-activity:");
932                                if (aName.length() > 0) {
933                                    printf(" name='%s' ",
934                                            ResTable::normalizeForOutput(aName.string()).string());
935                                }
936                                printf(" label='%s' icon='%s' banner='%s'\n",
937                                        ResTable::normalizeForOutput(activityLabel.string()).string(),
938                                        ResTable::normalizeForOutput(activityIcon.string()).string(),
939                                        ResTable::normalizeForOutput(activityBanner.string()).string());
940                            }
941                        }
942                        if (!hasIntentFilter) {
943                            hasOtherActivities |= withinActivity;
944                            hasOtherReceivers |= withinReceiver;
945                            hasOtherServices |= withinService;
946                        } else {
947                            if (withinService) {
948                                hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
949                                        hasBindNfcServicePermission);
950                                hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
951                                        hasBindNfcServicePermission);
952                            }
953                        }
954                        withinActivity = false;
955                        withinService = false;
956                        withinReceiver = false;
957                        withinProvider = false;
958                        hasIntentFilter = false;
959                        isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
960                    } else if (depth < 4) {
961                        if (withinIntentFilter) {
962                            if (withinActivity) {
963                                hasMainActivity |= actMainActivity;
964                                hasLauncher |= catLauncher;
965                                hasCameraActivity |= actCamera;
966                                hasCameraSecureActivity |= actCameraSecure;
967                                hasOtherActivities |= !actMainActivity && !actCamera && !actCameraSecure;
968                            } else if (withinReceiver) {
969                                hasWidgetReceivers |= actWidgetReceivers;
970                                hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
971                                        hasBindDeviceAdminPermission);
972                                hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled);
973                            } else if (withinService) {
974                                hasImeService |= actImeService;
975                                hasWallpaperService |= actWallpaperService;
976                                hasAccessibilityService |= (actAccessibilityService &&
977                                        hasBindAccessibilityServicePermission);
978                                hasPrintService |= (actPrintService && hasBindPrintServicePermission);
979                                hasNotificationListenerService |= actNotificationListenerService &&
980                                        hasBindNotificationListenerServicePermission;
981                                hasDreamService |= actDreamService && hasBindDreamServicePermission;
982                                hasOtherServices |= (!actImeService && !actWallpaperService &&
983                                        !actAccessibilityService && !actPrintService &&
984                                        !actHostApduService && !actOffHostApduService &&
985                                        !actNotificationListenerService);
986                            } else if (withinProvider) {
987                                hasDocumentsProvider |= actDocumentsProvider && hasRequiredSafAttributes;
988                            }
989                        }
990                        withinIntentFilter = false;
991                    }
992                    continue;
993                }
994                if (code != ResXMLTree::START_TAG) {
995                    continue;
996                }
997                depth++;
998
999                const char16_t* ctag16 = tree.getElementName(&len);
1000                if (ctag16 == NULL) {
1001                    fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
1002                    goto bail;
1003                }
1004                String8 tag(ctag16);
1005                //printf("Depth %d,  %s\n", depth, tag.string());
1006                if (depth == 1) {
1007                    if (tag != "manifest") {
1008                        fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
1009                        goto bail;
1010                    }
1011                    pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
1012                    printf("package: name='%s' ",
1013                            ResTable::normalizeForOutput(pkg.string()).string());
1014                    int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR,
1015                            &error);
1016                    if (error != "") {
1017                        fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n",
1018                                error.string());
1019                        goto bail;
1020                    }
1021                    if (versionCode > 0) {
1022                        printf("versionCode='%d' ", versionCode);
1023                    } else {
1024                        printf("versionCode='' ");
1025                    }
1026                    String8 versionName = AaptXml::getResolvedAttribute(res, tree,
1027                            VERSION_NAME_ATTR, &error);
1028                    if (error != "") {
1029                        fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n",
1030                                error.string());
1031                        goto bail;
1032                    }
1033                    printf("versionName='%s'",
1034                            ResTable::normalizeForOutput(versionName.string()).string());
1035
1036                    String8 splitName = AaptXml::getAttribute(tree, NULL, "split");
1037                    if (!splitName.isEmpty()) {
1038                        printf(" split='%s'", ResTable::normalizeForOutput(
1039                                    splitName.string()).string());
1040                    }
1041
1042                    String8 platformVersionName = AaptXml::getAttribute(tree, NULL,
1043                            "platformBuildVersionName");
1044                    printf(" platformBuildVersionName='%s'", platformVersionName.string());
1045                    printf("\n");
1046
1047                    int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
1048                            INSTALL_LOCATION_ATTR, &error);
1049                    if (error != "") {
1050                        fprintf(stderr, "ERROR getting 'android:installLocation' attribute: %s\n",
1051                                error.string());
1052                        goto bail;
1053                    }
1054
1055                    if (installLocation >= 0) {
1056                        printf("install-location:'");
1057                        switch (installLocation) {
1058                            case 0:
1059                                printf("auto");
1060                                break;
1061                            case 1:
1062                                printf("internalOnly");
1063                                break;
1064                            case 2:
1065                                printf("preferExternal");
1066                                break;
1067                            default:
1068                                fprintf(stderr, "Invalid installLocation %d\n", installLocation);
1069                                goto bail;
1070                        }
1071                        printf("'\n");
1072                    }
1073                } else if (depth == 2) {
1074                    withinApplication = false;
1075                    if (tag == "application") {
1076                        withinApplication = true;
1077
1078                        String8 label;
1079                        const size_t NL = locales.size();
1080                        for (size_t i=0; i<NL; i++) {
1081                            const char* localeStr =  locales[i].string();
1082                            assets.setLocale(localeStr != NULL ? localeStr : "");
1083                            String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1084                                    &error);
1085                            if (llabel != "") {
1086                                if (localeStr == NULL || strlen(localeStr) == 0) {
1087                                    label = llabel;
1088                                    printf("application-label:'%s'\n",
1089                                            ResTable::normalizeForOutput(llabel.string()).string());
1090                                } else {
1091                                    if (label == "") {
1092                                        label = llabel;
1093                                    }
1094                                    printf("application-label-%s:'%s'\n", localeStr,
1095                                           ResTable::normalizeForOutput(llabel.string()).string());
1096                                }
1097                            }
1098                        }
1099
1100                        ResTable_config tmpConfig = config;
1101                        const size_t ND = densities.size();
1102                        for (size_t i=0; i<ND; i++) {
1103                            tmpConfig.density = densities[i];
1104                            assets.setConfiguration(tmpConfig);
1105                            String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1106                                    &error);
1107                            if (icon != "") {
1108                                printf("application-icon-%d:'%s'\n", densities[i],
1109                                        ResTable::normalizeForOutput(icon.string()).string());
1110                            }
1111                        }
1112                        assets.setConfiguration(config);
1113
1114                        String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error);
1115                        if (error != "") {
1116                            fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1117                                    error.string());
1118                            goto bail;
1119                        }
1120                        int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0,
1121                                &error);
1122                        if (error != "") {
1123                            fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n",
1124                                    error.string());
1125                            goto bail;
1126                        }
1127
1128                        String8 banner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR, &error);
1129                        if (error != "") {
1130                            fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
1131                                    error.string());
1132                            goto bail;
1133                        }
1134                        printf("application: label='%s' ",
1135                                ResTable::normalizeForOutput(label.string()).string());
1136                        printf("icon='%s'", ResTable::normalizeForOutput(icon.string()).string());
1137                        if (banner != "") {
1138                            printf(" banner='%s'", ResTable::normalizeForOutput(banner.string()).string());
1139                        }
1140                        printf("\n");
1141                        if (testOnly != 0) {
1142                            printf("testOnly='%d'\n", testOnly);
1143                        }
1144
1145                        int32_t isGame = AaptXml::getResolvedIntegerAttribute(res, tree,
1146                                ISGAME_ATTR, 0, &error);
1147                        if (error != "") {
1148                            fprintf(stderr, "ERROR getting 'android:isGame' attribute: %s\n",
1149                                    error.string());
1150                            goto bail;
1151                        }
1152                        if (isGame != 0) {
1153                            printf("application-isGame\n");
1154                        }
1155
1156                        int32_t debuggable = AaptXml::getResolvedIntegerAttribute(res, tree,
1157                                DEBUGGABLE_ATTR, 0, &error);
1158                        if (error != "") {
1159                            fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n",
1160                                    error.string());
1161                            goto bail;
1162                        }
1163                        if (debuggable != 0) {
1164                            printf("application-debuggable\n");
1165                        }
1166
1167                        // We must search by name because the multiArch flag hasn't been API
1168                        // frozen yet.
1169                        int32_t multiArchIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
1170                                "multiArch");
1171                        if (multiArchIndex >= 0) {
1172                            Res_value value;
1173                            if (tree.getAttributeValue(multiArchIndex, &value) != NO_ERROR) {
1174                                if (value.dataType >= Res_value::TYPE_FIRST_INT &&
1175                                        value.dataType <= Res_value::TYPE_LAST_INT) {
1176                                    hasMultiArch = value.data;
1177                                }
1178                            }
1179                        }
1180                    } else if (tag == "uses-sdk") {
1181                        int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
1182                        if (error != "") {
1183                            error = "";
1184                            String8 name = AaptXml::getResolvedAttribute(res, tree,
1185                                    MIN_SDK_VERSION_ATTR, &error);
1186                            if (error != "") {
1187                                fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
1188                                        error.string());
1189                                goto bail;
1190                            }
1191                            if (name == "Donut") targetSdk = 4;
1192                            printf("sdkVersion:'%s'\n",
1193                                    ResTable::normalizeForOutput(name.string()).string());
1194                        } else if (code != -1) {
1195                            targetSdk = code;
1196                            printf("sdkVersion:'%d'\n", code);
1197                        }
1198                        code = AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR);
1199                        if (code != -1) {
1200                            printf("maxSdkVersion:'%d'\n", code);
1201                        }
1202                        code = AaptXml::getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
1203                        if (error != "") {
1204                            error = "";
1205                            String8 name = AaptXml::getResolvedAttribute(res, tree,
1206                                    TARGET_SDK_VERSION_ATTR, &error);
1207                            if (error != "") {
1208                                fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
1209                                        error.string());
1210                                goto bail;
1211                            }
1212                            if (name == "Donut" && targetSdk < 4) targetSdk = 4;
1213                            printf("targetSdkVersion:'%s'\n",
1214                                    ResTable::normalizeForOutput(name.string()).string());
1215                        } else if (code != -1) {
1216                            if (targetSdk < code) {
1217                                targetSdk = code;
1218                            }
1219                            printf("targetSdkVersion:'%d'\n", code);
1220                        }
1221                    } else if (tag == "uses-configuration") {
1222                        int32_t reqTouchScreen = AaptXml::getIntegerAttribute(tree,
1223                                REQ_TOUCH_SCREEN_ATTR, 0);
1224                        int32_t reqKeyboardType = AaptXml::getIntegerAttribute(tree,
1225                                REQ_KEYBOARD_TYPE_ATTR, 0);
1226                        int32_t reqHardKeyboard = AaptXml::getIntegerAttribute(tree,
1227                                REQ_HARD_KEYBOARD_ATTR, 0);
1228                        int32_t reqNavigation = AaptXml::getIntegerAttribute(tree,
1229                                REQ_NAVIGATION_ATTR, 0);
1230                        int32_t reqFiveWayNav = AaptXml::getIntegerAttribute(tree,
1231                                REQ_FIVE_WAY_NAV_ATTR, 0);
1232                        printf("uses-configuration:");
1233                        if (reqTouchScreen != 0) {
1234                            printf(" reqTouchScreen='%d'", reqTouchScreen);
1235                        }
1236                        if (reqKeyboardType != 0) {
1237                            printf(" reqKeyboardType='%d'", reqKeyboardType);
1238                        }
1239                        if (reqHardKeyboard != 0) {
1240                            printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1241                        }
1242                        if (reqNavigation != 0) {
1243                            printf(" reqNavigation='%d'", reqNavigation);
1244                        }
1245                        if (reqFiveWayNav != 0) {
1246                            printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1247                        }
1248                        printf("\n");
1249                    } else if (tag == "supports-input") {
1250                        withinSupportsInput = true;
1251                    } else if (tag == "supports-screens") {
1252                        smallScreen = AaptXml::getIntegerAttribute(tree,
1253                                SMALL_SCREEN_ATTR, 1);
1254                        normalScreen = AaptXml::getIntegerAttribute(tree,
1255                                NORMAL_SCREEN_ATTR, 1);
1256                        largeScreen = AaptXml::getIntegerAttribute(tree,
1257                                LARGE_SCREEN_ATTR, 1);
1258                        xlargeScreen = AaptXml::getIntegerAttribute(tree,
1259                                XLARGE_SCREEN_ATTR, 1);
1260                        anyDensity = AaptXml::getIntegerAttribute(tree,
1261                                ANY_DENSITY_ATTR, 1);
1262                        requiresSmallestWidthDp = AaptXml::getIntegerAttribute(tree,
1263                                REQUIRES_SMALLEST_WIDTH_DP_ATTR, 0);
1264                        compatibleWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1265                                COMPATIBLE_WIDTH_LIMIT_DP_ATTR, 0);
1266                        largestWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1267                                LARGEST_WIDTH_LIMIT_DP_ATTR, 0);
1268                    } else if (tag == "feature-group") {
1269                        withinFeatureGroup = true;
1270                        FeatureGroup group;
1271                        group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error);
1272                        if (error != "") {
1273                            fprintf(stderr, "ERROR getting 'android:label' attribute:"
1274                                    " %s\n", error.string());
1275                            goto bail;
1276                        }
1277                        featureGroups.add(group);
1278
1279                    } else if (tag == "uses-feature") {
1280                        String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1281                        if (name != "" && error == "") {
1282                            int req = AaptXml::getIntegerAttribute(tree,
1283                                    REQUIRED_ATTR, 1);
1284
1285                            commonFeatures.features.add(name, req);
1286                            if (req) {
1287                                addParentFeatures(&commonFeatures, name);
1288                            }
1289                        } else {
1290                            int vers = AaptXml::getIntegerAttribute(tree,
1291                                    GL_ES_VERSION_ATTR, &error);
1292                            if (error == "") {
1293                                if (vers > commonFeatures.openGLESVersion) {
1294                                    commonFeatures.openGLESVersion = vers;
1295                                }
1296                            }
1297                        }
1298                    } else if (tag == "uses-permission") {
1299                        String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1300                        if (name != "" && error == "") {
1301                            if (name == "android.permission.CAMERA") {
1302                                addImpliedFeature(&impliedFeatures, "android.hardware.camera",
1303                                        String8::format("requested %s permission", name.string())
1304                                        .string());
1305                            } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
1306                                addImpliedFeature(&impliedFeatures, "android.hardware.location.gps",
1307                                        String8::format("requested %s permission", name.string())
1308                                        .string());
1309                                addImpliedFeature(&impliedFeatures, "android.hardware.location",
1310                                        String8::format("requested %s permission", name.string())
1311                                        .string());
1312                            } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
1313                                addImpliedFeature(&impliedFeatures, "android.hardware.location",
1314                                        String8::format("requested %s permission", name.string())
1315                                        .string());
1316                            } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
1317                                addImpliedFeature(&impliedFeatures, "android.hardware.location.network",
1318                                        String8::format("requested %s permission", name.string())
1319                                        .string());
1320                                addImpliedFeature(&impliedFeatures, "android.hardware.location",
1321                                        String8::format("requested %s permission", name.string())
1322                                        .string());
1323                            } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
1324                                       name == "android.permission.INSTALL_LOCATION_PROVIDER") {
1325                                addImpliedFeature(&impliedFeatures, "android.hardware.location",
1326                                        String8::format("requested %s permission", name.string())
1327                                        .string());
1328                            } else if (name == "android.permission.BLUETOOTH" ||
1329                                       name == "android.permission.BLUETOOTH_ADMIN") {
1330                                if (targetSdk > 4) {
1331                                    addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
1332                                            String8::format("requested %s permission", name.string())
1333                                            .string());
1334                                    addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
1335                                            "targetSdkVersion > 4");
1336                                }
1337                            } else if (name == "android.permission.RECORD_AUDIO") {
1338                                addImpliedFeature(&impliedFeatures, "android.hardware.microphone",
1339                                        String8::format("requested %s permission", name.string())
1340                                        .string());
1341                            } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1342                                       name == "android.permission.CHANGE_WIFI_STATE" ||
1343                                       name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
1344                                addImpliedFeature(&impliedFeatures, "android.hardware.wifi",
1345                                        String8::format("requested %s permission", name.string())
1346                                        .string());
1347                            } else if (name == "android.permission.CALL_PHONE" ||
1348                                       name == "android.permission.CALL_PRIVILEGED" ||
1349                                       name == "android.permission.MODIFY_PHONE_STATE" ||
1350                                       name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1351                                       name == "android.permission.READ_SMS" ||
1352                                       name == "android.permission.RECEIVE_SMS" ||
1353                                       name == "android.permission.RECEIVE_MMS" ||
1354                                       name == "android.permission.RECEIVE_WAP_PUSH" ||
1355                                       name == "android.permission.SEND_SMS" ||
1356                                       name == "android.permission.WRITE_APN_SETTINGS" ||
1357                                       name == "android.permission.WRITE_SMS") {
1358                                addImpliedFeature(&impliedFeatures, "android.hardware.telephony",
1359                                        String8("requested a telephony permission").string());
1360                            } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1361                                hasWriteExternalStoragePermission = true;
1362                            } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1363                                hasReadExternalStoragePermission = true;
1364                            } else if (name == "android.permission.READ_PHONE_STATE") {
1365                                hasReadPhoneStatePermission = true;
1366                            } else if (name == "android.permission.READ_CONTACTS") {
1367                                hasReadContactsPermission = true;
1368                            } else if (name == "android.permission.WRITE_CONTACTS") {
1369                                hasWriteContactsPermission = true;
1370                            } else if (name == "android.permission.READ_CALL_LOG") {
1371                                hasReadCallLogPermission = true;
1372                            } else if (name == "android.permission.WRITE_CALL_LOG") {
1373                                hasWriteCallLogPermission = true;
1374                            }
1375
1376                            printUsesPermission(name,
1377                                    AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
1378                                    AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
1379                       } else {
1380                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1381                                    error.string());
1382                            goto bail;
1383                        }
1384                    } else if (tag == "uses-package") {
1385                        String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1386                        if (name != "" && error == "") {
1387                            printf("uses-package:'%s'\n",
1388                                    ResTable::normalizeForOutput(name.string()).string());
1389                        } else {
1390                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1391                                    error.string());
1392                                goto bail;
1393                        }
1394                    } else if (tag == "original-package") {
1395                        String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1396                        if (name != "" && error == "") {
1397                            printf("original-package:'%s'\n",
1398                                    ResTable::normalizeForOutput(name.string()).string());
1399                        } else {
1400                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1401                                    error.string());
1402                                goto bail;
1403                        }
1404                    } else if (tag == "supports-gl-texture") {
1405                        String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1406                        if (name != "" && error == "") {
1407                            printf("supports-gl-texture:'%s'\n",
1408                                    ResTable::normalizeForOutput(name.string()).string());
1409                        } else {
1410                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1411                                    error.string());
1412                                goto bail;
1413                        }
1414                    } else if (tag == "compatible-screens") {
1415                        printCompatibleScreens(tree, &error);
1416                        if (error != "") {
1417                            fprintf(stderr, "ERROR getting compatible screens: %s\n",
1418                                    error.string());
1419                            goto bail;
1420                        }
1421                        depth--;
1422                    } else if (tag == "package-verifier") {
1423                        String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1424                        if (name != "" && error == "") {
1425                            String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR, &error);
1426                            if (publicKey != "" && error == "") {
1427                                printf("package-verifier: name='%s' publicKey='%s'\n",
1428                                        ResTable::normalizeForOutput(name.string()).string(),
1429                                        ResTable::normalizeForOutput(publicKey.string()).string());
1430                            }
1431                        }
1432                    }
1433                } else if (depth == 3) {
1434                    withinActivity = false;
1435                    withinReceiver = false;
1436                    withinService = false;
1437                    withinProvider = false;
1438                    hasIntentFilter = false;
1439                    hasMetaHostPaymentCategory = false;
1440                    hasMetaOffHostPaymentCategory = false;
1441                    hasBindDeviceAdminPermission = false;
1442                    hasBindInputMethodPermission = false;
1443                    hasBindAccessibilityServicePermission = false;
1444                    hasBindPrintServicePermission = false;
1445                    hasBindNfcServicePermission = false;
1446                    hasRequiredSafAttributes = false;
1447                    hasBindNotificationListenerServicePermission = false;
1448                    hasBindDreamServicePermission = false;
1449                    if (withinApplication) {
1450                        if(tag == "activity") {
1451                            withinActivity = true;
1452                            activityName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1453                            if (error != "") {
1454                                fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1455                                        error.string());
1456                                goto bail;
1457                            }
1458
1459                            activityLabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1460                                    &error);
1461                            if (error != "") {
1462                                fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
1463                                        error.string());
1464                                goto bail;
1465                            }
1466
1467                            activityIcon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1468                                    &error);
1469                            if (error != "") {
1470                                fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1471                                        error.string());
1472                                goto bail;
1473                            }
1474
1475                            activityBanner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1476                                    &error);
1477                            if (error != "") {
1478                                fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
1479                                        error.string());
1480                                goto bail;
1481                            }
1482
1483                            int32_t orien = AaptXml::getResolvedIntegerAttribute(res, tree,
1484                                    SCREEN_ORIENTATION_ATTR, &error);
1485                            if (error == "") {
1486                                if (orien == 0 || orien == 6 || orien == 8) {
1487                                    // Requests landscape, sensorLandscape, or reverseLandscape.
1488                                    addImpliedFeature(&impliedFeatures, "android.hardware.screen.landscape",
1489                                            "one or more activities have specified a landscape orientation");
1490                                } else if (orien == 1 || orien == 7 || orien == 9) {
1491                                    // Requests portrait, sensorPortrait, or reversePortrait.
1492                                    addImpliedFeature(&impliedFeatures, "android.hardware.screen.portrait",
1493                                            "one or more activities have specified a portrait orientation");
1494                                }
1495                            }
1496                        } else if (tag == "uses-library") {
1497                            String8 libraryName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1498                            if (error != "") {
1499                                fprintf(stderr,
1500                                        "ERROR getting 'android:name' attribute for uses-library"
1501                                        " %s\n", error.string());
1502                                goto bail;
1503                            }
1504                            int req = AaptXml::getIntegerAttribute(tree,
1505                                    REQUIRED_ATTR, 1);
1506                            printf("uses-library%s:'%s'\n",
1507                                    req ? "" : "-not-required", ResTable::normalizeForOutput(
1508                                            libraryName.string()).string());
1509                        } else if (tag == "receiver") {
1510                            withinReceiver = true;
1511                            receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1512
1513                            if (error != "") {
1514                                fprintf(stderr,
1515                                        "ERROR getting 'android:name' attribute for receiver:"
1516                                        " %s\n", error.string());
1517                                goto bail;
1518                            }
1519
1520                            String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1521                                    &error);
1522                            if (error == "") {
1523                                if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1524                                    hasBindDeviceAdminPermission = true;
1525                                }
1526                            } else {
1527                                fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1528                                        " receiver '%s': %s\n", receiverName.string(), error.string());
1529                            }
1530                        } else if (tag == "service") {
1531                            withinService = true;
1532                            serviceName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1533
1534                            if (error != "") {
1535                                fprintf(stderr, "ERROR getting 'android:name' attribute for "
1536                                        "service:%s\n", error.string());
1537                                goto bail;
1538                            }
1539
1540                            String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1541                                    &error);
1542                            if (error == "") {
1543                                if (permission == "android.permission.BIND_INPUT_METHOD") {
1544                                    hasBindInputMethodPermission = true;
1545                                } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") {
1546                                    hasBindAccessibilityServicePermission = true;
1547                                } else if (permission == "android.permission.BIND_PRINT_SERVICE") {
1548                                    hasBindPrintServicePermission = true;
1549                                } else if (permission == "android.permission.BIND_NFC_SERVICE") {
1550                                    hasBindNfcServicePermission = true;
1551                                } else if (permission == "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
1552                                    hasBindNotificationListenerServicePermission = true;
1553                                } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
1554                                    hasBindDreamServicePermission = true;
1555                                }
1556                            } else {
1557                                fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1558                                        " service '%s': %s\n", serviceName.string(), error.string());
1559                            }
1560                        } else if (tag == "provider") {
1561                            withinProvider = true;
1562
1563                            bool exported = AaptXml::getResolvedIntegerAttribute(res, tree,
1564                                    EXPORTED_ATTR, &error);
1565                            if (error != "") {
1566                                fprintf(stderr, "ERROR getting 'android:exported' attribute for provider:"
1567                                        " %s\n", error.string());
1568                                goto bail;
1569                            }
1570
1571                            bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute(
1572                                    res, tree, GRANT_URI_PERMISSIONS_ATTR, &error);
1573                            if (error != "") {
1574                                fprintf(stderr, "ERROR getting 'android:grantUriPermissions' attribute for provider:"
1575                                        " %s\n", error.string());
1576                                goto bail;
1577                            }
1578
1579                            String8 permission = AaptXml::getResolvedAttribute(res, tree,
1580                                    PERMISSION_ATTR, &error);
1581                            if (error != "") {
1582                                fprintf(stderr, "ERROR getting 'android:permission' attribute for provider:"
1583                                        " %s\n", error.string());
1584                                goto bail;
1585                            }
1586
1587                            hasRequiredSafAttributes |= exported && grantUriPermissions &&
1588                                permission == "android.permission.MANAGE_DOCUMENTS";
1589
1590                        } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
1591                            String8 metaDataName = AaptXml::getResolvedAttribute(res, tree,
1592                                    NAME_ATTR, &error);
1593                            if (error != "") {
1594                                fprintf(stderr, "ERROR getting 'android:name' attribute for "
1595                                        "meta-data:%s\n", error.string());
1596                                goto bail;
1597                            }
1598                            printf("meta-data: name='%s' ",
1599                                    ResTable::normalizeForOutput(metaDataName.string()).string());
1600                            printResolvedResourceAttribute(res, tree, VALUE_ATTR, String8("value"),
1601                                    &error);
1602                            if (error != "") {
1603                                // Try looking for a RESOURCE_ATTR
1604                                error = "";
1605                                printResolvedResourceAttribute(res, tree, RESOURCE_ATTR,
1606                                        String8("resource"), &error);
1607                                if (error != "") {
1608                                    fprintf(stderr, "ERROR getting 'android:value' or "
1609                                            "'android:resource' attribute for "
1610                                            "meta-data:%s\n", error.string());
1611                                    goto bail;
1612                                }
1613                            }
1614                            printf("\n");
1615                        } else if (withinSupportsInput && tag == "input-type") {
1616                            String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1617                            if (name != "" && error == "") {
1618                                supportedInput.add(name);
1619                            } else {
1620                                fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1621                                        error.string());
1622                                goto bail;
1623                            }
1624                        }
1625                    } else if (withinFeatureGroup && tag == "uses-feature") {
1626                        FeatureGroup& top = featureGroups.editTop();
1627
1628                        String8 name = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error);
1629                        if (name != "" && error == "") {
1630                            top.features.add(name, true);
1631                            addParentFeatures(&top, name);
1632                        } else {
1633                            int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR,
1634                                    &error);
1635                            if (error == "") {
1636                                if (vers > top.openGLESVersion) {
1637                                    top.openGLESVersion = vers;
1638                                }
1639                            }
1640                        }
1641                    }
1642                } else if (depth == 4) {
1643                    if (tag == "intent-filter") {
1644                        hasIntentFilter = true;
1645                        withinIntentFilter = true;
1646                        actMainActivity = false;
1647                        actWidgetReceivers = false;
1648                        actImeService = false;
1649                        actWallpaperService = false;
1650                        actAccessibilityService = false;
1651                        actPrintService = false;
1652                        actDeviceAdminEnabled = false;
1653                        actHostApduService = false;
1654                        actOffHostApduService = false;
1655                        actDocumentsProvider = false;
1656                        actNotificationListenerService = false;
1657                        actDreamService = false;
1658                        actCamera = false;
1659                        actCameraSecure = false;
1660                        catLauncher = false;
1661                    } else if (withinService && tag == "meta-data") {
1662                        String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1663                        if (error != "") {
1664                            fprintf(stderr, "ERROR getting 'android:name' attribute for"
1665                                    " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1666                            goto bail;
1667                        }
1668
1669                        if (name == "android.nfc.cardemulation.host_apdu_service" ||
1670                                name == "android.nfc.cardemulation.off_host_apdu_service") {
1671                            bool offHost = true;
1672                            if (name == "android.nfc.cardemulation.host_apdu_service") {
1673                                offHost = false;
1674                            }
1675
1676                            String8 xmlPath = AaptXml::getResolvedAttribute(res, tree,
1677                                    RESOURCE_ATTR, &error);
1678                            if (error != "") {
1679                                fprintf(stderr, "ERROR getting 'android:resource' attribute for"
1680                                        " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1681                                goto bail;
1682                            }
1683
1684                            Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1685                                    offHost, &error);
1686                            if (error != "") {
1687                                fprintf(stderr, "ERROR getting AID category for service '%s'\n",
1688                                        serviceName.string());
1689                                goto bail;
1690                            }
1691
1692                            const size_t catLen = categories.size();
1693                            for (size_t i = 0; i < catLen; i++) {
1694                                bool paymentCategory = (categories[i] == "payment");
1695                                if (offHost) {
1696                                    hasMetaOffHostPaymentCategory |= paymentCategory;
1697                                } else {
1698                                    hasMetaHostPaymentCategory |= paymentCategory;
1699                                }
1700                            }
1701                        }
1702                    }
1703                } else if ((depth == 5) && withinIntentFilter) {
1704                    String8 action;
1705                    if (tag == "action") {
1706                        action = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1707                        if (error != "") {
1708                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1709                                    error.string());
1710                            goto bail;
1711                        }
1712
1713                        if (withinActivity) {
1714                            if (action == "android.intent.action.MAIN") {
1715                                isMainActivity = true;
1716                                actMainActivity = true;
1717                            } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
1718                                    action == "android.media.action.VIDEO_CAMERA") {
1719                                actCamera = true;
1720                            } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
1721                                actCameraSecure = true;
1722                            }
1723                        } else if (withinReceiver) {
1724                            if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1725                                actWidgetReceivers = true;
1726                            } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1727                                actDeviceAdminEnabled = true;
1728                            }
1729                        } else if (withinService) {
1730                            if (action == "android.view.InputMethod") {
1731                                actImeService = true;
1732                            } else if (action == "android.service.wallpaper.WallpaperService") {
1733                                actWallpaperService = true;
1734                            } else if (action == "android.accessibilityservice.AccessibilityService") {
1735                                actAccessibilityService = true;
1736                            } else if (action == "android.printservice.PrintService") {
1737                                actPrintService = true;
1738                            } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
1739                                actHostApduService = true;
1740                            } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
1741                                actOffHostApduService = true;
1742                            } else if (action == "android.service.notification.NotificationListenerService") {
1743                                actNotificationListenerService = true;
1744                            } else if (action == "android.service.dreams.DreamService") {
1745                                actDreamService = true;
1746                            }
1747                        } else if (withinProvider) {
1748                            if (action == "android.content.action.DOCUMENTS_PROVIDER") {
1749                                actDocumentsProvider = true;
1750                            }
1751                        }
1752                        if (action == "android.intent.action.SEARCH") {
1753                            isSearchable = true;
1754                        }
1755                    }
1756
1757                    if (tag == "category") {
1758                        String8 category = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1759                        if (error != "") {
1760                            fprintf(stderr, "ERROR getting 'name' attribute: %s\n",
1761                                    error.string());
1762                            goto bail;
1763                        }
1764                        if (withinActivity) {
1765                            if (category == "android.intent.category.LAUNCHER") {
1766                                isLauncherActivity = true;
1767                            } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
1768                                isLeanbackLauncherActivity = true;
1769                            } else if (category == "android.intent.category.HOME") {
1770                                catLauncher = true;
1771                            }
1772                        }
1773                    }
1774                }
1775            }
1776
1777            // Pre-1.6 implicitly granted permission compatibility logic
1778            if (targetSdk < 4) {
1779                if (!hasWriteExternalStoragePermission) {
1780                    printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
1781                    printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
1782                            String8("targetSdkVersion < 4"));
1783                    hasWriteExternalStoragePermission = true;
1784                }
1785                if (!hasReadPhoneStatePermission) {
1786                    printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
1787                    printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
1788                            String8("targetSdkVersion < 4"));
1789                }
1790            }
1791
1792            // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1793            // force them to always take READ_EXTERNAL_STORAGE as well.  We always
1794            // do this (regardless of target API version) because we can't have
1795            // an app with write permission but not read permission.
1796            if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
1797                printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"));
1798                printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
1799                        String8("requested WRITE_EXTERNAL_STORAGE"));
1800            }
1801
1802            // Pre-JellyBean call log permission compatibility.
1803            if (targetSdk < 16) {
1804                if (!hasReadCallLogPermission && hasReadContactsPermission) {
1805                    printUsesPermission(String8("android.permission.READ_CALL_LOG"));
1806                    printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
1807                            String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
1808                }
1809                if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
1810                    printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
1811                    printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
1812                            String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
1813                }
1814            }
1815
1816            addImpliedFeature(&impliedFeatures, "android.hardware.touchscreen",
1817                    "default feature for all apps");
1818
1819            const size_t numFeatureGroups = featureGroups.size();
1820            if (numFeatureGroups == 0) {
1821                // If no <feature-group> tags were defined, apply auto-implied features.
1822                printFeatureGroup(commonFeatures, &impliedFeatures);
1823
1824            } else {
1825                // <feature-group> tags are defined, so we ignore implied features and
1826                for (size_t i = 0; i < numFeatureGroups; i++) {
1827                    FeatureGroup& grp = featureGroups.editItemAt(i);
1828
1829                    if (commonFeatures.openGLESVersion > grp.openGLESVersion) {
1830                        grp.openGLESVersion = commonFeatures.openGLESVersion;
1831                    }
1832
1833                    // Merge the features defined in the top level (not inside a <feature-group>)
1834                    // with this feature group.
1835                    const size_t numCommonFeatures = commonFeatures.features.size();
1836                    for (size_t j = 0; j < numCommonFeatures; j++) {
1837                        if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
1838                            grp.features.add(commonFeatures.features.keyAt(j),
1839                                    commonFeatures.features[j]);
1840                        }
1841                    }
1842
1843                    if (!grp.features.isEmpty()) {
1844                        printFeatureGroup(grp);
1845                    }
1846                }
1847            }
1848
1849
1850            if (hasWidgetReceivers) {
1851                printComponentPresence("app-widget");
1852            }
1853            if (hasDeviceAdminReceiver) {
1854                printComponentPresence("device-admin");
1855            }
1856            if (hasImeService) {
1857                printComponentPresence("ime");
1858            }
1859            if (hasWallpaperService) {
1860                printComponentPresence("wallpaper");
1861            }
1862            if (hasAccessibilityService) {
1863                printComponentPresence("accessibility");
1864            }
1865            if (hasPrintService) {
1866                printComponentPresence("print-service");
1867            }
1868            if (hasPaymentService) {
1869                printComponentPresence("payment");
1870            }
1871            if (isSearchable) {
1872                printComponentPresence("search");
1873            }
1874            if (hasDocumentsProvider) {
1875                printComponentPresence("document-provider");
1876            }
1877            if (hasLauncher) {
1878                printComponentPresence("launcher");
1879            }
1880            if (hasNotificationListenerService) {
1881                printComponentPresence("notification-listener");
1882            }
1883            if (hasDreamService) {
1884                printComponentPresence("dream");
1885            }
1886            if (hasCameraActivity) {
1887                printComponentPresence("camera");
1888            }
1889            if (hasCameraSecureActivity) {
1890                printComponentPresence("camera-secure");
1891            }
1892
1893            if (hasMainActivity) {
1894                printf("main\n");
1895            }
1896            if (hasOtherActivities) {
1897                printf("other-activities\n");
1898            }
1899             if (hasOtherReceivers) {
1900                printf("other-receivers\n");
1901            }
1902            if (hasOtherServices) {
1903                printf("other-services\n");
1904            }
1905
1906            // For modern apps, if screen size buckets haven't been specified
1907            // but the new width ranges have, then infer the buckets from them.
1908            if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1909                    && requiresSmallestWidthDp > 0) {
1910                int compatWidth = compatibleWidthLimitDp;
1911                if (compatWidth <= 0) {
1912                    compatWidth = requiresSmallestWidthDp;
1913                }
1914                if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1915                    smallScreen = -1;
1916                } else {
1917                    smallScreen = 0;
1918                }
1919                if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1920                    normalScreen = -1;
1921                } else {
1922                    normalScreen = 0;
1923                }
1924                if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1925                    largeScreen = -1;
1926                } else {
1927                    largeScreen = 0;
1928                }
1929                if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1930                    xlargeScreen = -1;
1931                } else {
1932                    xlargeScreen = 0;
1933                }
1934            }
1935
1936            // Determine default values for any unspecified screen sizes,
1937            // based on the target SDK of the package.  As of 4 (donut)
1938            // the screen size support was introduced, so all default to
1939            // enabled.
1940            if (smallScreen > 0) {
1941                smallScreen = targetSdk >= 4 ? -1 : 0;
1942            }
1943            if (normalScreen > 0) {
1944                normalScreen = -1;
1945            }
1946            if (largeScreen > 0) {
1947                largeScreen = targetSdk >= 4 ? -1 : 0;
1948            }
1949            if (xlargeScreen > 0) {
1950                // Introduced in Gingerbread.
1951                xlargeScreen = targetSdk >= 9 ? -1 : 0;
1952            }
1953            if (anyDensity > 0) {
1954                anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1955                        || compatibleWidthLimitDp > 0) ? -1 : 0;
1956            }
1957            printf("supports-screens:");
1958            if (smallScreen != 0) {
1959                printf(" 'small'");
1960            }
1961            if (normalScreen != 0) {
1962                printf(" 'normal'");
1963            }
1964            if (largeScreen != 0) {
1965                printf(" 'large'");
1966            }
1967            if (xlargeScreen != 0) {
1968                printf(" 'xlarge'");
1969            }
1970            printf("\n");
1971            printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
1972            if (requiresSmallestWidthDp > 0) {
1973                printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1974            }
1975            if (compatibleWidthLimitDp > 0) {
1976                printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1977            }
1978            if (largestWidthLimitDp > 0) {
1979                printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1980            }
1981
1982            printf("locales:");
1983            const size_t NL = locales.size();
1984            for (size_t i=0; i<NL; i++) {
1985                const char* localeStr =  locales[i].string();
1986                if (localeStr == NULL || strlen(localeStr) == 0) {
1987                    localeStr = "--_--";
1988                }
1989                printf(" '%s'", localeStr);
1990            }
1991            printf("\n");
1992
1993            printf("densities:");
1994            const size_t ND = densities.size();
1995            for (size_t i=0; i<ND; i++) {
1996                printf(" '%d'", densities[i]);
1997            }
1998            printf("\n");
1999
2000            AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
2001            if (dir != NULL) {
2002                if (dir->getFileCount() > 0) {
2003                    SortedVector<String8> architectures;
2004                    for (size_t i=0; i<dir->getFileCount(); i++) {
2005                        architectures.add(ResTable::normalizeForOutput(
2006                                dir->getFileName(i).string()));
2007                    }
2008
2009                    bool outputAltNativeCode = false;
2010                    // A multiArch package is one that contains 64-bit and
2011                    // 32-bit versions of native code and expects 3rd-party
2012                    // apps to load these native code libraries. Since most
2013                    // 64-bit systems also support 32-bit apps, the apps
2014                    // loading this multiArch package's code may be either
2015                    // 32-bit or 64-bit.
2016                    if (hasMultiArch) {
2017                        // If this is a multiArch package, report the 64-bit
2018                        // version only. Then as a separate entry, report the
2019                        // rest.
2020                        //
2021                        // If we report the 32-bit architecture, this APK will
2022                        // be installed on a 32-bit device, causing a large waste
2023                        // of bandwidth and disk space. This assumes that
2024                        // the developer of the multiArch package has also
2025                        // made a version that is 32-bit only.
2026                        String8 intel64("x86_64");
2027                        String8 arm64("arm64-v8a");
2028                        ssize_t index = architectures.indexOf(intel64);
2029                        if (index < 0) {
2030                            index = architectures.indexOf(arm64);
2031                        }
2032
2033                        if (index >= 0) {
2034                            printf("native-code: '%s'\n", architectures[index].string());
2035                            architectures.removeAt(index);
2036                            outputAltNativeCode = true;
2037                        }
2038                    }
2039
2040                    const size_t archCount = architectures.size();
2041                    if (archCount > 0) {
2042                        if (outputAltNativeCode) {
2043                            printf("alt-");
2044                        }
2045                        printf("native-code:");
2046                        for (size_t i = 0; i < archCount; i++) {
2047                            printf(" '%s'", architectures[i].string());
2048                        }
2049                        printf("\n");
2050                    }
2051                }
2052                delete dir;
2053            }
2054        } else if (strcmp("badger", option) == 0) {
2055            printf("%s", CONSOLE_DATA);
2056        } else if (strcmp("configurations", option) == 0) {
2057            Vector<ResTable_config> configs;
2058            res.getConfigurations(&configs);
2059            const size_t N = configs.size();
2060            for (size_t i=0; i<N; i++) {
2061                printf("%s\n", configs[i].toString().string());
2062            }
2063        } else {
2064            fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2065            goto bail;
2066        }
2067    }
2068
2069    result = NO_ERROR;
2070
2071bail:
2072    if (asset) {
2073        delete asset;
2074    }
2075    return (result != NO_ERROR);
2076}
2077
2078
2079/*
2080 * Handle the "add" command, which wants to add files to a new or
2081 * pre-existing archive.
2082 */
2083int doAdd(Bundle* bundle)
2084{
2085    ZipFile* zip = NULL;
2086    status_t result = UNKNOWN_ERROR;
2087    const char* zipFileName;
2088
2089    if (bundle->getUpdate()) {
2090        /* avoid confusion */
2091        fprintf(stderr, "ERROR: can't use '-u' with add\n");
2092        goto bail;
2093    }
2094
2095    if (bundle->getFileSpecCount() < 1) {
2096        fprintf(stderr, "ERROR: must specify zip file name\n");
2097        goto bail;
2098    }
2099    zipFileName = bundle->getFileSpecEntry(0);
2100
2101    if (bundle->getFileSpecCount() < 2) {
2102        fprintf(stderr, "NOTE: nothing to do\n");
2103        goto bail;
2104    }
2105
2106    zip = openReadWrite(zipFileName, true);
2107    if (zip == NULL) {
2108        fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2109        goto bail;
2110    }
2111
2112    for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2113        const char* fileName = bundle->getFileSpecEntry(i);
2114
2115        if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
2116            printf(" '%s'... (from gzip)\n", fileName);
2117            result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
2118        } else {
2119            if (bundle->getJunkPath()) {
2120                String8 storageName = String8(fileName).getPathLeaf();
2121                printf(" '%s' as '%s'...\n", fileName,
2122                        ResTable::normalizeForOutput(storageName.string()).string());
2123                result = zip->add(fileName, storageName.string(),
2124                                  bundle->getCompressionMethod(), NULL);
2125            } else {
2126                printf(" '%s'...\n", fileName);
2127                result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2128            }
2129        }
2130        if (result != NO_ERROR) {
2131            fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2132            if (result == NAME_NOT_FOUND) {
2133                fprintf(stderr, ": file not found\n");
2134            } else if (result == ALREADY_EXISTS) {
2135                fprintf(stderr, ": already exists in archive\n");
2136            } else {
2137                fprintf(stderr, "\n");
2138            }
2139            goto bail;
2140        }
2141    }
2142
2143    result = NO_ERROR;
2144
2145bail:
2146    delete zip;
2147    return (result != NO_ERROR);
2148}
2149
2150
2151/*
2152 * Delete files from an existing archive.
2153 */
2154int doRemove(Bundle* bundle)
2155{
2156    ZipFile* zip = NULL;
2157    status_t result = UNKNOWN_ERROR;
2158    const char* zipFileName;
2159
2160    if (bundle->getFileSpecCount() < 1) {
2161        fprintf(stderr, "ERROR: must specify zip file name\n");
2162        goto bail;
2163    }
2164    zipFileName = bundle->getFileSpecEntry(0);
2165
2166    if (bundle->getFileSpecCount() < 2) {
2167        fprintf(stderr, "NOTE: nothing to do\n");
2168        goto bail;
2169    }
2170
2171    zip = openReadWrite(zipFileName, false);
2172    if (zip == NULL) {
2173        fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2174            zipFileName);
2175        goto bail;
2176    }
2177
2178    for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2179        const char* fileName = bundle->getFileSpecEntry(i);
2180        ZipEntry* entry;
2181
2182        entry = zip->getEntryByName(fileName);
2183        if (entry == NULL) {
2184            printf(" '%s' NOT FOUND\n", fileName);
2185            continue;
2186        }
2187
2188        result = zip->remove(entry);
2189
2190        if (result != NO_ERROR) {
2191            fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2192                bundle->getFileSpecEntry(i), zipFileName);
2193            goto bail;
2194        }
2195    }
2196
2197    /* update the archive */
2198    zip->flush();
2199
2200bail:
2201    delete zip;
2202    return (result != NO_ERROR);
2203}
2204
2205static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
2206    const size_t numDirs = dir->getDirs().size();
2207    for (size_t i = 0; i < numDirs; i++) {
2208        bool ignore = ignoreConfig;
2209        const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2210        const char* dirStr = subDir->getLeaf().string();
2211        if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2212            ignore = true;
2213        }
2214        status_t err = addResourcesToBuilder(subDir, builder, ignore);
2215        if (err != NO_ERROR) {
2216            return err;
2217        }
2218    }
2219
2220    const size_t numFiles = dir->getFiles().size();
2221    for (size_t i = 0; i < numFiles; i++) {
2222        sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2223        const size_t numConfigs = gp->getFiles().size();
2224        for (size_t j = 0; j < numConfigs; j++) {
2225            status_t err = NO_ERROR;
2226            if (ignoreConfig) {
2227                err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2228            } else {
2229                err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2230            }
2231            if (err != NO_ERROR) {
2232                fprintf(stderr, "Failed to add %s (%s) to builder.\n",
2233                        gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
2234                return err;
2235            }
2236        }
2237    }
2238    return NO_ERROR;
2239}
2240
2241static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2242    if (split->isBase()) {
2243        return original;
2244    }
2245
2246    String8 ext(original.getPathExtension());
2247    if (ext == String8(".apk")) {
2248        return String8::format("%s_%s%s",
2249                original.getBasePath().string(),
2250                split->getDirectorySafeName().string(),
2251                ext.string());
2252    }
2253
2254    return String8::format("%s_%s", original.string(),
2255            split->getDirectorySafeName().string());
2256}
2257
2258/*
2259 * Package up an asset directory and associated application files.
2260 */
2261int doPackage(Bundle* bundle)
2262{
2263    const char* outputAPKFile;
2264    int retVal = 1;
2265    status_t err;
2266    sp<AaptAssets> assets;
2267    int N;
2268    FILE* fp;
2269    String8 dependencyFile;
2270    sp<ApkBuilder> builder;
2271
2272    // -c en_XA or/and ar_XB means do pseudolocalization
2273    sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2274    err = configFilter->parse(bundle->getConfigurations());
2275    if (err != NO_ERROR) {
2276        goto bail;
2277    }
2278    if (configFilter->containsPseudo()) {
2279        bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2280    }
2281    if (configFilter->containsPseudoBidi()) {
2282        bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
2283    }
2284
2285    N = bundle->getFileSpecCount();
2286    if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
2287            && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
2288        fprintf(stderr, "ERROR: no input files\n");
2289        goto bail;
2290    }
2291
2292    outputAPKFile = bundle->getOutputAPKFile();
2293
2294    // Make sure the filenames provided exist and are of the appropriate type.
2295    if (outputAPKFile) {
2296        FileType type;
2297        type = getFileType(outputAPKFile);
2298        if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2299            fprintf(stderr,
2300                "ERROR: output file '%s' exists but is not regular file\n",
2301                outputAPKFile);
2302            goto bail;
2303        }
2304    }
2305
2306    // Load the assets.
2307    assets = new AaptAssets();
2308
2309    // Set up the resource gathering in assets if we're going to generate
2310    // dependency files. Every time we encounter a resource while slurping
2311    // the tree, we'll add it to these stores so we have full resource paths
2312    // to write to a dependency file.
2313    if (bundle->getGenDependencies()) {
2314        sp<FilePathStore> resPathStore = new FilePathStore;
2315        assets->setFullResPaths(resPathStore);
2316        sp<FilePathStore> assetPathStore = new FilePathStore;
2317        assets->setFullAssetPaths(assetPathStore);
2318    }
2319
2320    err = assets->slurpFromArgs(bundle);
2321    if (err < 0) {
2322        goto bail;
2323    }
2324
2325    if (bundle->getVerbose()) {
2326        assets->print(String8());
2327    }
2328
2329    // Create the ApkBuilder, which will collect the compiled files
2330    // to write to the final APK (or sets of APKs if we are building
2331    // a Split APK.
2332    builder = new ApkBuilder(configFilter);
2333
2334    // If we are generating a Split APK, find out which configurations to split on.
2335    if (bundle->getSplitConfigurations().size() > 0) {
2336        const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2337        const size_t numSplits = splitStrs.size();
2338        for (size_t i = 0; i < numSplits; i++) {
2339            std::set<ConfigDescription> configs;
2340            if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2341                fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2342                goto bail;
2343            }
2344
2345            err = builder->createSplitForConfigs(configs);
2346            if (err != NO_ERROR) {
2347                goto bail;
2348            }
2349        }
2350    }
2351
2352    // If they asked for any fileAs that need to be compiled, do so.
2353    if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
2354        err = buildResources(bundle, assets, builder);
2355        if (err != 0) {
2356            goto bail;
2357        }
2358    }
2359
2360    // At this point we've read everything and processed everything.  From here
2361    // on out it's just writing output files.
2362    if (SourcePos::hasErrors()) {
2363        goto bail;
2364    }
2365
2366    // Update symbols with information about which ones are needed as Java symbols.
2367    assets->applyJavaSymbols();
2368    if (SourcePos::hasErrors()) {
2369        goto bail;
2370    }
2371
2372    // If we've been asked to generate a dependency file, do that here
2373    if (bundle->getGenDependencies()) {
2374        // If this is the packaging step, generate the dependency file next to
2375        // the output apk (e.g. bin/resources.ap_.d)
2376        if (outputAPKFile) {
2377            dependencyFile = String8(outputAPKFile);
2378            // Add the .d extension to the dependency file.
2379            dependencyFile.append(".d");
2380        } else {
2381            // Else if this is the R.java dependency generation step,
2382            // generate the dependency file in the R.java package subdirectory
2383            // e.g. gen/com/foo/app/R.java.d
2384            dependencyFile = String8(bundle->getRClassDir());
2385            dependencyFile.appendPath("R.java.d");
2386        }
2387        // Make sure we have a clean dependency file to start with
2388        fp = fopen(dependencyFile, "w");
2389        fclose(fp);
2390    }
2391
2392    // Write out R.java constants
2393    if (!assets->havePrivateSymbols()) {
2394        if (bundle->getCustomPackage() == NULL) {
2395            // Write the R.java file into the appropriate class directory
2396            // e.g. gen/com/foo/app/R.java
2397            err = writeResourceSymbols(bundle, assets, assets->getPackage(), true,
2398                    bundle->getBuildSharedLibrary());
2399        } else {
2400            const String8 customPkg(bundle->getCustomPackage());
2401            err = writeResourceSymbols(bundle, assets, customPkg, true,
2402                    bundle->getBuildSharedLibrary());
2403        }
2404        if (err < 0) {
2405            goto bail;
2406        }
2407        // If we have library files, we're going to write our R.java file into
2408        // the appropriate class directory for those libraries as well.
2409        // e.g. gen/com/foo/app/lib/R.java
2410        if (bundle->getExtraPackages() != NULL) {
2411            // Split on colon
2412            String8 libs(bundle->getExtraPackages());
2413            char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2414            while (packageString != NULL) {
2415                // Write the R.java file out with the correct package name
2416                err = writeResourceSymbols(bundle, assets, String8(packageString), true,
2417                        bundle->getBuildSharedLibrary());
2418                if (err < 0) {
2419                    goto bail;
2420                }
2421                packageString = strtok(NULL, ":");
2422            }
2423            libs.unlockBuffer();
2424        }
2425    } else {
2426        err = writeResourceSymbols(bundle, assets, assets->getPackage(), false, false);
2427        if (err < 0) {
2428            goto bail;
2429        }
2430        err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true, false);
2431        if (err < 0) {
2432            goto bail;
2433        }
2434    }
2435
2436    // Write out the ProGuard file
2437    err = writeProguardFile(bundle, assets);
2438    if (err < 0) {
2439        goto bail;
2440    }
2441
2442    // Write the apk
2443    if (outputAPKFile) {
2444        // Gather all resources and add them to the APK Builder. The builder will then
2445        // figure out which Split they belong in.
2446        err = addResourcesToBuilder(assets, builder);
2447        if (err != NO_ERROR) {
2448            goto bail;
2449        }
2450
2451        const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2452        const size_t numSplits = splits.size();
2453        for (size_t i = 0; i < numSplits; i++) {
2454            const sp<ApkSplit>& split = splits[i];
2455            String8 outputPath = buildApkName(String8(outputAPKFile), split);
2456            err = writeAPK(bundle, outputPath, split);
2457            if (err != NO_ERROR) {
2458                fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2459                goto bail;
2460            }
2461        }
2462    }
2463
2464    // If we've been asked to generate a dependency file, we need to finish up here.
2465    // the writeResourceSymbols and writeAPK functions have already written the target
2466    // half of the dependency file, now we need to write the prerequisites. (files that
2467    // the R.java file or .ap_ file depend on)
2468    if (bundle->getGenDependencies()) {
2469        // Now that writeResourceSymbols or writeAPK has taken care of writing
2470        // the targets to our dependency file, we'll write the prereqs
2471        fp = fopen(dependencyFile, "a+");
2472        fprintf(fp, " : ");
2473        bool includeRaw = (outputAPKFile != NULL);
2474        err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2475        // Also manually add the AndroidManifeset since it's not under res/ or assets/
2476        // and therefore was not added to our pathstores during slurping
2477        fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2478        fclose(fp);
2479    }
2480
2481    retVal = 0;
2482bail:
2483    if (SourcePos::hasErrors()) {
2484        SourcePos::printErrors(stderr);
2485    }
2486    return retVal;
2487}
2488
2489/*
2490 * Do PNG Crunching
2491 * PRECONDITIONS
2492 *  -S flag points to a source directory containing drawable* folders
2493 *  -C flag points to destination directory. The folder structure in the
2494 *     source directory will be mirrored to the destination (cache) directory
2495 *
2496 * POSTCONDITIONS
2497 *  Destination directory will be updated to match the PNG files in
2498 *  the source directory.
2499 */
2500int doCrunch(Bundle* bundle)
2501{
2502    fprintf(stdout, "Crunching PNG Files in ");
2503    fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2504    fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2505
2506    updatePreProcessedCache(bundle);
2507
2508    return NO_ERROR;
2509}
2510
2511/*
2512 * Do PNG Crunching on a single flag
2513 *  -i points to a single png file
2514 *  -o points to a single png output file
2515 */
2516int doSingleCrunch(Bundle* bundle)
2517{
2518    fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2519    fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2520
2521    String8 input(bundle->getSingleCrunchInputFile());
2522    String8 output(bundle->getSingleCrunchOutputFile());
2523
2524    if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2525        // we can't return the status_t as it gets truncate to the lower 8 bits.
2526        return 42;
2527    }
2528
2529    return NO_ERROR;
2530}
2531
2532int runInDaemonMode(Bundle* bundle) {
2533    std::cout << "Ready" << std::endl;
2534    for (std::string cmd; std::getline(std::cin, cmd);) {
2535        if (cmd == "quit") {
2536            return NO_ERROR;
2537        } else if (cmd == "s") {
2538            // Two argument crunch
2539            std::string inputFile, outputFile;
2540            std::getline(std::cin, inputFile);
2541            std::getline(std::cin, outputFile);
2542            bundle->setSingleCrunchInputFile(inputFile.c_str());
2543            bundle->setSingleCrunchOutputFile(outputFile.c_str());
2544            std::cout << "Crunching " << inputFile << std::endl;
2545            if (doSingleCrunch(bundle) != NO_ERROR) {
2546                std::cout << "Error" << std::endl;
2547            }
2548            std::cout << "Done" << std::endl;
2549        } else {
2550            // in case of invalid command, just bail out.
2551            std::cerr << "Unknown command" << std::endl;
2552            return -1;
2553        }
2554    }
2555    return -1;
2556}
2557
2558char CONSOLE_DATA[2925] = {
2559    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2560    32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2561    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2562    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2563    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2564    86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2565    62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2566    32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2567    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2568    81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2569    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2570    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2571    32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2572    59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2573    32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2574    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2575    59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2576    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2577    10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2578    32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2579    58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2580    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2581    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2582    47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2583    121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2584    32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2585    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2586    81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2587    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2588    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2589    32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2590    59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2591    32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2592    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2593    59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2594    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2595    32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2596    32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2597    70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2598    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2599    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2600    32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2601    81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2602    32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2603    32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2604    81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2605    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2606    32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2607    59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2608    60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2609    32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2610    61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2611    61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2612    46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2613    32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2614    59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2615    109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2616    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2617    67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2618    59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2619    61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2620    32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2621    59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2622    73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2623    59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2624    32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2625    59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2626    46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2627    97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2628    46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2629    119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2630    59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2631    32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2632    32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2633    119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2634    59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2635    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2636    32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2637    81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2638    87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2639    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2640    81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2641    45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2642    32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2643    32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2644    81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2645    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2646    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2647    59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2648    59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2649    32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2650    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2651    81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2652    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2653    10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2654    32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2655    81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2656    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2657    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2658    81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2659    32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2660    32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2661    32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2662    81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2663    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2664    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2665    58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2666    61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2667    32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2668    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2669    81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2670    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2671    32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2672    32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2673    81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2674    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2675    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2676    59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2677    59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2678    32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2679    32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2680    58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2681    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2682    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2683    61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2684    59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2685    32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2686    32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2687    32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2688    61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2689    32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2690    32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2691    59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2692    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2693    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2694    59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2695    59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2696    32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2697    32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2698    32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2699    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2700    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2701    46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2702    46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2703    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2704    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2705    59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2706    59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2707    32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2708    32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2709    32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2710    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2711    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2712    59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2713    59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2714    32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2715    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2716    32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2717    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2718    10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2719    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2720    32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2721    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2722  };
2723