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