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