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