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