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