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