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