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