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