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