Command.cpp revision 1bcc3d691fd1cb68abced62caf3024e2bb228561
1//
2// Copyright 2006 The Android Open Source Project
3//
4// Android Asset Packaging Tool main entry point.
5//
6#include "Main.h"
7#include "Bundle.h"
8#include "ResourceFilter.h"
9#include "ResourceTable.h"
10#include "Images.h"
11#include "XMLNode.h"
12
13#include <utils/Log.h>
14#include <utils/threads.h>
15#include <utils/List.h>
16#include <utils/Errors.h>
17
18#include <fcntl.h>
19#include <errno.h>
20
21using namespace android;
22
23/*
24 * Show version info.  All the cool kids do it.
25 */
26int doVersion(Bundle* bundle)
27{
28    if (bundle->getFileSpecCount() != 0)
29        printf("(ignoring extra arguments)\n");
30    printf("Android Asset Packaging Tool, v0.2\n");
31
32    return 0;
33}
34
35
36/*
37 * Open the file read only.  The call fails if the file doesn't exist.
38 *
39 * Returns NULL on failure.
40 */
41ZipFile* openReadOnly(const char* fileName)
42{
43    ZipFile* zip;
44    status_t result;
45
46    zip = new ZipFile;
47    result = zip->open(fileName, ZipFile::kOpenReadOnly);
48    if (result != NO_ERROR) {
49        if (result == NAME_NOT_FOUND)
50            fprintf(stderr, "ERROR: '%s' not found\n", fileName);
51        else if (result == PERMISSION_DENIED)
52            fprintf(stderr, "ERROR: '%s' access denied\n", fileName);
53        else
54            fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n",
55                fileName);
56        delete zip;
57        return NULL;
58    }
59
60    return zip;
61}
62
63/*
64 * Open the file read-write.  The file will be created if it doesn't
65 * already exist and "okayToCreate" is set.
66 *
67 * Returns NULL on failure.
68 */
69ZipFile* openReadWrite(const char* fileName, bool okayToCreate)
70{
71    ZipFile* zip = NULL;
72    status_t result;
73    int flags;
74
75    flags = ZipFile::kOpenReadWrite;
76    if (okayToCreate)
77        flags |= ZipFile::kOpenCreate;
78
79    zip = new ZipFile;
80    result = zip->open(fileName, flags);
81    if (result != NO_ERROR) {
82        delete zip;
83        zip = NULL;
84        goto bail;
85    }
86
87bail:
88    return zip;
89}
90
91
92/*
93 * Return a short string describing the compression method.
94 */
95const char* compressionName(int method)
96{
97    if (method == ZipEntry::kCompressStored)
98        return "Stored";
99    else if (method == ZipEntry::kCompressDeflated)
100        return "Deflated";
101    else
102        return "Unknown";
103}
104
105/*
106 * Return the percent reduction in size (0% == no compression).
107 */
108int calcPercent(long uncompressedLen, long compressedLen)
109{
110    if (!uncompressedLen)
111        return 0;
112    else
113        return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5);
114}
115
116/*
117 * Handle the "list" command, which can be a simple file dump or
118 * a verbose listing.
119 *
120 * The verbose listing closely matches the output of the Info-ZIP "unzip"
121 * command.
122 */
123int doList(Bundle* bundle)
124{
125    int result = 1;
126    ZipFile* zip = NULL;
127    const ZipEntry* entry;
128    long totalUncLen, totalCompLen;
129    const char* zipFileName;
130
131    if (bundle->getFileSpecCount() != 1) {
132        fprintf(stderr, "ERROR: specify zip file name (only)\n");
133        goto bail;
134    }
135    zipFileName = bundle->getFileSpecEntry(0);
136
137    zip = openReadOnly(zipFileName);
138    if (zip == NULL)
139        goto bail;
140
141    int count, i;
142
143    if (bundle->getVerbose()) {
144        printf("Archive:  %s\n", zipFileName);
145        printf(
146            " Length   Method    Size  Ratio   Offset      Date  Time  CRC-32    Name\n");
147        printf(
148            "--------  ------  ------- -----  -------      ----  ----  ------    ----\n");
149    }
150
151    totalUncLen = totalCompLen = 0;
152
153    count = zip->getNumEntries();
154    for (i = 0; i < count; i++) {
155        entry = zip->getEntryByIndex(i);
156        if (bundle->getVerbose()) {
157            char dateBuf[32];
158            time_t when;
159
160            when = entry->getModWhen();
161            strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M",
162                localtime(&when));
163
164            printf("%8ld  %-7.7s %7ld %3d%%  %8zd  %s  %08lx  %s\n",
165                (long) entry->getUncompressedLen(),
166                compressionName(entry->getCompressionMethod()),
167                (long) entry->getCompressedLen(),
168                calcPercent(entry->getUncompressedLen(),
169                            entry->getCompressedLen()),
170                (size_t) entry->getLFHOffset(),
171                dateBuf,
172                entry->getCRC32(),
173                entry->getFileName());
174        } else {
175            printf("%s\n", entry->getFileName());
176        }
177
178        totalUncLen += entry->getUncompressedLen();
179        totalCompLen += entry->getCompressedLen();
180    }
181
182    if (bundle->getVerbose()) {
183        printf(
184        "--------          -------  ---                            -------\n");
185        printf("%8ld          %7ld  %2d%%                            %d files\n",
186            totalUncLen,
187            totalCompLen,
188            calcPercent(totalUncLen, totalCompLen),
189            zip->getNumEntries());
190    }
191
192    if (bundle->getAndroidList()) {
193        AssetManager assets;
194        if (!assets.addAssetPath(String8(zipFileName), NULL)) {
195            fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n");
196            goto bail;
197        }
198
199        const ResTable& res = assets.getResources(false);
200        if (&res == NULL) {
201            printf("\nNo resource table found.\n");
202        } else {
203#ifndef HAVE_ANDROID_OS
204            printf("\nResource table:\n");
205            res.print(false);
206#endif
207        }
208
209        Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
210                                                   Asset::ACCESS_BUFFER);
211        if (manifestAsset == NULL) {
212            printf("\nNo AndroidManifest.xml found.\n");
213        } else {
214            printf("\nAndroid manifest:\n");
215            ResXMLTree tree;
216            tree.setTo(manifestAsset->getBuffer(true),
217                       manifestAsset->getLength());
218            printXMLBlock(&tree);
219        }
220        delete manifestAsset;
221    }
222
223    result = 0;
224
225bail:
226    delete zip;
227    return result;
228}
229
230static ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes)
231{
232    size_t N = tree.getAttributeCount();
233    for (size_t i=0; i<N; i++) {
234        if (tree.getAttributeNameResID(i) == attrRes) {
235            return (ssize_t)i;
236        }
237    }
238    return -1;
239}
240
241String8 getAttribute(const ResXMLTree& tree, const char* ns,
242                            const char* attr, String8* outError)
243{
244    ssize_t idx = tree.indexOfAttribute(ns, attr);
245    if (idx < 0) {
246        return String8();
247    }
248    Res_value value;
249    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
250        if (value.dataType != Res_value::TYPE_STRING) {
251            if (outError != NULL) *outError = "attribute is not a string value";
252            return String8();
253        }
254    }
255    size_t len;
256    const uint16_t* str = tree.getAttributeStringValue(idx, &len);
257    return str ? String8(str, len) : String8();
258}
259
260static String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError)
261{
262    ssize_t idx = indexOfAttribute(tree, attrRes);
263    if (idx < 0) {
264        return String8();
265    }
266    Res_value value;
267    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
268        if (value.dataType != Res_value::TYPE_STRING) {
269            if (outError != NULL) *outError = "attribute is not a string value";
270            return String8();
271        }
272    }
273    size_t len;
274    const uint16_t* str = tree.getAttributeStringValue(idx, &len);
275    return str ? String8(str, len) : String8();
276}
277
278static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes,
279        String8* outError, int32_t defValue = -1)
280{
281    ssize_t idx = indexOfAttribute(tree, attrRes);
282    if (idx < 0) {
283        return defValue;
284    }
285    Res_value value;
286    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
287        if (value.dataType < Res_value::TYPE_FIRST_INT
288                || value.dataType > Res_value::TYPE_LAST_INT) {
289            if (outError != NULL) *outError = "attribute is not an integer value";
290            return defValue;
291        }
292    }
293    return value.data;
294}
295
296static int32_t getResolvedIntegerAttribute(const ResTable* resTable, const ResXMLTree& tree,
297        uint32_t attrRes, String8* outError, int32_t defValue = -1)
298{
299    ssize_t idx = indexOfAttribute(tree, attrRes);
300    if (idx < 0) {
301        return defValue;
302    }
303    Res_value value;
304    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
305        if (value.dataType == Res_value::TYPE_REFERENCE) {
306            resTable->resolveReference(&value, 0);
307        }
308        if (value.dataType < Res_value::TYPE_FIRST_INT
309                || value.dataType > Res_value::TYPE_LAST_INT) {
310            if (outError != NULL) *outError = "attribute is not an integer value";
311            return defValue;
312        }
313    }
314    return value.data;
315}
316
317static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree,
318        uint32_t attrRes, String8* outError)
319{
320    ssize_t idx = indexOfAttribute(tree, attrRes);
321    if (idx < 0) {
322        return String8();
323    }
324    Res_value value;
325    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
326        if (value.dataType == Res_value::TYPE_STRING) {
327            size_t len;
328            const uint16_t* str = tree.getAttributeStringValue(idx, &len);
329            return str ? String8(str, len) : String8();
330        }
331        resTable->resolveReference(&value, 0);
332        if (value.dataType != Res_value::TYPE_STRING) {
333            if (outError != NULL) *outError = "attribute is not a string value";
334            return String8();
335        }
336    }
337    size_t len;
338    const Res_value* value2 = &value;
339    const char16_t* str = const_cast<ResTable*>(resTable)->valueToString(value2, 0, NULL, &len);
340    return str ? String8(str, len) : String8();
341}
342
343// These are attribute resource constants for the platform, as found
344// in android.R.attr
345enum {
346    LABEL_ATTR = 0x01010001,
347    ICON_ATTR = 0x01010002,
348    NAME_ATTR = 0x01010003,
349    DEBUGGABLE_ATTR = 0x0101000f,
350    VERSION_CODE_ATTR = 0x0101021b,
351    VERSION_NAME_ATTR = 0x0101021c,
352    SCREEN_ORIENTATION_ATTR = 0x0101001e,
353    MIN_SDK_VERSION_ATTR = 0x0101020c,
354    MAX_SDK_VERSION_ATTR = 0x01010271,
355    REQ_TOUCH_SCREEN_ATTR = 0x01010227,
356    REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
357    REQ_HARD_KEYBOARD_ATTR = 0x01010229,
358    REQ_NAVIGATION_ATTR = 0x0101022a,
359    REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
360    TARGET_SDK_VERSION_ATTR = 0x01010270,
361    TEST_ONLY_ATTR = 0x01010272,
362    ANY_DENSITY_ATTR = 0x0101026c,
363    GL_ES_VERSION_ATTR = 0x01010281,
364    SMALL_SCREEN_ATTR = 0x01010284,
365    NORMAL_SCREEN_ATTR = 0x01010285,
366    LARGE_SCREEN_ATTR = 0x01010286,
367    XLARGE_SCREEN_ATTR = 0x010102bf,
368    REQUIRED_ATTR = 0x0101028e,
369    SCREEN_SIZE_ATTR = 0x010102ca,
370    SCREEN_DENSITY_ATTR = 0x010102cb,
371    REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
372    COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
373    LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
374    PUBLIC_KEY_ATTR = 0x010103a6,
375};
376
377const char *getComponentName(String8 &pkgName, String8 &componentName) {
378    ssize_t idx = componentName.find(".");
379    String8 retStr(pkgName);
380    if (idx == 0) {
381        retStr += componentName;
382    } else if (idx < 0) {
383        retStr += ".";
384        retStr += componentName;
385    } else {
386        return componentName.string();
387    }
388    return retStr.string();
389}
390
391static void printCompatibleScreens(ResXMLTree& tree) {
392    size_t len;
393    ResXMLTree::event_code_t code;
394    int depth = 0;
395    bool first = true;
396    printf("compatible-screens:");
397    while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
398        if (code == ResXMLTree::END_TAG) {
399            depth--;
400            if (depth < 0) {
401                break;
402            }
403            continue;
404        }
405        if (code != ResXMLTree::START_TAG) {
406            continue;
407        }
408        depth++;
409        String8 tag(tree.getElementName(&len));
410        if (tag == "screen") {
411            int32_t screenSize = getIntegerAttribute(tree,
412                    SCREEN_SIZE_ATTR, NULL, -1);
413            int32_t screenDensity = getIntegerAttribute(tree,
414                    SCREEN_DENSITY_ATTR, NULL, -1);
415            if (screenSize > 0 && screenDensity > 0) {
416                if (!first) {
417                    printf(",");
418                }
419                first = false;
420                printf("'%d/%d'", screenSize, screenDensity);
421            }
422        }
423    }
424    printf("\n");
425}
426
427/*
428 * Handle the "dump" command, to extract select data from an archive.
429 */
430extern char CONSOLE_DATA[2925]; // see EOF
431int doDump(Bundle* bundle)
432{
433    status_t result = UNKNOWN_ERROR;
434    Asset* asset = NULL;
435
436    if (bundle->getFileSpecCount() < 1) {
437        fprintf(stderr, "ERROR: no dump option specified\n");
438        return 1;
439    }
440
441    if (bundle->getFileSpecCount() < 2) {
442        fprintf(stderr, "ERROR: no dump file specified\n");
443        return 1;
444    }
445
446    const char* option = bundle->getFileSpecEntry(0);
447    const char* filename = bundle->getFileSpecEntry(1);
448
449    AssetManager assets;
450    void* assetsCookie;
451    if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
452        fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
453        return 1;
454    }
455
456    // Make a dummy config for retrieving resources...  we need to supply
457    // non-default values for some configs so that we can retrieve resources
458    // in the app that don't have a default.  The most important of these is
459    // the API version because key resources like icons will have an implicit
460    // version if they are using newer config types like density.
461    ResTable_config config;
462    config.language[0] = 'e';
463    config.language[1] = 'n';
464    config.country[0] = 'U';
465    config.country[1] = 'S';
466    config.orientation = ResTable_config::ORIENTATION_PORT;
467    config.density = ResTable_config::DENSITY_MEDIUM;
468    config.sdkVersion = 10000; // Very high.
469    config.screenWidthDp = 320;
470    config.screenHeightDp = 480;
471    config.smallestScreenWidthDp = 320;
472    assets.setConfiguration(config);
473
474    const ResTable& res = assets.getResources(false);
475    if (&res == NULL) {
476        fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
477        goto bail;
478    }
479
480    if (strcmp("resources", option) == 0) {
481#ifndef HAVE_ANDROID_OS
482        res.print(bundle->getValues());
483#endif
484
485    } else if (strcmp("strings", option) == 0) {
486        const ResStringPool* pool = res.getTableStringBlock(0);
487        printStringPool(pool);
488
489    } else if (strcmp("xmltree", option) == 0) {
490        if (bundle->getFileSpecCount() < 3) {
491            fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
492            goto bail;
493        }
494
495        for (int i=2; i<bundle->getFileSpecCount(); i++) {
496            const char* resname = bundle->getFileSpecEntry(i);
497            ResXMLTree tree;
498            asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
499            if (asset == NULL) {
500                fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
501                goto bail;
502            }
503
504            if (tree.setTo(asset->getBuffer(true),
505                           asset->getLength()) != NO_ERROR) {
506                fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
507                goto bail;
508            }
509            tree.restart();
510            printXMLBlock(&tree);
511            tree.uninit();
512            delete asset;
513            asset = NULL;
514        }
515
516    } else if (strcmp("xmlstrings", option) == 0) {
517        if (bundle->getFileSpecCount() < 3) {
518            fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
519            goto bail;
520        }
521
522        for (int i=2; i<bundle->getFileSpecCount(); i++) {
523            const char* resname = bundle->getFileSpecEntry(i);
524            ResXMLTree tree;
525            asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
526            if (asset == NULL) {
527                fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
528                goto bail;
529            }
530
531            if (tree.setTo(asset->getBuffer(true),
532                           asset->getLength()) != NO_ERROR) {
533                fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
534                goto bail;
535            }
536            printStringPool(&tree.getStrings());
537            delete asset;
538            asset = NULL;
539        }
540
541    } else {
542        ResXMLTree tree;
543        asset = assets.openNonAsset("AndroidManifest.xml",
544                                            Asset::ACCESS_BUFFER);
545        if (asset == NULL) {
546            fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
547            goto bail;
548        }
549
550        if (tree.setTo(asset->getBuffer(true),
551                       asset->getLength()) != NO_ERROR) {
552            fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
553            goto bail;
554        }
555        tree.restart();
556
557        if (strcmp("permissions", option) == 0) {
558            size_t len;
559            ResXMLTree::event_code_t code;
560            int depth = 0;
561            while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
562                if (code == ResXMLTree::END_TAG) {
563                    depth--;
564                    continue;
565                }
566                if (code != ResXMLTree::START_TAG) {
567                    continue;
568                }
569                depth++;
570                String8 tag(tree.getElementName(&len));
571                //printf("Depth %d tag %s\n", depth, tag.string());
572                if (depth == 1) {
573                    if (tag != "manifest") {
574                        fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
575                        goto bail;
576                    }
577                    String8 pkg = getAttribute(tree, NULL, "package", NULL);
578                    printf("package: %s\n", pkg.string());
579                } else if (depth == 2 && tag == "permission") {
580                    String8 error;
581                    String8 name = getAttribute(tree, NAME_ATTR, &error);
582                    if (error != "") {
583                        fprintf(stderr, "ERROR: %s\n", error.string());
584                        goto bail;
585                    }
586                    printf("permission: %s\n", name.string());
587                } else if (depth == 2 && tag == "uses-permission") {
588                    String8 error;
589                    String8 name = getAttribute(tree, NAME_ATTR, &error);
590                    if (error != "") {
591                        fprintf(stderr, "ERROR: %s\n", error.string());
592                        goto bail;
593                    }
594                    printf("uses-permission: %s\n", name.string());
595                    int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
596                    if (!req) {
597                        printf("optional-permission: %s\n", name.string());
598                    }
599                }
600            }
601        } else if (strcmp("badging", option) == 0) {
602            Vector<String8> locales;
603            res.getLocales(&locales);
604
605            Vector<ResTable_config> configs;
606            res.getConfigurations(&configs);
607            SortedVector<int> densities;
608            const size_t NC = configs.size();
609            for (size_t i=0; i<NC; i++) {
610                int dens = configs[i].density;
611                if (dens == 0) dens = 160;
612                densities.add(dens);
613            }
614
615            size_t len;
616            ResXMLTree::event_code_t code;
617            int depth = 0;
618            String8 error;
619            bool withinActivity = false;
620            bool isMainActivity = false;
621            bool isLauncherActivity = false;
622            bool isSearchable = false;
623            bool withinApplication = false;
624            bool withinReceiver = false;
625            bool withinService = false;
626            bool withinIntentFilter = false;
627            bool hasMainActivity = false;
628            bool hasOtherActivities = false;
629            bool hasOtherReceivers = false;
630            bool hasOtherServices = false;
631            bool hasWallpaperService = false;
632            bool hasImeService = false;
633            bool hasWidgetReceivers = false;
634            bool hasIntentFilter = false;
635            bool actMainActivity = false;
636            bool actWidgetReceivers = false;
637            bool actImeService = false;
638            bool actWallpaperService = false;
639
640            // These two implement the implicit permissions that are granted
641            // to pre-1.6 applications.
642            bool hasWriteExternalStoragePermission = false;
643            bool hasReadPhoneStatePermission = false;
644
645            // If an app requests write storage, they will also get read storage.
646            bool hasReadExternalStoragePermission = false;
647
648            // Implement transition to read and write call log.
649            bool hasReadContactsPermission = false;
650            bool hasWriteContactsPermission = false;
651            bool hasReadCallLogPermission = false;
652            bool hasWriteCallLogPermission = false;
653
654            // This next group of variables is used to implement a group of
655            // backward-compatibility heuristics necessitated by the addition of
656            // some new uses-feature constants in 2.1 and 2.2. In most cases, the
657            // heuristic is "if an app requests a permission but doesn't explicitly
658            // request the corresponding <uses-feature>, presume it's there anyway".
659            bool specCameraFeature = false; // camera-related
660            bool specCameraAutofocusFeature = false;
661            bool reqCameraAutofocusFeature = false;
662            bool reqCameraFlashFeature = false;
663            bool hasCameraPermission = false;
664            bool specLocationFeature = false; // location-related
665            bool specNetworkLocFeature = false;
666            bool reqNetworkLocFeature = false;
667            bool specGpsFeature = false;
668            bool reqGpsFeature = false;
669            bool hasMockLocPermission = false;
670            bool hasCoarseLocPermission = false;
671            bool hasGpsPermission = false;
672            bool hasGeneralLocPermission = false;
673            bool specBluetoothFeature = false; // Bluetooth API-related
674            bool hasBluetoothPermission = false;
675            bool specMicrophoneFeature = false; // microphone-related
676            bool hasRecordAudioPermission = false;
677            bool specWiFiFeature = false;
678            bool hasWiFiPermission = false;
679            bool specTelephonyFeature = false; // telephony-related
680            bool reqTelephonySubFeature = false;
681            bool hasTelephonyPermission = false;
682            bool specTouchscreenFeature = false; // touchscreen-related
683            bool specMultitouchFeature = false;
684            bool reqDistinctMultitouchFeature = false;
685            bool specScreenPortraitFeature = false;
686            bool specScreenLandscapeFeature = false;
687            bool reqScreenPortraitFeature = false;
688            bool reqScreenLandscapeFeature = false;
689            // 2.2 also added some other features that apps can request, but that
690            // have no corresponding permission, so we cannot implement any
691            // back-compatibility heuristic for them. The below are thus unnecessary
692            // (but are retained here for documentary purposes.)
693            //bool specCompassFeature = false;
694            //bool specAccelerometerFeature = false;
695            //bool specProximityFeature = false;
696            //bool specAmbientLightFeature = false;
697            //bool specLiveWallpaperFeature = false;
698
699            int targetSdk = 0;
700            int smallScreen = 1;
701            int normalScreen = 1;
702            int largeScreen = 1;
703            int xlargeScreen = 1;
704            int anyDensity = 1;
705            int requiresSmallestWidthDp = 0;
706            int compatibleWidthLimitDp = 0;
707            int largestWidthLimitDp = 0;
708            String8 pkg;
709            String8 activityName;
710            String8 activityLabel;
711            String8 activityIcon;
712            String8 receiverName;
713            String8 serviceName;
714            while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
715                if (code == ResXMLTree::END_TAG) {
716                    depth--;
717                    if (depth < 2) {
718                        withinApplication = false;
719                    } else if (depth < 3) {
720                        if (withinActivity && isMainActivity && isLauncherActivity) {
721                            const char *aName = getComponentName(pkg, activityName);
722                            printf("launchable-activity:");
723                            if (aName != NULL) {
724                                printf(" name='%s' ", aName);
725                            }
726                            printf(" label='%s' icon='%s'\n",
727                                    activityLabel.string(),
728                                    activityIcon.string());
729                        }
730                        if (!hasIntentFilter) {
731                            hasOtherActivities |= withinActivity;
732                            hasOtherReceivers |= withinReceiver;
733                            hasOtherServices |= withinService;
734                        }
735                        withinActivity = false;
736                        withinService = false;
737                        withinReceiver = false;
738                        hasIntentFilter = false;
739                        isMainActivity = isLauncherActivity = false;
740                    } else if (depth < 4) {
741                        if (withinIntentFilter) {
742                            if (withinActivity) {
743                                hasMainActivity |= actMainActivity;
744                                hasOtherActivities |= !actMainActivity;
745                            } else if (withinReceiver) {
746                                hasWidgetReceivers |= actWidgetReceivers;
747                                hasOtherReceivers |= !actWidgetReceivers;
748                            } else if (withinService) {
749                                hasImeService |= actImeService;
750                                hasWallpaperService |= actWallpaperService;
751                                hasOtherServices |= (!actImeService && !actWallpaperService);
752                            }
753                        }
754                        withinIntentFilter = false;
755                    }
756                    continue;
757                }
758                if (code != ResXMLTree::START_TAG) {
759                    continue;
760                }
761                depth++;
762                String8 tag(tree.getElementName(&len));
763                //printf("Depth %d,  %s\n", depth, tag.string());
764                if (depth == 1) {
765                    if (tag != "manifest") {
766                        fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
767                        goto bail;
768                    }
769                    pkg = getAttribute(tree, NULL, "package", NULL);
770                    printf("package: name='%s' ", pkg.string());
771                    int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
772                    if (error != "") {
773                        fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
774                        goto bail;
775                    }
776                    if (versionCode > 0) {
777                        printf("versionCode='%d' ", versionCode);
778                    } else {
779                        printf("versionCode='' ");
780                    }
781                    String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
782                    if (error != "") {
783                        fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
784                        goto bail;
785                    }
786                    printf("versionName='%s'\n", versionName.string());
787                } else if (depth == 2) {
788                    withinApplication = false;
789                    if (tag == "application") {
790                        withinApplication = true;
791
792                        String8 label;
793                        const size_t NL = locales.size();
794                        for (size_t i=0; i<NL; i++) {
795                            const char* localeStr =  locales[i].string();
796                            assets.setLocale(localeStr != NULL ? localeStr : "");
797                            String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
798                            if (llabel != "") {
799                                if (localeStr == NULL || strlen(localeStr) == 0) {
800                                    label = llabel;
801                                    printf("application-label:'%s'\n", llabel.string());
802                                } else {
803                                    if (label == "") {
804                                        label = llabel;
805                                    }
806                                    printf("application-label-%s:'%s'\n", localeStr,
807                                            llabel.string());
808                                }
809                            }
810                        }
811
812                        ResTable_config tmpConfig = config;
813                        const size_t ND = densities.size();
814                        for (size_t i=0; i<ND; i++) {
815                            tmpConfig.density = densities[i];
816                            assets.setConfiguration(tmpConfig);
817                            String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
818                            if (icon != "") {
819                                printf("application-icon-%d:'%s'\n", densities[i], icon.string());
820                            }
821                        }
822                        assets.setConfiguration(config);
823
824                        String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
825                        if (error != "") {
826                            fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
827                            goto bail;
828                        }
829                        int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
830                        if (error != "") {
831                            fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
832                            goto bail;
833                        }
834                        printf("application: label='%s' ", label.string());
835                        printf("icon='%s'\n", icon.string());
836                        if (testOnly != 0) {
837                            printf("testOnly='%d'\n", testOnly);
838                        }
839
840                        int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0);
841                        if (error != "") {
842                            fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string());
843                            goto bail;
844                        }
845                        if (debuggable != 0) {
846                            printf("application-debuggable\n");
847                        }
848                    } else if (tag == "uses-sdk") {
849                        int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
850                        if (error != "") {
851                            error = "";
852                            String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
853                            if (error != "") {
854                                fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
855                                        error.string());
856                                goto bail;
857                            }
858                            if (name == "Donut") targetSdk = 4;
859                            printf("sdkVersion:'%s'\n", name.string());
860                        } else if (code != -1) {
861                            targetSdk = code;
862                            printf("sdkVersion:'%d'\n", code);
863                        }
864                        code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
865                        if (code != -1) {
866                            printf("maxSdkVersion:'%d'\n", code);
867                        }
868                        code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
869                        if (error != "") {
870                            error = "";
871                            String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
872                            if (error != "") {
873                                fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
874                                        error.string());
875                                goto bail;
876                            }
877                            if (name == "Donut" && targetSdk < 4) targetSdk = 4;
878                            printf("targetSdkVersion:'%s'\n", name.string());
879                        } else if (code != -1) {
880                            if (targetSdk < code) {
881                                targetSdk = code;
882                            }
883                            printf("targetSdkVersion:'%d'\n", code);
884                        }
885                    } else if (tag == "uses-configuration") {
886                        int32_t reqTouchScreen = getIntegerAttribute(tree,
887                                REQ_TOUCH_SCREEN_ATTR, NULL, 0);
888                        int32_t reqKeyboardType = getIntegerAttribute(tree,
889                                REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
890                        int32_t reqHardKeyboard = getIntegerAttribute(tree,
891                                REQ_HARD_KEYBOARD_ATTR, NULL, 0);
892                        int32_t reqNavigation = getIntegerAttribute(tree,
893                                REQ_NAVIGATION_ATTR, NULL, 0);
894                        int32_t reqFiveWayNav = getIntegerAttribute(tree,
895                                REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
896                        printf("uses-configuration:");
897                        if (reqTouchScreen != 0) {
898                            printf(" reqTouchScreen='%d'", reqTouchScreen);
899                        }
900                        if (reqKeyboardType != 0) {
901                            printf(" reqKeyboardType='%d'", reqKeyboardType);
902                        }
903                        if (reqHardKeyboard != 0) {
904                            printf(" reqHardKeyboard='%d'", reqHardKeyboard);
905                        }
906                        if (reqNavigation != 0) {
907                            printf(" reqNavigation='%d'", reqNavigation);
908                        }
909                        if (reqFiveWayNav != 0) {
910                            printf(" reqFiveWayNav='%d'", reqFiveWayNav);
911                        }
912                        printf("\n");
913                    } else if (tag == "supports-screens") {
914                        smallScreen = getIntegerAttribute(tree,
915                                SMALL_SCREEN_ATTR, NULL, 1);
916                        normalScreen = getIntegerAttribute(tree,
917                                NORMAL_SCREEN_ATTR, NULL, 1);
918                        largeScreen = getIntegerAttribute(tree,
919                                LARGE_SCREEN_ATTR, NULL, 1);
920                        xlargeScreen = getIntegerAttribute(tree,
921                                XLARGE_SCREEN_ATTR, NULL, 1);
922                        anyDensity = getIntegerAttribute(tree,
923                                ANY_DENSITY_ATTR, NULL, 1);
924                        requiresSmallestWidthDp = getIntegerAttribute(tree,
925                                REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
926                        compatibleWidthLimitDp = getIntegerAttribute(tree,
927                                COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
928                        largestWidthLimitDp = getIntegerAttribute(tree,
929                                LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
930                    } else if (tag == "uses-feature") {
931                        String8 name = getAttribute(tree, NAME_ATTR, &error);
932
933                        if (name != "" && error == "") {
934                            int req = getIntegerAttribute(tree,
935                                    REQUIRED_ATTR, NULL, 1);
936
937                            if (name == "android.hardware.camera") {
938                                specCameraFeature = true;
939                            } else if (name == "android.hardware.camera.autofocus") {
940                                // these have no corresponding permission to check for,
941                                // but should imply the foundational camera permission
942                                reqCameraAutofocusFeature = reqCameraAutofocusFeature || req;
943                                specCameraAutofocusFeature = true;
944                            } else if (req && (name == "android.hardware.camera.flash")) {
945                                // these have no corresponding permission to check for,
946                                // but should imply the foundational camera permission
947                                reqCameraFlashFeature = true;
948                            } else if (name == "android.hardware.location") {
949                                specLocationFeature = true;
950                            } else if (name == "android.hardware.location.network") {
951                                specNetworkLocFeature = true;
952                                reqNetworkLocFeature = reqNetworkLocFeature || req;
953                            } else if (name == "android.hardware.location.gps") {
954                                specGpsFeature = true;
955                                reqGpsFeature = reqGpsFeature || req;
956                            } else if (name == "android.hardware.bluetooth") {
957                                specBluetoothFeature = true;
958                            } else if (name == "android.hardware.touchscreen") {
959                                specTouchscreenFeature = true;
960                            } else if (name == "android.hardware.touchscreen.multitouch") {
961                                specMultitouchFeature = true;
962                            } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
963                                reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req;
964                            } else if (name == "android.hardware.microphone") {
965                                specMicrophoneFeature = true;
966                            } else if (name == "android.hardware.wifi") {
967                                specWiFiFeature = true;
968                            } else if (name == "android.hardware.telephony") {
969                                specTelephonyFeature = true;
970                            } else if (req && (name == "android.hardware.telephony.gsm" ||
971                                               name == "android.hardware.telephony.cdma")) {
972                                // these have no corresponding permission to check for,
973                                // but should imply the foundational telephony permission
974                                reqTelephonySubFeature = true;
975                            } else if (name == "android.hardware.screen.portrait") {
976                                specScreenPortraitFeature = true;
977                            } else if (name == "android.hardware.screen.landscape") {
978                                specScreenLandscapeFeature = true;
979                            }
980                            printf("uses-feature%s:'%s'\n",
981                                    req ? "" : "-not-required", name.string());
982                        } else {
983                            int vers = getIntegerAttribute(tree,
984                                    GL_ES_VERSION_ATTR, &error);
985                            if (error == "") {
986                                printf("uses-gl-es:'0x%x'\n", vers);
987                            }
988                        }
989                    } else if (tag == "uses-permission") {
990                        String8 name = getAttribute(tree, NAME_ATTR, &error);
991                        if (name != "" && error == "") {
992                            if (name == "android.permission.CAMERA") {
993                                hasCameraPermission = true;
994                            } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
995                                hasGpsPermission = true;
996                            } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
997                                hasMockLocPermission = true;
998                            } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
999                                hasCoarseLocPermission = true;
1000                            } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
1001                                       name == "android.permission.INSTALL_LOCATION_PROVIDER") {
1002                                hasGeneralLocPermission = true;
1003                            } else if (name == "android.permission.BLUETOOTH" ||
1004                                       name == "android.permission.BLUETOOTH_ADMIN") {
1005                                hasBluetoothPermission = true;
1006                            } else if (name == "android.permission.RECORD_AUDIO") {
1007                                hasRecordAudioPermission = true;
1008                            } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1009                                       name == "android.permission.CHANGE_WIFI_STATE" ||
1010                                       name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
1011                                hasWiFiPermission = true;
1012                            } else if (name == "android.permission.CALL_PHONE" ||
1013                                       name == "android.permission.CALL_PRIVILEGED" ||
1014                                       name == "android.permission.MODIFY_PHONE_STATE" ||
1015                                       name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1016                                       name == "android.permission.READ_SMS" ||
1017                                       name == "android.permission.RECEIVE_SMS" ||
1018                                       name == "android.permission.RECEIVE_MMS" ||
1019                                       name == "android.permission.RECEIVE_WAP_PUSH" ||
1020                                       name == "android.permission.SEND_SMS" ||
1021                                       name == "android.permission.WRITE_APN_SETTINGS" ||
1022                                       name == "android.permission.WRITE_SMS") {
1023                                hasTelephonyPermission = true;
1024                            } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1025                                hasWriteExternalStoragePermission = true;
1026                            } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1027                                hasReadExternalStoragePermission = true;
1028                            } else if (name == "android.permission.READ_PHONE_STATE") {
1029                                hasReadPhoneStatePermission = true;
1030                            } else if (name == "android.permission.READ_CONTACTS") {
1031                                hasReadContactsPermission = true;
1032                            } else if (name == "android.permission.WRITE_CONTACTS") {
1033                                hasWriteContactsPermission = true;
1034                            } else if (name == "android.permission.READ_CALL_LOG") {
1035                                hasReadCallLogPermission = true;
1036                            } else if (name == "android.permission.WRITE_CALL_LOG") {
1037                                hasWriteCallLogPermission = true;
1038                            }
1039                            printf("uses-permission:'%s'\n", name.string());
1040                            int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
1041                            if (!req) {
1042                                printf("optional-permission:'%s'\n", name.string());
1043                            }
1044                        } else {
1045                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1046                                    error.string());
1047                            goto bail;
1048                        }
1049                    } else if (tag == "uses-package") {
1050                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1051                        if (name != "" && error == "") {
1052                            printf("uses-package:'%s'\n", name.string());
1053                        } else {
1054                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1055                                    error.string());
1056                                goto bail;
1057                        }
1058                    } else if (tag == "original-package") {
1059                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1060                        if (name != "" && error == "") {
1061                            printf("original-package:'%s'\n", name.string());
1062                        } else {
1063                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1064                                    error.string());
1065                                goto bail;
1066                        }
1067                    } else if (tag == "supports-gl-texture") {
1068                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1069                        if (name != "" && error == "") {
1070                            printf("supports-gl-texture:'%s'\n", name.string());
1071                        } else {
1072                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1073                                    error.string());
1074                                goto bail;
1075                        }
1076                    } else if (tag == "compatible-screens") {
1077                        printCompatibleScreens(tree);
1078                        depth--;
1079                    } else if (tag == "package-verifier") {
1080                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1081                        if (name != "" && error == "") {
1082                            String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
1083                            if (publicKey != "" && error == "") {
1084                                printf("package-verifier: name='%s' publicKey='%s'\n",
1085                                        name.string(), publicKey.string());
1086                            }
1087                        }
1088                    }
1089                } else if (depth == 3 && withinApplication) {
1090                    withinActivity = false;
1091                    withinReceiver = false;
1092                    withinService = false;
1093                    hasIntentFilter = false;
1094                    if(tag == "activity") {
1095                        withinActivity = true;
1096                        activityName = getAttribute(tree, NAME_ATTR, &error);
1097                        if (error != "") {
1098                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1099                            goto bail;
1100                        }
1101
1102                        activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1103                        if (error != "") {
1104                            fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string());
1105                            goto bail;
1106                        }
1107
1108                        activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1109                        if (error != "") {
1110                            fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
1111                            goto bail;
1112                        }
1113
1114                        int32_t orien = getResolvedIntegerAttribute(&res, tree,
1115                                SCREEN_ORIENTATION_ATTR, &error);
1116                        if (error == "") {
1117                            if (orien == 0 || orien == 6 || orien == 8) {
1118                                // Requests landscape, sensorLandscape, or reverseLandscape.
1119                                reqScreenLandscapeFeature = true;
1120                            } else if (orien == 1 || orien == 7 || orien == 9) {
1121                                // Requests portrait, sensorPortrait, or reversePortrait.
1122                                reqScreenPortraitFeature = true;
1123                            }
1124                        }
1125                    } else if (tag == "uses-library") {
1126                        String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1127                        if (error != "") {
1128                            fprintf(stderr, "ERROR getting 'android:name' attribute for uses-library: %s\n", error.string());
1129                            goto bail;
1130                        }
1131                        int req = getIntegerAttribute(tree,
1132                                REQUIRED_ATTR, NULL, 1);
1133                        printf("uses-library%s:'%s'\n",
1134                                req ? "" : "-not-required", libraryName.string());
1135                    } else if (tag == "receiver") {
1136                        withinReceiver = true;
1137                        receiverName = getAttribute(tree, NAME_ATTR, &error);
1138
1139                        if (error != "") {
1140                            fprintf(stderr, "ERROR getting 'android:name' attribute for receiver: %s\n", error.string());
1141                            goto bail;
1142                        }
1143                    } else if (tag == "service") {
1144                        withinService = true;
1145                        serviceName = getAttribute(tree, NAME_ATTR, &error);
1146
1147                        if (error != "") {
1148                            fprintf(stderr, "ERROR getting 'android:name' attribute for service: %s\n", error.string());
1149                            goto bail;
1150                        }
1151                    }
1152                } else if ((depth == 4) && (tag == "intent-filter")) {
1153                    hasIntentFilter = true;
1154                    withinIntentFilter = true;
1155                    actMainActivity = actWidgetReceivers = actImeService = actWallpaperService = false;
1156                } else if ((depth == 5) && withinIntentFilter){
1157                    String8 action;
1158                    if (tag == "action") {
1159                        action = getAttribute(tree, NAME_ATTR, &error);
1160                        if (error != "") {
1161                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1162                            goto bail;
1163                        }
1164                        if (withinActivity) {
1165                            if (action == "android.intent.action.MAIN") {
1166                                isMainActivity = true;
1167                                actMainActivity = true;
1168                            }
1169                        } else if (withinReceiver) {
1170                            if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1171                                actWidgetReceivers = true;
1172                            }
1173                        } else if (withinService) {
1174                            if (action == "android.view.InputMethod") {
1175                                actImeService = true;
1176                            } else if (action == "android.service.wallpaper.WallpaperService") {
1177                                actWallpaperService = true;
1178                            }
1179                        }
1180                        if (action == "android.intent.action.SEARCH") {
1181                            isSearchable = true;
1182                        }
1183                    }
1184
1185                    if (tag == "category") {
1186                        String8 category = getAttribute(tree, NAME_ATTR, &error);
1187                        if (error != "") {
1188                            fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string());
1189                            goto bail;
1190                        }
1191                        if (withinActivity) {
1192                            if (category == "android.intent.category.LAUNCHER") {
1193                                isLauncherActivity = true;
1194                            }
1195                        }
1196                    }
1197                }
1198            }
1199
1200            // Pre-1.6 implicitly granted permission compatibility logic
1201            if (targetSdk < 4) {
1202                if (!hasWriteExternalStoragePermission) {
1203                    printf("uses-permission:'android.permission.WRITE_EXTERNAL_STORAGE'\n");
1204                    printf("uses-implied-permission:'android.permission.WRITE_EXTERNAL_STORAGE'," \
1205                            "'targetSdkVersion < 4'\n");
1206                    hasWriteExternalStoragePermission = true;
1207                }
1208                if (!hasReadPhoneStatePermission) {
1209                    printf("uses-permission:'android.permission.READ_PHONE_STATE'\n");
1210                    printf("uses-implied-permission:'android.permission.READ_PHONE_STATE'," \
1211                            "'targetSdkVersion < 4'\n");
1212                }
1213            }
1214
1215            // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1216            // force them to always take READ_EXTERNAL_STORAGE as well.  We always
1217            // do this (regardless of target API version) because we can't have
1218            // an app with write permission but not read permission.
1219            if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
1220                printf("uses-permission:'android.permission.READ_EXTERNAL_STORAGE'\n");
1221                printf("uses-implied-permission:'android.permission.READ_EXTERNAL_STORAGE'," \
1222                        "'requested WRITE_EXTERNAL_STORAGE'\n");
1223            }
1224
1225            // Pre-JellyBean call log permission compatibility.
1226            if (targetSdk < 16) {
1227                if (!hasReadCallLogPermission && hasReadContactsPermission) {
1228                    printf("uses-permission:'android.permission.READ_CALL_LOG'\n");
1229                    printf("uses-implied-permission:'android.permission.READ_CALL_LOG'," \
1230                            "'targetSdkVersion < 16 and requested READ_CONTACTS'\n");
1231                }
1232                if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
1233                    printf("uses-permission:'android.permission.WRITE_CALL_LOG'\n");
1234                    printf("uses-implied-permission:'android.permission.WRITE_CALL_LOG'," \
1235                            "'targetSdkVersion < 16 and requested WRITE_CONTACTS'\n");
1236                }
1237            }
1238
1239            /* The following blocks handle printing "inferred" uses-features, based
1240             * on whether related features or permissions are used by the app.
1241             * Note that the various spec*Feature variables denote whether the
1242             * relevant tag was *present* in the AndroidManfest, not that it was
1243             * present and set to true.
1244             */
1245            // Camera-related back-compatibility logic
1246            if (!specCameraFeature) {
1247                if (reqCameraFlashFeature) {
1248                    // if app requested a sub-feature (autofocus or flash) and didn't
1249                    // request the base camera feature, we infer that it meant to
1250                    printf("uses-feature:'android.hardware.camera'\n");
1251                    printf("uses-implied-feature:'android.hardware.camera'," \
1252                            "'requested android.hardware.camera.flash feature'\n");
1253                } else if (reqCameraAutofocusFeature) {
1254                    // if app requested a sub-feature (autofocus or flash) and didn't
1255                    // request the base camera feature, we infer that it meant to
1256                    printf("uses-feature:'android.hardware.camera'\n");
1257                    printf("uses-implied-feature:'android.hardware.camera'," \
1258                            "'requested android.hardware.camera.autofocus feature'\n");
1259                } else if (hasCameraPermission) {
1260                    // if app wants to use camera but didn't request the feature, we infer
1261                    // that it meant to, and further that it wants autofocus
1262                    // (which was the 1.0 - 1.5 behavior)
1263                    printf("uses-feature:'android.hardware.camera'\n");
1264                    if (!specCameraAutofocusFeature) {
1265                        printf("uses-feature:'android.hardware.camera.autofocus'\n");
1266                        printf("uses-implied-feature:'android.hardware.camera.autofocus'," \
1267                                "'requested android.permission.CAMERA permission'\n");
1268                    }
1269                }
1270            }
1271
1272            // Location-related back-compatibility logic
1273            if (!specLocationFeature &&
1274                (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission ||
1275                 hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) {
1276                // if app either takes a location-related permission or requests one of the
1277                // sub-features, we infer that it also meant to request the base location feature
1278                printf("uses-feature:'android.hardware.location'\n");
1279                printf("uses-implied-feature:'android.hardware.location'," \
1280                        "'requested a location access permission'\n");
1281            }
1282            if (!specGpsFeature && hasGpsPermission) {
1283                // if app takes GPS (FINE location) perm but does not request the GPS
1284                // feature, we infer that it meant to
1285                printf("uses-feature:'android.hardware.location.gps'\n");
1286                printf("uses-implied-feature:'android.hardware.location.gps'," \
1287                        "'requested android.permission.ACCESS_FINE_LOCATION permission'\n");
1288            }
1289            if (!specNetworkLocFeature && hasCoarseLocPermission) {
1290                // if app takes Network location (COARSE location) perm but does not request the
1291                // network location feature, we infer that it meant to
1292                printf("uses-feature:'android.hardware.location.network'\n");
1293                printf("uses-implied-feature:'android.hardware.location.network'," \
1294                        "'requested android.permission.ACCESS_COARSE_LOCATION permission'\n");
1295            }
1296
1297            // Bluetooth-related compatibility logic
1298            if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
1299                // if app takes a Bluetooth permission but does not request the Bluetooth
1300                // feature, we infer that it meant to
1301                printf("uses-feature:'android.hardware.bluetooth'\n");
1302                printf("uses-implied-feature:'android.hardware.bluetooth'," \
1303                        "'requested android.permission.BLUETOOTH or android.permission.BLUETOOTH_ADMIN " \
1304                        "permission and targetSdkVersion > 4'\n");
1305            }
1306
1307            // Microphone-related compatibility logic
1308            if (!specMicrophoneFeature && hasRecordAudioPermission) {
1309                // if app takes the record-audio permission but does not request the microphone
1310                // feature, we infer that it meant to
1311                printf("uses-feature:'android.hardware.microphone'\n");
1312                printf("uses-implied-feature:'android.hardware.microphone'," \
1313                        "'requested android.permission.RECORD_AUDIO permission'\n");
1314            }
1315
1316            // WiFi-related compatibility logic
1317            if (!specWiFiFeature && hasWiFiPermission) {
1318                // if app takes one of the WiFi permissions but does not request the WiFi
1319                // feature, we infer that it meant to
1320                printf("uses-feature:'android.hardware.wifi'\n");
1321                printf("uses-implied-feature:'android.hardware.wifi'," \
1322                        "'requested android.permission.ACCESS_WIFI_STATE, " \
1323                        "android.permission.CHANGE_WIFI_STATE, or " \
1324                        "android.permission.CHANGE_WIFI_MULTICAST_STATE permission'\n");
1325            }
1326
1327            // Telephony-related compatibility logic
1328            if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) {
1329                // if app takes one of the telephony permissions or requests a sub-feature but
1330                // does not request the base telephony feature, we infer that it meant to
1331                printf("uses-feature:'android.hardware.telephony'\n");
1332                printf("uses-implied-feature:'android.hardware.telephony'," \
1333                        "'requested a telephony-related permission or feature'\n");
1334            }
1335
1336            // Touchscreen-related back-compatibility logic
1337            if (!specTouchscreenFeature) { // not a typo!
1338                // all apps are presumed to require a touchscreen, unless they explicitly say
1339                // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
1340                // Note that specTouchscreenFeature is true if the tag is present, regardless
1341                // of whether its value is true or false, so this is safe
1342                printf("uses-feature:'android.hardware.touchscreen'\n");
1343                printf("uses-implied-feature:'android.hardware.touchscreen'," \
1344                        "'assumed you require a touch screen unless explicitly made optional'\n");
1345            }
1346            if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
1347                // if app takes one of the telephony permissions or requests a sub-feature but
1348                // does not request the base telephony feature, we infer that it meant to
1349                printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
1350                printf("uses-implied-feature:'android.hardware.touchscreen.multitouch'," \
1351                        "'requested android.hardware.touchscreen.multitouch.distinct feature'\n");
1352            }
1353
1354            // Landscape/portrait-related compatibility logic
1355            if (!specScreenLandscapeFeature && !specScreenPortraitFeature) {
1356                // If the app has specified any activities in its manifest
1357                // that request a specific orientation, then assume that
1358                // orientation is required.
1359                if (reqScreenLandscapeFeature) {
1360                    printf("uses-feature:'android.hardware.screen.landscape'\n");
1361                    printf("uses-implied-feature:'android.hardware.screen.landscape'," \
1362                            "'one or more activities have specified a landscape orientation'\n");
1363                }
1364                if (reqScreenPortraitFeature) {
1365                    printf("uses-feature:'android.hardware.screen.portrait'\n");
1366                    printf("uses-implied-feature:'android.hardware.screen.portrait'," \
1367                            "'one or more activities have specified a portrait orientation'\n");
1368                }
1369            }
1370
1371            if (hasMainActivity) {
1372                printf("main\n");
1373            }
1374            if (hasWidgetReceivers) {
1375                printf("app-widget\n");
1376            }
1377            if (hasImeService) {
1378                printf("ime\n");
1379            }
1380            if (hasWallpaperService) {
1381                printf("wallpaper\n");
1382            }
1383            if (hasOtherActivities) {
1384                printf("other-activities\n");
1385            }
1386            if (isSearchable) {
1387                printf("search\n");
1388            }
1389            if (hasOtherReceivers) {
1390                printf("other-receivers\n");
1391            }
1392            if (hasOtherServices) {
1393                printf("other-services\n");
1394            }
1395
1396            // For modern apps, if screen size buckets haven't been specified
1397            // but the new width ranges have, then infer the buckets from them.
1398            if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1399                    && requiresSmallestWidthDp > 0) {
1400                int compatWidth = compatibleWidthLimitDp;
1401                if (compatWidth <= 0) compatWidth = requiresSmallestWidthDp;
1402                if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1403                    smallScreen = -1;
1404                } else {
1405                    smallScreen = 0;
1406                }
1407                if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1408                    normalScreen = -1;
1409                } else {
1410                    normalScreen = 0;
1411                }
1412                if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1413                    largeScreen = -1;
1414                } else {
1415                    largeScreen = 0;
1416                }
1417                if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1418                    xlargeScreen = -1;
1419                } else {
1420                    xlargeScreen = 0;
1421                }
1422            }
1423
1424            // Determine default values for any unspecified screen sizes,
1425            // based on the target SDK of the package.  As of 4 (donut)
1426            // the screen size support was introduced, so all default to
1427            // enabled.
1428            if (smallScreen > 0) {
1429                smallScreen = targetSdk >= 4 ? -1 : 0;
1430            }
1431            if (normalScreen > 0) {
1432                normalScreen = -1;
1433            }
1434            if (largeScreen > 0) {
1435                largeScreen = targetSdk >= 4 ? -1 : 0;
1436            }
1437            if (xlargeScreen > 0) {
1438                // Introduced in Gingerbread.
1439                xlargeScreen = targetSdk >= 9 ? -1 : 0;
1440            }
1441            if (anyDensity > 0) {
1442                anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1443                        || compatibleWidthLimitDp > 0) ? -1 : 0;
1444            }
1445            printf("supports-screens:");
1446            if (smallScreen != 0) printf(" 'small'");
1447            if (normalScreen != 0) printf(" 'normal'");
1448            if (largeScreen != 0) printf(" 'large'");
1449            if (xlargeScreen != 0) printf(" 'xlarge'");
1450            printf("\n");
1451            printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
1452            if (requiresSmallestWidthDp > 0) {
1453                printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1454            }
1455            if (compatibleWidthLimitDp > 0) {
1456                printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1457            }
1458            if (largestWidthLimitDp > 0) {
1459                printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1460            }
1461
1462            printf("locales:");
1463            const size_t NL = locales.size();
1464            for (size_t i=0; i<NL; i++) {
1465                const char* localeStr =  locales[i].string();
1466                if (localeStr == NULL || strlen(localeStr) == 0) {
1467                    localeStr = "--_--";
1468                }
1469                printf(" '%s'", localeStr);
1470            }
1471            printf("\n");
1472
1473            printf("densities:");
1474            const size_t ND = densities.size();
1475            for (size_t i=0; i<ND; i++) {
1476                printf(" '%d'", densities[i]);
1477            }
1478            printf("\n");
1479
1480            AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1481            if (dir != NULL) {
1482                if (dir->getFileCount() > 0) {
1483                    printf("native-code:");
1484                    for (size_t i=0; i<dir->getFileCount(); i++) {
1485                        printf(" '%s'", dir->getFileName(i).string());
1486                    }
1487                    printf("\n");
1488                }
1489                delete dir;
1490            }
1491        } else if (strcmp("badger", option) == 0) {
1492            printf("%s", CONSOLE_DATA);
1493        } else if (strcmp("configurations", option) == 0) {
1494            Vector<ResTable_config> configs;
1495            res.getConfigurations(&configs);
1496            const size_t N = configs.size();
1497            for (size_t i=0; i<N; i++) {
1498                printf("%s\n", configs[i].toString().string());
1499            }
1500        } else {
1501            fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
1502            goto bail;
1503        }
1504    }
1505
1506    result = NO_ERROR;
1507
1508bail:
1509    if (asset) {
1510        delete asset;
1511    }
1512    return (result != NO_ERROR);
1513}
1514
1515
1516/*
1517 * Handle the "add" command, which wants to add files to a new or
1518 * pre-existing archive.
1519 */
1520int doAdd(Bundle* bundle)
1521{
1522    ZipFile* zip = NULL;
1523    status_t result = UNKNOWN_ERROR;
1524    const char* zipFileName;
1525
1526    if (bundle->getUpdate()) {
1527        /* avoid confusion */
1528        fprintf(stderr, "ERROR: can't use '-u' with add\n");
1529        goto bail;
1530    }
1531
1532    if (bundle->getFileSpecCount() < 1) {
1533        fprintf(stderr, "ERROR: must specify zip file name\n");
1534        goto bail;
1535    }
1536    zipFileName = bundle->getFileSpecEntry(0);
1537
1538    if (bundle->getFileSpecCount() < 2) {
1539        fprintf(stderr, "NOTE: nothing to do\n");
1540        goto bail;
1541    }
1542
1543    zip = openReadWrite(zipFileName, true);
1544    if (zip == NULL) {
1545        fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
1546        goto bail;
1547    }
1548
1549    for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1550        const char* fileName = bundle->getFileSpecEntry(i);
1551
1552        if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
1553            printf(" '%s'... (from gzip)\n", fileName);
1554            result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
1555        } else {
1556            if (bundle->getJunkPath()) {
1557                String8 storageName = String8(fileName).getPathLeaf();
1558                printf(" '%s' as '%s'...\n", fileName, storageName.string());
1559                result = zip->add(fileName, storageName.string(),
1560                                  bundle->getCompressionMethod(), NULL);
1561            } else {
1562                printf(" '%s'...\n", fileName);
1563                result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
1564            }
1565        }
1566        if (result != NO_ERROR) {
1567            fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
1568            if (result == NAME_NOT_FOUND)
1569                fprintf(stderr, ": file not found\n");
1570            else if (result == ALREADY_EXISTS)
1571                fprintf(stderr, ": already exists in archive\n");
1572            else
1573                fprintf(stderr, "\n");
1574            goto bail;
1575        }
1576    }
1577
1578    result = NO_ERROR;
1579
1580bail:
1581    delete zip;
1582    return (result != NO_ERROR);
1583}
1584
1585
1586/*
1587 * Delete files from an existing archive.
1588 */
1589int doRemove(Bundle* bundle)
1590{
1591    ZipFile* zip = NULL;
1592    status_t result = UNKNOWN_ERROR;
1593    const char* zipFileName;
1594
1595    if (bundle->getFileSpecCount() < 1) {
1596        fprintf(stderr, "ERROR: must specify zip file name\n");
1597        goto bail;
1598    }
1599    zipFileName = bundle->getFileSpecEntry(0);
1600
1601    if (bundle->getFileSpecCount() < 2) {
1602        fprintf(stderr, "NOTE: nothing to do\n");
1603        goto bail;
1604    }
1605
1606    zip = openReadWrite(zipFileName, false);
1607    if (zip == NULL) {
1608        fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
1609            zipFileName);
1610        goto bail;
1611    }
1612
1613    for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1614        const char* fileName = bundle->getFileSpecEntry(i);
1615        ZipEntry* entry;
1616
1617        entry = zip->getEntryByName(fileName);
1618        if (entry == NULL) {
1619            printf(" '%s' NOT FOUND\n", fileName);
1620            continue;
1621        }
1622
1623        result = zip->remove(entry);
1624
1625        if (result != NO_ERROR) {
1626            fprintf(stderr, "Unable to delete '%s' from '%s'\n",
1627                bundle->getFileSpecEntry(i), zipFileName);
1628            goto bail;
1629        }
1630    }
1631
1632    /* update the archive */
1633    zip->flush();
1634
1635bail:
1636    delete zip;
1637    return (result != NO_ERROR);
1638}
1639
1640
1641/*
1642 * Package up an asset directory and associated application files.
1643 */
1644int doPackage(Bundle* bundle)
1645{
1646    const char* outputAPKFile;
1647    int retVal = 1;
1648    status_t err;
1649    sp<AaptAssets> assets;
1650    int N;
1651    FILE* fp;
1652    String8 dependencyFile;
1653
1654    // -c zz_ZZ means do pseudolocalization
1655    ResourceFilter filter;
1656    err = filter.parse(bundle->getConfigurations());
1657    if (err != NO_ERROR) {
1658        goto bail;
1659    }
1660    if (filter.containsPseudo()) {
1661        bundle->setPseudolocalize(true);
1662    }
1663
1664    N = bundle->getFileSpecCount();
1665    if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
1666            && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDir() == NULL) {
1667        fprintf(stderr, "ERROR: no input files\n");
1668        goto bail;
1669    }
1670
1671    outputAPKFile = bundle->getOutputAPKFile();
1672
1673    // Make sure the filenames provided exist and are of the appropriate type.
1674    if (outputAPKFile) {
1675        FileType type;
1676        type = getFileType(outputAPKFile);
1677        if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
1678            fprintf(stderr,
1679                "ERROR: output file '%s' exists but is not regular file\n",
1680                outputAPKFile);
1681            goto bail;
1682        }
1683    }
1684
1685    // Load the assets.
1686    assets = new AaptAssets();
1687
1688    // Set up the resource gathering in assets if we're going to generate
1689    // dependency files. Every time we encounter a resource while slurping
1690    // the tree, we'll add it to these stores so we have full resource paths
1691    // to write to a dependency file.
1692    if (bundle->getGenDependencies()) {
1693        sp<FilePathStore> resPathStore = new FilePathStore;
1694        assets->setFullResPaths(resPathStore);
1695        sp<FilePathStore> assetPathStore = new FilePathStore;
1696        assets->setFullAssetPaths(assetPathStore);
1697    }
1698
1699    err = assets->slurpFromArgs(bundle);
1700    if (err < 0) {
1701        goto bail;
1702    }
1703
1704    if (bundle->getVerbose()) {
1705        assets->print(String8());
1706    }
1707
1708    // If they asked for any fileAs that need to be compiled, do so.
1709    if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
1710        err = buildResources(bundle, assets);
1711        if (err != 0) {
1712            goto bail;
1713        }
1714    }
1715
1716    // At this point we've read everything and processed everything.  From here
1717    // on out it's just writing output files.
1718    if (SourcePos::hasErrors()) {
1719        goto bail;
1720    }
1721
1722    // Update symbols with information about which ones are needed as Java symbols.
1723    assets->applyJavaSymbols();
1724    if (SourcePos::hasErrors()) {
1725        goto bail;
1726    }
1727
1728    // If we've been asked to generate a dependency file, do that here
1729    if (bundle->getGenDependencies()) {
1730        // If this is the packaging step, generate the dependency file next to
1731        // the output apk (e.g. bin/resources.ap_.d)
1732        if (outputAPKFile) {
1733            dependencyFile = String8(outputAPKFile);
1734            // Add the .d extension to the dependency file.
1735            dependencyFile.append(".d");
1736        } else {
1737            // Else if this is the R.java dependency generation step,
1738            // generate the dependency file in the R.java package subdirectory
1739            // e.g. gen/com/foo/app/R.java.d
1740            dependencyFile = String8(bundle->getRClassDir());
1741            dependencyFile.appendPath("R.java.d");
1742        }
1743        // Make sure we have a clean dependency file to start with
1744        fp = fopen(dependencyFile, "w");
1745        fclose(fp);
1746    }
1747
1748    // Write out R.java constants
1749    if (!assets->havePrivateSymbols()) {
1750        if (bundle->getCustomPackage() == NULL) {
1751            // Write the R.java file into the appropriate class directory
1752            // e.g. gen/com/foo/app/R.java
1753            err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
1754        } else {
1755            const String8 customPkg(bundle->getCustomPackage());
1756            err = writeResourceSymbols(bundle, assets, customPkg, true);
1757        }
1758        if (err < 0) {
1759            goto bail;
1760        }
1761        // If we have library files, we're going to write our R.java file into
1762        // the appropriate class directory for those libraries as well.
1763        // e.g. gen/com/foo/app/lib/R.java
1764        if (bundle->getExtraPackages() != NULL) {
1765            // Split on colon
1766            String8 libs(bundle->getExtraPackages());
1767            char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
1768            while (packageString != NULL) {
1769                // Write the R.java file out with the correct package name
1770                err = writeResourceSymbols(bundle, assets, String8(packageString), true);
1771                if (err < 0) {
1772                    goto bail;
1773                }
1774                packageString = strtok(NULL, ":");
1775            }
1776            libs.unlockBuffer();
1777        }
1778    } else {
1779        err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
1780        if (err < 0) {
1781            goto bail;
1782        }
1783        err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
1784        if (err < 0) {
1785            goto bail;
1786        }
1787    }
1788
1789    // Write out the ProGuard file
1790    err = writeProguardFile(bundle, assets);
1791    if (err < 0) {
1792        goto bail;
1793    }
1794
1795    // Write the apk
1796    if (outputAPKFile) {
1797        err = writeAPK(bundle, assets, String8(outputAPKFile));
1798        if (err != NO_ERROR) {
1799            fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
1800            goto bail;
1801        }
1802    }
1803
1804    // If we've been asked to generate a dependency file, we need to finish up here.
1805    // the writeResourceSymbols and writeAPK functions have already written the target
1806    // half of the dependency file, now we need to write the prerequisites. (files that
1807    // the R.java file or .ap_ file depend on)
1808    if (bundle->getGenDependencies()) {
1809        // Now that writeResourceSymbols or writeAPK has taken care of writing
1810        // the targets to our dependency file, we'll write the prereqs
1811        fp = fopen(dependencyFile, "a+");
1812        fprintf(fp, " : ");
1813        bool includeRaw = (outputAPKFile != NULL);
1814        err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
1815        // Also manually add the AndroidManifeset since it's not under res/ or assets/
1816        // and therefore was not added to our pathstores during slurping
1817        fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
1818        fclose(fp);
1819    }
1820
1821    retVal = 0;
1822bail:
1823    if (SourcePos::hasErrors()) {
1824        SourcePos::printErrors(stderr);
1825    }
1826    return retVal;
1827}
1828
1829/*
1830 * Do PNG Crunching
1831 * PRECONDITIONS
1832 *  -S flag points to a source directory containing drawable* folders
1833 *  -C flag points to destination directory. The folder structure in the
1834 *     source directory will be mirrored to the destination (cache) directory
1835 *
1836 * POSTCONDITIONS
1837 *  Destination directory will be updated to match the PNG files in
1838 *  the source directory.
1839 */
1840int doCrunch(Bundle* bundle)
1841{
1842    fprintf(stdout, "Crunching PNG Files in ");
1843    fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
1844    fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
1845
1846    updatePreProcessedCache(bundle);
1847
1848    return NO_ERROR;
1849}
1850
1851/*
1852 * Do PNG Crunching on a single flag
1853 *  -i points to a single png file
1854 *  -o points to a single png output file
1855 */
1856int doSingleCrunch(Bundle* bundle)
1857{
1858    fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
1859    fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
1860
1861    String8 input(bundle->getSingleCrunchInputFile());
1862    String8 output(bundle->getSingleCrunchOutputFile());
1863
1864    if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
1865        // we can't return the status_t as it gets truncate to the lower 8 bits.
1866        return 42;
1867    }
1868
1869    return NO_ERROR;
1870}
1871
1872char CONSOLE_DATA[2925] = {
1873    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1874    32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1875    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1876    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1877    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
1878    86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
1879    62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1880    32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1881    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
1882    81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
1883    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1884    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1885    32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
1886    59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1887    32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1888    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
1889    59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
1890    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1891    10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1892    32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
1893    58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
1894    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
1895    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
1896    47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
1897    121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1898    32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1899    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
1900    81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
1901    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
1902    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1903    32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
1904    59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1905    32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1906    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
1907    59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
1908    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1909    32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1910    32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
1911    70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
1912    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
1913    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1914    32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
1915    81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1916    32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1917    32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
1918    81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
1919    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
1920    32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
1921    59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
1922    60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1923    32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
1924    61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
1925    61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
1926    46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1927    32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
1928    59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
1929    109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
1930    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
1931    67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
1932    59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
1933    61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
1934    32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
1935    59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
1936    73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
1937    59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
1938    32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
1939    59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
1940    46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
1941    97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
1942    46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
1943    119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
1944    59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
1945    32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1946    32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
1947    119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
1948    59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
1949    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1950    32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
1951    81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
1952    87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1953    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
1954    81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
1955    45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
1956    32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1957    32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
1958    81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
1959    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1960    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
1961    59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
1962    59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1963    32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1964    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
1965    81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
1966    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1967    10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1968    32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
1969    81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1970    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
1971    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
1972    81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
1973    32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1974    32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1975    32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
1976    81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
1977    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
1978    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1979    58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
1980    61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1981    32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1982    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
1983    81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
1984    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1985    32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1986    32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
1987    81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1988    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
1989    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
1990    59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
1991    59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1992    32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1993    32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
1994    58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
1995    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
1996    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
1997    61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
1998    59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
1999    32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2000    32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2001    32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2002    61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2003    32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2004    32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2005    59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2006    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2007    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2008    59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2009    59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2010    32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2011    32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2012    32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2013    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2014    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2015    46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2016    46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2017    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2018    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2019    59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2020    59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2021    32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2022    32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2023    32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2024    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2025    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2026    59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2027    59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2028    32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2029    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2030    32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2031    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2032    10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2033    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2034    32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2035    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2036  };
2037