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