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