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                }
596            }
597        } else if (strcmp("badging", option) == 0) {
598            Vector<String8> locales;
599            res.getLocales(&locales);
600
601            Vector<ResTable_config> configs;
602            res.getConfigurations(&configs);
603            SortedVector<int> densities;
604            const size_t NC = configs.size();
605            for (size_t i=0; i<NC; i++) {
606                int dens = configs[i].density;
607                if (dens == 0) dens = 160;
608                densities.add(dens);
609            }
610
611            size_t len;
612            ResXMLTree::event_code_t code;
613            int depth = 0;
614            String8 error;
615            bool withinActivity = false;
616            bool isMainActivity = false;
617            bool isLauncherActivity = false;
618            bool isSearchable = false;
619            bool withinApplication = false;
620            bool withinReceiver = false;
621            bool withinService = false;
622            bool withinIntentFilter = false;
623            bool hasMainActivity = false;
624            bool hasOtherActivities = false;
625            bool hasOtherReceivers = false;
626            bool hasOtherServices = false;
627            bool hasWallpaperService = false;
628            bool hasImeService = false;
629            bool hasWidgetReceivers = false;
630            bool hasIntentFilter = false;
631            bool actMainActivity = false;
632            bool actWidgetReceivers = false;
633            bool actImeService = false;
634            bool actWallpaperService = false;
635
636            // These two implement the implicit permissions that are granted
637            // to pre-1.6 applications.
638            bool hasWriteExternalStoragePermission = false;
639            bool hasReadPhoneStatePermission = false;
640
641            // If an app requests write storage, they will also get read storage.
642            bool hasReadExternalStoragePermission = false;
643
644            // Implement transition to read and write call log.
645            bool hasReadContactsPermission = false;
646            bool hasWriteContactsPermission = false;
647            bool hasReadCallLogPermission = false;
648            bool hasWriteCallLogPermission = false;
649
650            // This next group of variables is used to implement a group of
651            // backward-compatibility heuristics necessitated by the addition of
652            // some new uses-feature constants in 2.1 and 2.2. In most cases, the
653            // heuristic is "if an app requests a permission but doesn't explicitly
654            // request the corresponding <uses-feature>, presume it's there anyway".
655            bool specCameraFeature = false; // camera-related
656            bool specCameraAutofocusFeature = false;
657            bool reqCameraAutofocusFeature = false;
658            bool reqCameraFlashFeature = false;
659            bool hasCameraPermission = false;
660            bool specLocationFeature = false; // location-related
661            bool specNetworkLocFeature = false;
662            bool reqNetworkLocFeature = false;
663            bool specGpsFeature = false;
664            bool reqGpsFeature = false;
665            bool hasMockLocPermission = false;
666            bool hasCoarseLocPermission = false;
667            bool hasGpsPermission = false;
668            bool hasGeneralLocPermission = false;
669            bool specBluetoothFeature = false; // Bluetooth API-related
670            bool hasBluetoothPermission = false;
671            bool specMicrophoneFeature = false; // microphone-related
672            bool hasRecordAudioPermission = false;
673            bool specWiFiFeature = false;
674            bool hasWiFiPermission = false;
675            bool specTelephonyFeature = false; // telephony-related
676            bool reqTelephonySubFeature = false;
677            bool hasTelephonyPermission = false;
678            bool specTouchscreenFeature = false; // touchscreen-related
679            bool specMultitouchFeature = false;
680            bool reqDistinctMultitouchFeature = false;
681            bool specScreenPortraitFeature = false;
682            bool specScreenLandscapeFeature = false;
683            bool reqScreenPortraitFeature = false;
684            bool reqScreenLandscapeFeature = false;
685            // 2.2 also added some other features that apps can request, but that
686            // have no corresponding permission, so we cannot implement any
687            // back-compatibility heuristic for them. The below are thus unnecessary
688            // (but are retained here for documentary purposes.)
689            //bool specCompassFeature = false;
690            //bool specAccelerometerFeature = false;
691            //bool specProximityFeature = false;
692            //bool specAmbientLightFeature = false;
693            //bool specLiveWallpaperFeature = false;
694
695            int targetSdk = 0;
696            int smallScreen = 1;
697            int normalScreen = 1;
698            int largeScreen = 1;
699            int xlargeScreen = 1;
700            int anyDensity = 1;
701            int requiresSmallestWidthDp = 0;
702            int compatibleWidthLimitDp = 0;
703            int largestWidthLimitDp = 0;
704            String8 pkg;
705            String8 activityName;
706            String8 activityLabel;
707            String8 activityIcon;
708            String8 receiverName;
709            String8 serviceName;
710            while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
711                if (code == ResXMLTree::END_TAG) {
712                    depth--;
713                    if (depth < 2) {
714                        withinApplication = false;
715                    } else if (depth < 3) {
716                        if (withinActivity && isMainActivity && isLauncherActivity) {
717                            const char *aName = getComponentName(pkg, activityName);
718                            printf("launchable-activity:");
719                            if (aName != NULL) {
720                                printf(" name='%s' ", aName);
721                            }
722                            printf(" label='%s' icon='%s'\n",
723                                    activityLabel.string(),
724                                    activityIcon.string());
725                        }
726                        if (!hasIntentFilter) {
727                            hasOtherActivities |= withinActivity;
728                            hasOtherReceivers |= withinReceiver;
729                            hasOtherServices |= withinService;
730                        }
731                        withinActivity = false;
732                        withinService = false;
733                        withinReceiver = false;
734                        hasIntentFilter = false;
735                        isMainActivity = isLauncherActivity = false;
736                    } else if (depth < 4) {
737                        if (withinIntentFilter) {
738                            if (withinActivity) {
739                                hasMainActivity |= actMainActivity;
740                                hasOtherActivities |= !actMainActivity;
741                            } else if (withinReceiver) {
742                                hasWidgetReceivers |= actWidgetReceivers;
743                                hasOtherReceivers |= !actWidgetReceivers;
744                            } else if (withinService) {
745                                hasImeService |= actImeService;
746                                hasWallpaperService |= actWallpaperService;
747                                hasOtherServices |= (!actImeService && !actWallpaperService);
748                            }
749                        }
750                        withinIntentFilter = false;
751                    }
752                    continue;
753                }
754                if (code != ResXMLTree::START_TAG) {
755                    continue;
756                }
757                depth++;
758                String8 tag(tree.getElementName(&len));
759                //printf("Depth %d,  %s\n", depth, tag.string());
760                if (depth == 1) {
761                    if (tag != "manifest") {
762                        fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
763                        goto bail;
764                    }
765                    pkg = getAttribute(tree, NULL, "package", NULL);
766                    printf("package: name='%s' ", pkg.string());
767                    int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
768                    if (error != "") {
769                        fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
770                        goto bail;
771                    }
772                    if (versionCode > 0) {
773                        printf("versionCode='%d' ", versionCode);
774                    } else {
775                        printf("versionCode='' ");
776                    }
777                    String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
778                    if (error != "") {
779                        fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
780                        goto bail;
781                    }
782                    printf("versionName='%s'\n", versionName.string());
783                } else if (depth == 2) {
784                    withinApplication = false;
785                    if (tag == "application") {
786                        withinApplication = true;
787
788                        String8 label;
789                        const size_t NL = locales.size();
790                        for (size_t i=0; i<NL; i++) {
791                            const char* localeStr =  locales[i].string();
792                            assets.setLocale(localeStr != NULL ? localeStr : "");
793                            String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
794                            if (llabel != "") {
795                                if (localeStr == NULL || strlen(localeStr) == 0) {
796                                    label = llabel;
797                                    printf("application-label:'%s'\n", llabel.string());
798                                } else {
799                                    if (label == "") {
800                                        label = llabel;
801                                    }
802                                    printf("application-label-%s:'%s'\n", localeStr,
803                                            llabel.string());
804                                }
805                            }
806                        }
807
808                        ResTable_config tmpConfig = config;
809                        const size_t ND = densities.size();
810                        for (size_t i=0; i<ND; i++) {
811                            tmpConfig.density = densities[i];
812                            assets.setConfiguration(tmpConfig);
813                            String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
814                            if (icon != "") {
815                                printf("application-icon-%d:'%s'\n", densities[i], icon.string());
816                            }
817                        }
818                        assets.setConfiguration(config);
819
820                        String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
821                        if (error != "") {
822                            fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
823                            goto bail;
824                        }
825                        int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
826                        if (error != "") {
827                            fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
828                            goto bail;
829                        }
830                        printf("application: label='%s' ", label.string());
831                        printf("icon='%s'\n", icon.string());
832                        if (testOnly != 0) {
833                            printf("testOnly='%d'\n", testOnly);
834                        }
835
836                        int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0);
837                        if (error != "") {
838                            fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string());
839                            goto bail;
840                        }
841                        if (debuggable != 0) {
842                            printf("application-debuggable\n");
843                        }
844                    } else if (tag == "uses-sdk") {
845                        int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
846                        if (error != "") {
847                            error = "";
848                            String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
849                            if (error != "") {
850                                fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
851                                        error.string());
852                                goto bail;
853                            }
854                            if (name == "Donut") targetSdk = 4;
855                            printf("sdkVersion:'%s'\n", name.string());
856                        } else if (code != -1) {
857                            targetSdk = code;
858                            printf("sdkVersion:'%d'\n", code);
859                        }
860                        code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
861                        if (code != -1) {
862                            printf("maxSdkVersion:'%d'\n", code);
863                        }
864                        code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
865                        if (error != "") {
866                            error = "";
867                            String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
868                            if (error != "") {
869                                fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
870                                        error.string());
871                                goto bail;
872                            }
873                            if (name == "Donut" && targetSdk < 4) targetSdk = 4;
874                            printf("targetSdkVersion:'%s'\n", name.string());
875                        } else if (code != -1) {
876                            if (targetSdk < code) {
877                                targetSdk = code;
878                            }
879                            printf("targetSdkVersion:'%d'\n", code);
880                        }
881                    } else if (tag == "uses-configuration") {
882                        int32_t reqTouchScreen = getIntegerAttribute(tree,
883                                REQ_TOUCH_SCREEN_ATTR, NULL, 0);
884                        int32_t reqKeyboardType = getIntegerAttribute(tree,
885                                REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
886                        int32_t reqHardKeyboard = getIntegerAttribute(tree,
887                                REQ_HARD_KEYBOARD_ATTR, NULL, 0);
888                        int32_t reqNavigation = getIntegerAttribute(tree,
889                                REQ_NAVIGATION_ATTR, NULL, 0);
890                        int32_t reqFiveWayNav = getIntegerAttribute(tree,
891                                REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
892                        printf("uses-configuration:");
893                        if (reqTouchScreen != 0) {
894                            printf(" reqTouchScreen='%d'", reqTouchScreen);
895                        }
896                        if (reqKeyboardType != 0) {
897                            printf(" reqKeyboardType='%d'", reqKeyboardType);
898                        }
899                        if (reqHardKeyboard != 0) {
900                            printf(" reqHardKeyboard='%d'", reqHardKeyboard);
901                        }
902                        if (reqNavigation != 0) {
903                            printf(" reqNavigation='%d'", reqNavigation);
904                        }
905                        if (reqFiveWayNav != 0) {
906                            printf(" reqFiveWayNav='%d'", reqFiveWayNav);
907                        }
908                        printf("\n");
909                    } else if (tag == "supports-screens") {
910                        smallScreen = getIntegerAttribute(tree,
911                                SMALL_SCREEN_ATTR, NULL, 1);
912                        normalScreen = getIntegerAttribute(tree,
913                                NORMAL_SCREEN_ATTR, NULL, 1);
914                        largeScreen = getIntegerAttribute(tree,
915                                LARGE_SCREEN_ATTR, NULL, 1);
916                        xlargeScreen = getIntegerAttribute(tree,
917                                XLARGE_SCREEN_ATTR, NULL, 1);
918                        anyDensity = getIntegerAttribute(tree,
919                                ANY_DENSITY_ATTR, NULL, 1);
920                        requiresSmallestWidthDp = getIntegerAttribute(tree,
921                                REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
922                        compatibleWidthLimitDp = getIntegerAttribute(tree,
923                                COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
924                        largestWidthLimitDp = getIntegerAttribute(tree,
925                                LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
926                    } else if (tag == "uses-feature") {
927                        String8 name = getAttribute(tree, NAME_ATTR, &error);
928
929                        if (name != "" && error == "") {
930                            int req = getIntegerAttribute(tree,
931                                    REQUIRED_ATTR, NULL, 1);
932
933                            if (name == "android.hardware.camera") {
934                                specCameraFeature = true;
935                            } else if (name == "android.hardware.camera.autofocus") {
936                                // these have no corresponding permission to check for,
937                                // but should imply the foundational camera permission
938                                reqCameraAutofocusFeature = reqCameraAutofocusFeature || req;
939                                specCameraAutofocusFeature = true;
940                            } else if (req && (name == "android.hardware.camera.flash")) {
941                                // these have no corresponding permission to check for,
942                                // but should imply the foundational camera permission
943                                reqCameraFlashFeature = true;
944                            } else if (name == "android.hardware.location") {
945                                specLocationFeature = true;
946                            } else if (name == "android.hardware.location.network") {
947                                specNetworkLocFeature = true;
948                                reqNetworkLocFeature = reqNetworkLocFeature || req;
949                            } else if (name == "android.hardware.location.gps") {
950                                specGpsFeature = true;
951                                reqGpsFeature = reqGpsFeature || req;
952                            } else if (name == "android.hardware.bluetooth") {
953                                specBluetoothFeature = true;
954                            } else if (name == "android.hardware.touchscreen") {
955                                specTouchscreenFeature = true;
956                            } else if (name == "android.hardware.touchscreen.multitouch") {
957                                specMultitouchFeature = true;
958                            } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
959                                reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req;
960                            } else if (name == "android.hardware.microphone") {
961                                specMicrophoneFeature = true;
962                            } else if (name == "android.hardware.wifi") {
963                                specWiFiFeature = true;
964                            } else if (name == "android.hardware.telephony") {
965                                specTelephonyFeature = true;
966                            } else if (req && (name == "android.hardware.telephony.gsm" ||
967                                               name == "android.hardware.telephony.cdma")) {
968                                // these have no corresponding permission to check for,
969                                // but should imply the foundational telephony permission
970                                reqTelephonySubFeature = true;
971                            } else if (name == "android.hardware.screen.portrait") {
972                                specScreenPortraitFeature = true;
973                            } else if (name == "android.hardware.screen.landscape") {
974                                specScreenLandscapeFeature = true;
975                            }
976                            printf("uses-feature%s:'%s'\n",
977                                    req ? "" : "-not-required", name.string());
978                        } else {
979                            int vers = getIntegerAttribute(tree,
980                                    GL_ES_VERSION_ATTR, &error);
981                            if (error == "") {
982                                printf("uses-gl-es:'0x%x'\n", vers);
983                            }
984                        }
985                    } else if (tag == "uses-permission") {
986                        String8 name = getAttribute(tree, NAME_ATTR, &error);
987                        if (name != "" && error == "") {
988                            if (name == "android.permission.CAMERA") {
989                                hasCameraPermission = true;
990                            } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
991                                hasGpsPermission = true;
992                            } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
993                                hasMockLocPermission = true;
994                            } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
995                                hasCoarseLocPermission = true;
996                            } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
997                                       name == "android.permission.INSTALL_LOCATION_PROVIDER") {
998                                hasGeneralLocPermission = true;
999                            } else if (name == "android.permission.BLUETOOTH" ||
1000                                       name == "android.permission.BLUETOOTH_ADMIN") {
1001                                hasBluetoothPermission = true;
1002                            } else if (name == "android.permission.RECORD_AUDIO") {
1003                                hasRecordAudioPermission = true;
1004                            } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1005                                       name == "android.permission.CHANGE_WIFI_STATE" ||
1006                                       name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
1007                                hasWiFiPermission = true;
1008                            } else if (name == "android.permission.CALL_PHONE" ||
1009                                       name == "android.permission.CALL_PRIVILEGED" ||
1010                                       name == "android.permission.MODIFY_PHONE_STATE" ||
1011                                       name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1012                                       name == "android.permission.READ_SMS" ||
1013                                       name == "android.permission.RECEIVE_SMS" ||
1014                                       name == "android.permission.RECEIVE_MMS" ||
1015                                       name == "android.permission.RECEIVE_WAP_PUSH" ||
1016                                       name == "android.permission.SEND_SMS" ||
1017                                       name == "android.permission.WRITE_APN_SETTINGS" ||
1018                                       name == "android.permission.WRITE_SMS") {
1019                                hasTelephonyPermission = true;
1020                            } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1021                                hasWriteExternalStoragePermission = true;
1022                            } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1023                                hasReadExternalStoragePermission = true;
1024                            } else if (name == "android.permission.READ_PHONE_STATE") {
1025                                hasReadPhoneStatePermission = true;
1026                            } else if (name == "android.permission.READ_CONTACTS") {
1027                                hasReadContactsPermission = true;
1028                            } else if (name == "android.permission.WRITE_CONTACTS") {
1029                                hasWriteContactsPermission = true;
1030                            } else if (name == "android.permission.READ_CALL_LOG") {
1031                                hasReadCallLogPermission = true;
1032                            } else if (name == "android.permission.WRITE_CALL_LOG") {
1033                                hasWriteCallLogPermission = true;
1034                            }
1035                            printf("uses-permission:'%s'\n", name.string());
1036                        } else {
1037                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1038                                    error.string());
1039                            goto bail;
1040                        }
1041                    } else if (tag == "uses-package") {
1042                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1043                        if (name != "" && error == "") {
1044                            printf("uses-package:'%s'\n", name.string());
1045                        } else {
1046                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1047                                    error.string());
1048                                goto bail;
1049                        }
1050                    } else if (tag == "original-package") {
1051                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1052                        if (name != "" && error == "") {
1053                            printf("original-package:'%s'\n", name.string());
1054                        } else {
1055                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1056                                    error.string());
1057                                goto bail;
1058                        }
1059                    } else if (tag == "supports-gl-texture") {
1060                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1061                        if (name != "" && error == "") {
1062                            printf("supports-gl-texture:'%s'\n", name.string());
1063                        } else {
1064                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1065                                    error.string());
1066                                goto bail;
1067                        }
1068                    } else if (tag == "compatible-screens") {
1069                        printCompatibleScreens(tree);
1070                        depth--;
1071                    } else if (tag == "package-verifier") {
1072                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1073                        if (name != "" && error == "") {
1074                            String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
1075                            if (publicKey != "" && error == "") {
1076                                printf("package-verifier: name='%s' publicKey='%s'\n",
1077                                        name.string(), publicKey.string());
1078                            }
1079                        }
1080                    }
1081                } else if (depth == 3 && withinApplication) {
1082                    withinActivity = false;
1083                    withinReceiver = false;
1084                    withinService = false;
1085                    hasIntentFilter = false;
1086                    if(tag == "activity") {
1087                        withinActivity = true;
1088                        activityName = getAttribute(tree, NAME_ATTR, &error);
1089                        if (error != "") {
1090                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1091                            goto bail;
1092                        }
1093
1094                        activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1095                        if (error != "") {
1096                            fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string());
1097                            goto bail;
1098                        }
1099
1100                        activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1101                        if (error != "") {
1102                            fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
1103                            goto bail;
1104                        }
1105
1106                        int32_t orien = getResolvedIntegerAttribute(&res, tree,
1107                                SCREEN_ORIENTATION_ATTR, &error);
1108                        if (error == "") {
1109                            if (orien == 0 || orien == 6 || orien == 8) {
1110                                // Requests landscape, sensorLandscape, or reverseLandscape.
1111                                reqScreenLandscapeFeature = true;
1112                            } else if (orien == 1 || orien == 7 || orien == 9) {
1113                                // Requests portrait, sensorPortrait, or reversePortrait.
1114                                reqScreenPortraitFeature = true;
1115                            }
1116                        }
1117                    } else if (tag == "uses-library") {
1118                        String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1119                        if (error != "") {
1120                            fprintf(stderr, "ERROR getting 'android:name' attribute for uses-library: %s\n", error.string());
1121                            goto bail;
1122                        }
1123                        int req = getIntegerAttribute(tree,
1124                                REQUIRED_ATTR, NULL, 1);
1125                        printf("uses-library%s:'%s'\n",
1126                                req ? "" : "-not-required", libraryName.string());
1127                    } else if (tag == "receiver") {
1128                        withinReceiver = true;
1129                        receiverName = getAttribute(tree, NAME_ATTR, &error);
1130
1131                        if (error != "") {
1132                            fprintf(stderr, "ERROR getting 'android:name' attribute for receiver: %s\n", error.string());
1133                            goto bail;
1134                        }
1135                    } else if (tag == "service") {
1136                        withinService = true;
1137                        serviceName = getAttribute(tree, NAME_ATTR, &error);
1138
1139                        if (error != "") {
1140                            fprintf(stderr, "ERROR getting 'android:name' attribute for service: %s\n", error.string());
1141                            goto bail;
1142                        }
1143                    }
1144                } else if ((depth == 4) && (tag == "intent-filter")) {
1145                    hasIntentFilter = true;
1146                    withinIntentFilter = true;
1147                    actMainActivity = actWidgetReceivers = actImeService = actWallpaperService = false;
1148                } else if ((depth == 5) && withinIntentFilter){
1149                    String8 action;
1150                    if (tag == "action") {
1151                        action = getAttribute(tree, NAME_ATTR, &error);
1152                        if (error != "") {
1153                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1154                            goto bail;
1155                        }
1156                        if (withinActivity) {
1157                            if (action == "android.intent.action.MAIN") {
1158                                isMainActivity = true;
1159                                actMainActivity = true;
1160                            }
1161                        } else if (withinReceiver) {
1162                            if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1163                                actWidgetReceivers = true;
1164                            }
1165                        } else if (withinService) {
1166                            if (action == "android.view.InputMethod") {
1167                                actImeService = true;
1168                            } else if (action == "android.service.wallpaper.WallpaperService") {
1169                                actWallpaperService = true;
1170                            }
1171                        }
1172                        if (action == "android.intent.action.SEARCH") {
1173                            isSearchable = true;
1174                        }
1175                    }
1176
1177                    if (tag == "category") {
1178                        String8 category = getAttribute(tree, NAME_ATTR, &error);
1179                        if (error != "") {
1180                            fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string());
1181                            goto bail;
1182                        }
1183                        if (withinActivity) {
1184                            if (category == "android.intent.category.LAUNCHER") {
1185                                isLauncherActivity = true;
1186                            }
1187                        }
1188                    }
1189                }
1190            }
1191
1192            // Pre-1.6 implicitly granted permission compatibility logic
1193            if (targetSdk < 4) {
1194                if (!hasWriteExternalStoragePermission) {
1195                    printf("uses-permission:'android.permission.WRITE_EXTERNAL_STORAGE'\n");
1196                    printf("uses-implied-permission:'android.permission.WRITE_EXTERNAL_STORAGE'," \
1197                            "'targetSdkVersion < 4'\n");
1198                    hasWriteExternalStoragePermission = true;
1199                }
1200                if (!hasReadPhoneStatePermission) {
1201                    printf("uses-permission:'android.permission.READ_PHONE_STATE'\n");
1202                    printf("uses-implied-permission:'android.permission.READ_PHONE_STATE'," \
1203                            "'targetSdkVersion < 4'\n");
1204                }
1205            }
1206
1207            // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1208            // force them to always take READ_EXTERNAL_STORAGE as well.  We always
1209            // do this (regardless of target API version) because we can't have
1210            // an app with write permission but not read permission.
1211            if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
1212                printf("uses-permission:'android.permission.READ_EXTERNAL_STORAGE'\n");
1213                printf("uses-implied-permission:'android.permission.READ_EXTERNAL_STORAGE'," \
1214                        "'requested WRITE_EXTERNAL_STORAGE'\n");
1215            }
1216
1217            // Pre-JellyBean call log permission compatibility.
1218            if (targetSdk < 16) {
1219                if (!hasReadCallLogPermission && hasReadContactsPermission) {
1220                    printf("uses-permission:'android.permission.READ_CALL_LOG'\n");
1221                    printf("uses-implied-permission:'android.permission.READ_CALL_LOG'," \
1222                            "'targetSdkVersion < 16 and requested READ_CONTACTS'\n");
1223                }
1224                if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
1225                    printf("uses-permission:'android.permission.WRITE_CALL_LOG'\n");
1226                    printf("uses-implied-permission:'android.permission.WRITE_CALL_LOG'," \
1227                            "'targetSdkVersion < 16 and requested WRITE_CONTACTS'\n");
1228                }
1229            }
1230
1231            /* The following blocks handle printing "inferred" uses-features, based
1232             * on whether related features or permissions are used by the app.
1233             * Note that the various spec*Feature variables denote whether the
1234             * relevant tag was *present* in the AndroidManfest, not that it was
1235             * present and set to true.
1236             */
1237            // Camera-related back-compatibility logic
1238            if (!specCameraFeature) {
1239                if (reqCameraFlashFeature) {
1240                    // if app requested a sub-feature (autofocus or flash) and didn't
1241                    // request the base camera feature, we infer that it meant to
1242                    printf("uses-feature:'android.hardware.camera'\n");
1243                    printf("uses-implied-feature:'android.hardware.camera'," \
1244                            "'requested android.hardware.camera.flash feature'\n");
1245                } else if (reqCameraAutofocusFeature) {
1246                    // if app requested a sub-feature (autofocus or flash) and didn't
1247                    // request the base camera feature, we infer that it meant to
1248                    printf("uses-feature:'android.hardware.camera'\n");
1249                    printf("uses-implied-feature:'android.hardware.camera'," \
1250                            "'requested android.hardware.camera.autofocus feature'\n");
1251                } else if (hasCameraPermission) {
1252                    // if app wants to use camera but didn't request the feature, we infer
1253                    // that it meant to, and further that it wants autofocus
1254                    // (which was the 1.0 - 1.5 behavior)
1255                    printf("uses-feature:'android.hardware.camera'\n");
1256                    if (!specCameraAutofocusFeature) {
1257                        printf("uses-feature:'android.hardware.camera.autofocus'\n");
1258                        printf("uses-implied-feature:'android.hardware.camera.autofocus'," \
1259                                "'requested android.permission.CAMERA permission'\n");
1260                    }
1261                }
1262            }
1263
1264            // Location-related back-compatibility logic
1265            if (!specLocationFeature &&
1266                (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission ||
1267                 hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) {
1268                // if app either takes a location-related permission or requests one of the
1269                // sub-features, we infer that it also meant to request the base location feature
1270                printf("uses-feature:'android.hardware.location'\n");
1271                printf("uses-implied-feature:'android.hardware.location'," \
1272                        "'requested a location access permission'\n");
1273            }
1274            if (!specGpsFeature && hasGpsPermission) {
1275                // if app takes GPS (FINE location) perm but does not request the GPS
1276                // feature, we infer that it meant to
1277                printf("uses-feature:'android.hardware.location.gps'\n");
1278                printf("uses-implied-feature:'android.hardware.location.gps'," \
1279                        "'requested android.permission.ACCESS_FINE_LOCATION permission'\n");
1280            }
1281            if (!specNetworkLocFeature && hasCoarseLocPermission) {
1282                // if app takes Network location (COARSE location) perm but does not request the
1283                // network location feature, we infer that it meant to
1284                printf("uses-feature:'android.hardware.location.network'\n");
1285                printf("uses-implied-feature:'android.hardware.location.network'," \
1286                        "'requested android.permission.ACCESS_COURSE_LOCATION permission'\n");
1287            }
1288
1289            // Bluetooth-related compatibility logic
1290            if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
1291                // if app takes a Bluetooth permission but does not request the Bluetooth
1292                // feature, we infer that it meant to
1293                printf("uses-feature:'android.hardware.bluetooth'\n");
1294                printf("uses-implied-feature:'android.hardware.bluetooth'," \
1295                        "'requested android.permission.BLUETOOTH or android.permission.BLUETOOTH_ADMIN " \
1296                        "permission and targetSdkVersion > 4'\n");
1297            }
1298
1299            // Microphone-related compatibility logic
1300            if (!specMicrophoneFeature && hasRecordAudioPermission) {
1301                // if app takes the record-audio permission but does not request the microphone
1302                // feature, we infer that it meant to
1303                printf("uses-feature:'android.hardware.microphone'\n");
1304                printf("uses-implied-feature:'android.hardware.microphone'," \
1305                        "'requested android.permission.RECORD_AUDIO permission'\n");
1306            }
1307
1308            // WiFi-related compatibility logic
1309            if (!specWiFiFeature && hasWiFiPermission) {
1310                // if app takes one of the WiFi permissions but does not request the WiFi
1311                // feature, we infer that it meant to
1312                printf("uses-feature:'android.hardware.wifi'\n");
1313                printf("uses-implied-feature:'android.hardware.wifi'," \
1314                        "'requested android.permission.ACCESS_WIFI_STATE, " \
1315                        "android.permission.CHANGE_WIFI_STATE, or " \
1316                        "android.permission.CHANGE_WIFI_MULTICAST_STATE permission'\n");
1317            }
1318
1319            // Telephony-related compatibility logic
1320            if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) {
1321                // if app takes one of the telephony permissions or requests a sub-feature but
1322                // does not request the base telephony feature, we infer that it meant to
1323                printf("uses-feature:'android.hardware.telephony'\n");
1324                printf("uses-implied-feature:'android.hardware.telephony'," \
1325                        "'requested a telephony-related permission or feature'\n");
1326            }
1327
1328            // Touchscreen-related back-compatibility logic
1329            if (!specTouchscreenFeature) { // not a typo!
1330                // all apps are presumed to require a touchscreen, unless they explicitly say
1331                // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
1332                // Note that specTouchscreenFeature is true if the tag is present, regardless
1333                // of whether its value is true or false, so this is safe
1334                printf("uses-feature:'android.hardware.touchscreen'\n");
1335                printf("uses-implied-feature:'android.hardware.touchscreen'," \
1336                        "'assumed you require a touch screen unless explicitly made optional'\n");
1337            }
1338            if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
1339                // if app takes one of the telephony permissions or requests a sub-feature but
1340                // does not request the base telephony feature, we infer that it meant to
1341                printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
1342                printf("uses-implied-feature:'android.hardware.touchscreen.multitouch'," \
1343                        "'requested android.hardware.touchscreen.multitouch.distinct feature'\n");
1344            }
1345
1346            // Landscape/portrait-related compatibility logic
1347            if (!specScreenLandscapeFeature && !specScreenPortraitFeature) {
1348                // If the app has specified any activities in its manifest
1349                // that request a specific orientation, then assume that
1350                // orientation is required.
1351                if (reqScreenLandscapeFeature) {
1352                    printf("uses-feature:'android.hardware.screen.landscape'\n");
1353                    printf("uses-implied-feature:'android.hardware.screen.landscape'," \
1354                            "'one or more activities have specified a landscape orientation'\n");
1355                }
1356                if (reqScreenPortraitFeature) {
1357                    printf("uses-feature:'android.hardware.screen.portrait'\n");
1358                    printf("uses-implied-feature:'android.hardware.screen.portrait'," \
1359                            "'one or more activities have specified a portrait orientation'\n");
1360                }
1361            }
1362
1363            if (hasMainActivity) {
1364                printf("main\n");
1365            }
1366            if (hasWidgetReceivers) {
1367                printf("app-widget\n");
1368            }
1369            if (hasImeService) {
1370                printf("ime\n");
1371            }
1372            if (hasWallpaperService) {
1373                printf("wallpaper\n");
1374            }
1375            if (hasOtherActivities) {
1376                printf("other-activities\n");
1377            }
1378            if (isSearchable) {
1379                printf("search\n");
1380            }
1381            if (hasOtherReceivers) {
1382                printf("other-receivers\n");
1383            }
1384            if (hasOtherServices) {
1385                printf("other-services\n");
1386            }
1387
1388            // For modern apps, if screen size buckets haven't been specified
1389            // but the new width ranges have, then infer the buckets from them.
1390            if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1391                    && requiresSmallestWidthDp > 0) {
1392                int compatWidth = compatibleWidthLimitDp;
1393                if (compatWidth <= 0) compatWidth = requiresSmallestWidthDp;
1394                if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1395                    smallScreen = -1;
1396                } else {
1397                    smallScreen = 0;
1398                }
1399                if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1400                    normalScreen = -1;
1401                } else {
1402                    normalScreen = 0;
1403                }
1404                if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1405                    largeScreen = -1;
1406                } else {
1407                    largeScreen = 0;
1408                }
1409                if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1410                    xlargeScreen = -1;
1411                } else {
1412                    xlargeScreen = 0;
1413                }
1414            }
1415
1416            // Determine default values for any unspecified screen sizes,
1417            // based on the target SDK of the package.  As of 4 (donut)
1418            // the screen size support was introduced, so all default to
1419            // enabled.
1420            if (smallScreen > 0) {
1421                smallScreen = targetSdk >= 4 ? -1 : 0;
1422            }
1423            if (normalScreen > 0) {
1424                normalScreen = -1;
1425            }
1426            if (largeScreen > 0) {
1427                largeScreen = targetSdk >= 4 ? -1 : 0;
1428            }
1429            if (xlargeScreen > 0) {
1430                // Introduced in Gingerbread.
1431                xlargeScreen = targetSdk >= 9 ? -1 : 0;
1432            }
1433            if (anyDensity > 0) {
1434                anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1435                        || compatibleWidthLimitDp > 0) ? -1 : 0;
1436            }
1437            printf("supports-screens:");
1438            if (smallScreen != 0) printf(" 'small'");
1439            if (normalScreen != 0) printf(" 'normal'");
1440            if (largeScreen != 0) printf(" 'large'");
1441            if (xlargeScreen != 0) printf(" 'xlarge'");
1442            printf("\n");
1443            printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
1444            if (requiresSmallestWidthDp > 0) {
1445                printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1446            }
1447            if (compatibleWidthLimitDp > 0) {
1448                printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1449            }
1450            if (largestWidthLimitDp > 0) {
1451                printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1452            }
1453
1454            printf("locales:");
1455            const size_t NL = locales.size();
1456            for (size_t i=0; i<NL; i++) {
1457                const char* localeStr =  locales[i].string();
1458                if (localeStr == NULL || strlen(localeStr) == 0) {
1459                    localeStr = "--_--";
1460                }
1461                printf(" '%s'", localeStr);
1462            }
1463            printf("\n");
1464
1465            printf("densities:");
1466            const size_t ND = densities.size();
1467            for (size_t i=0; i<ND; i++) {
1468                printf(" '%d'", densities[i]);
1469            }
1470            printf("\n");
1471
1472            AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1473            if (dir != NULL) {
1474                if (dir->getFileCount() > 0) {
1475                    printf("native-code:");
1476                    for (size_t i=0; i<dir->getFileCount(); i++) {
1477                        printf(" '%s'", dir->getFileName(i).string());
1478                    }
1479                    printf("\n");
1480                }
1481                delete dir;
1482            }
1483        } else if (strcmp("badger", option) == 0) {
1484            printf("%s", CONSOLE_DATA);
1485        } else if (strcmp("configurations", option) == 0) {
1486            Vector<ResTable_config> configs;
1487            res.getConfigurations(&configs);
1488            const size_t N = configs.size();
1489            for (size_t i=0; i<N; i++) {
1490                printf("%s\n", configs[i].toString().string());
1491            }
1492        } else {
1493            fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
1494            goto bail;
1495        }
1496    }
1497
1498    result = NO_ERROR;
1499
1500bail:
1501    if (asset) {
1502        delete asset;
1503    }
1504    return (result != NO_ERROR);
1505}
1506
1507
1508/*
1509 * Handle the "add" command, which wants to add files to a new or
1510 * pre-existing archive.
1511 */
1512int doAdd(Bundle* bundle)
1513{
1514    ZipFile* zip = NULL;
1515    status_t result = UNKNOWN_ERROR;
1516    const char* zipFileName;
1517
1518    if (bundle->getUpdate()) {
1519        /* avoid confusion */
1520        fprintf(stderr, "ERROR: can't use '-u' with add\n");
1521        goto bail;
1522    }
1523
1524    if (bundle->getFileSpecCount() < 1) {
1525        fprintf(stderr, "ERROR: must specify zip file name\n");
1526        goto bail;
1527    }
1528    zipFileName = bundle->getFileSpecEntry(0);
1529
1530    if (bundle->getFileSpecCount() < 2) {
1531        fprintf(stderr, "NOTE: nothing to do\n");
1532        goto bail;
1533    }
1534
1535    zip = openReadWrite(zipFileName, true);
1536    if (zip == NULL) {
1537        fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
1538        goto bail;
1539    }
1540
1541    for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1542        const char* fileName = bundle->getFileSpecEntry(i);
1543
1544        if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
1545            printf(" '%s'... (from gzip)\n", fileName);
1546            result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
1547        } else {
1548            if (bundle->getJunkPath()) {
1549                String8 storageName = String8(fileName).getPathLeaf();
1550                printf(" '%s' as '%s'...\n", fileName, storageName.string());
1551                result = zip->add(fileName, storageName.string(),
1552                                  bundle->getCompressionMethod(), NULL);
1553            } else {
1554                printf(" '%s'...\n", fileName);
1555                result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
1556            }
1557        }
1558        if (result != NO_ERROR) {
1559            fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
1560            if (result == NAME_NOT_FOUND)
1561                fprintf(stderr, ": file not found\n");
1562            else if (result == ALREADY_EXISTS)
1563                fprintf(stderr, ": already exists in archive\n");
1564            else
1565                fprintf(stderr, "\n");
1566            goto bail;
1567        }
1568    }
1569
1570    result = NO_ERROR;
1571
1572bail:
1573    delete zip;
1574    return (result != NO_ERROR);
1575}
1576
1577
1578/*
1579 * Delete files from an existing archive.
1580 */
1581int doRemove(Bundle* bundle)
1582{
1583    ZipFile* zip = NULL;
1584    status_t result = UNKNOWN_ERROR;
1585    const char* zipFileName;
1586
1587    if (bundle->getFileSpecCount() < 1) {
1588        fprintf(stderr, "ERROR: must specify zip file name\n");
1589        goto bail;
1590    }
1591    zipFileName = bundle->getFileSpecEntry(0);
1592
1593    if (bundle->getFileSpecCount() < 2) {
1594        fprintf(stderr, "NOTE: nothing to do\n");
1595        goto bail;
1596    }
1597
1598    zip = openReadWrite(zipFileName, false);
1599    if (zip == NULL) {
1600        fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
1601            zipFileName);
1602        goto bail;
1603    }
1604
1605    for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1606        const char* fileName = bundle->getFileSpecEntry(i);
1607        ZipEntry* entry;
1608
1609        entry = zip->getEntryByName(fileName);
1610        if (entry == NULL) {
1611            printf(" '%s' NOT FOUND\n", fileName);
1612            continue;
1613        }
1614
1615        result = zip->remove(entry);
1616
1617        if (result != NO_ERROR) {
1618            fprintf(stderr, "Unable to delete '%s' from '%s'\n",
1619                bundle->getFileSpecEntry(i), zipFileName);
1620            goto bail;
1621        }
1622    }
1623
1624    /* update the archive */
1625    zip->flush();
1626
1627bail:
1628    delete zip;
1629    return (result != NO_ERROR);
1630}
1631
1632
1633/*
1634 * Package up an asset directory and associated application files.
1635 */
1636int doPackage(Bundle* bundle)
1637{
1638    const char* outputAPKFile;
1639    int retVal = 1;
1640    status_t err;
1641    sp<AaptAssets> assets;
1642    int N;
1643    FILE* fp;
1644    String8 dependencyFile;
1645
1646    // -c zz_ZZ means do pseudolocalization
1647    ResourceFilter filter;
1648    err = filter.parse(bundle->getConfigurations());
1649    if (err != NO_ERROR) {
1650        goto bail;
1651    }
1652    if (filter.containsPseudo()) {
1653        bundle->setPseudolocalize(true);
1654    }
1655
1656    N = bundle->getFileSpecCount();
1657    if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
1658            && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDir() == NULL) {
1659        fprintf(stderr, "ERROR: no input files\n");
1660        goto bail;
1661    }
1662
1663    outputAPKFile = bundle->getOutputAPKFile();
1664
1665    // Make sure the filenames provided exist and are of the appropriate type.
1666    if (outputAPKFile) {
1667        FileType type;
1668        type = getFileType(outputAPKFile);
1669        if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
1670            fprintf(stderr,
1671                "ERROR: output file '%s' exists but is not regular file\n",
1672                outputAPKFile);
1673            goto bail;
1674        }
1675    }
1676
1677    // Load the assets.
1678    assets = new AaptAssets();
1679
1680    // Set up the resource gathering in assets if we're going to generate
1681    // dependency files. Every time we encounter a resource while slurping
1682    // the tree, we'll add it to these stores so we have full resource paths
1683    // to write to a dependency file.
1684    if (bundle->getGenDependencies()) {
1685        sp<FilePathStore> resPathStore = new FilePathStore;
1686        assets->setFullResPaths(resPathStore);
1687        sp<FilePathStore> assetPathStore = new FilePathStore;
1688        assets->setFullAssetPaths(assetPathStore);
1689    }
1690
1691    err = assets->slurpFromArgs(bundle);
1692    if (err < 0) {
1693        goto bail;
1694    }
1695
1696    if (bundle->getVerbose()) {
1697        assets->print(String8());
1698    }
1699
1700    // If they asked for any fileAs that need to be compiled, do so.
1701    if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
1702        err = buildResources(bundle, assets);
1703        if (err != 0) {
1704            goto bail;
1705        }
1706    }
1707
1708    // At this point we've read everything and processed everything.  From here
1709    // on out it's just writing output files.
1710    if (SourcePos::hasErrors()) {
1711        goto bail;
1712    }
1713
1714    // Update symbols with information about which ones are needed as Java symbols.
1715    assets->applyJavaSymbols();
1716    if (SourcePos::hasErrors()) {
1717        goto bail;
1718    }
1719
1720    // If we've been asked to generate a dependency file, do that here
1721    if (bundle->getGenDependencies()) {
1722        // If this is the packaging step, generate the dependency file next to
1723        // the output apk (e.g. bin/resources.ap_.d)
1724        if (outputAPKFile) {
1725            dependencyFile = String8(outputAPKFile);
1726            // Add the .d extension to the dependency file.
1727            dependencyFile.append(".d");
1728        } else {
1729            // Else if this is the R.java dependency generation step,
1730            // generate the dependency file in the R.java package subdirectory
1731            // e.g. gen/com/foo/app/R.java.d
1732            dependencyFile = String8(bundle->getRClassDir());
1733            dependencyFile.appendPath("R.java.d");
1734        }
1735        // Make sure we have a clean dependency file to start with
1736        fp = fopen(dependencyFile, "w");
1737        fclose(fp);
1738    }
1739
1740    // Write out R.java constants
1741    if (!assets->havePrivateSymbols()) {
1742        if (bundle->getCustomPackage() == NULL) {
1743            // Write the R.java file into the appropriate class directory
1744            // e.g. gen/com/foo/app/R.java
1745            err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
1746        } else {
1747            const String8 customPkg(bundle->getCustomPackage());
1748            err = writeResourceSymbols(bundle, assets, customPkg, true);
1749        }
1750        if (err < 0) {
1751            goto bail;
1752        }
1753        // If we have library files, we're going to write our R.java file into
1754        // the appropriate class directory for those libraries as well.
1755        // e.g. gen/com/foo/app/lib/R.java
1756        if (bundle->getExtraPackages() != NULL) {
1757            // Split on colon
1758            String8 libs(bundle->getExtraPackages());
1759            char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
1760            while (packageString != NULL) {
1761                // Write the R.java file out with the correct package name
1762                err = writeResourceSymbols(bundle, assets, String8(packageString), true);
1763                if (err < 0) {
1764                    goto bail;
1765                }
1766                packageString = strtok(NULL, ":");
1767            }
1768            libs.unlockBuffer();
1769        }
1770    } else {
1771        err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
1772        if (err < 0) {
1773            goto bail;
1774        }
1775        err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
1776        if (err < 0) {
1777            goto bail;
1778        }
1779    }
1780
1781    // Write out the ProGuard file
1782    err = writeProguardFile(bundle, assets);
1783    if (err < 0) {
1784        goto bail;
1785    }
1786
1787    // Write the apk
1788    if (outputAPKFile) {
1789        err = writeAPK(bundle, assets, String8(outputAPKFile));
1790        if (err != NO_ERROR) {
1791            fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
1792            goto bail;
1793        }
1794    }
1795
1796    // If we've been asked to generate a dependency file, we need to finish up here.
1797    // the writeResourceSymbols and writeAPK functions have already written the target
1798    // half of the dependency file, now we need to write the prerequisites. (files that
1799    // the R.java file or .ap_ file depend on)
1800    if (bundle->getGenDependencies()) {
1801        // Now that writeResourceSymbols or writeAPK has taken care of writing
1802        // the targets to our dependency file, we'll write the prereqs
1803        fp = fopen(dependencyFile, "a+");
1804        fprintf(fp, " : ");
1805        bool includeRaw = (outputAPKFile != NULL);
1806        err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
1807        // Also manually add the AndroidManifeset since it's not under res/ or assets/
1808        // and therefore was not added to our pathstores during slurping
1809        fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
1810        fclose(fp);
1811    }
1812
1813    retVal = 0;
1814bail:
1815    if (SourcePos::hasErrors()) {
1816        SourcePos::printErrors(stderr);
1817    }
1818    return retVal;
1819}
1820
1821/*
1822 * Do PNG Crunching
1823 * PRECONDITIONS
1824 *  -S flag points to a source directory containing drawable* folders
1825 *  -C flag points to destination directory. The folder structure in the
1826 *     source directory will be mirrored to the destination (cache) directory
1827 *
1828 * POSTCONDITIONS
1829 *  Destination directory will be updated to match the PNG files in
1830 *  the source directory.
1831 */
1832int doCrunch(Bundle* bundle)
1833{
1834    fprintf(stdout, "Crunching PNG Files in ");
1835    fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
1836    fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
1837
1838    updatePreProcessedCache(bundle);
1839
1840    return NO_ERROR;
1841}
1842
1843/*
1844 * Do PNG Crunching on a single flag
1845 *  -i points to a single png file
1846 *  -o points to a single png output file
1847 */
1848int doSingleCrunch(Bundle* bundle)
1849{
1850    fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
1851    fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
1852
1853    String8 input(bundle->getSingleCrunchInputFile());
1854    String8 output(bundle->getSingleCrunchOutputFile());
1855    if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
1856        // we can't return the status_t as it gets truncate to the lower 8 bits.
1857        return 42;
1858    }
1859    return NO_ERROR;
1860}
1861
1862char CONSOLE_DATA[2925] = {
1863    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1864    32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1865    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1866    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1867    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
1868    86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
1869    62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1870    32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1871    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
1872    81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
1873    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1874    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1875    32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
1876    59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1877    32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1878    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
1879    59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
1880    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1881    10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1882    32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
1883    58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
1884    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
1885    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
1886    47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
1887    121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1888    32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1889    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
1890    81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
1891    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
1892    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1893    32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
1894    59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1895    32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1896    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
1897    59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
1898    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1899    32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1900    32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
1901    70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
1902    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
1903    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1904    32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
1905    81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1906    32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1907    32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
1908    81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
1909    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
1910    32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
1911    59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
1912    60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1913    32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
1914    61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
1915    61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
1916    46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1917    32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
1918    59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
1919    109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
1920    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
1921    67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
1922    59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
1923    61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
1924    32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
1925    59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
1926    73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
1927    59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
1928    32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
1929    59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
1930    46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
1931    97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
1932    46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
1933    119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
1934    59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
1935    32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1936    32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
1937    119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
1938    59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
1939    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1940    32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
1941    81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
1942    87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1943    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
1944    81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
1945    45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
1946    32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1947    32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
1948    81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
1949    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1950    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
1951    59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
1952    59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1953    32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1954    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
1955    81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
1956    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1957    10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1958    32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
1959    81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1960    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
1961    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
1962    81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
1963    32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1964    32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1965    32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
1966    81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
1967    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
1968    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1969    58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
1970    61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1971    32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1972    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
1973    81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
1974    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1975    32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1976    32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
1977    81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1978    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
1979    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
1980    59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
1981    59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1982    32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1983    32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
1984    58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
1985    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
1986    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
1987    61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
1988    59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
1989    32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
1990    32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
1991    32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
1992    61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1993    32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1994    32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
1995    59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
1996    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
1997    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
1998    59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
1999    59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2000    32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2001    32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2002    32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2003    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2004    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2005    46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2006    46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2007    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2008    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2009    59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2010    59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2011    32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2012    32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2013    32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2014    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2015    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2016    59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2017    59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2018    32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2019    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2020    32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2021    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2022    10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2023    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2024    32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2025    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2026  };
2027