Command.cpp revision eb8d1be6acad180eabde84f19196b9ecaba81353
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            bool hasDreamService = false;
823
824            bool actMainActivity = false;
825            bool actWidgetReceivers = false;
826            bool actDeviceAdminEnabled = false;
827            bool actImeService = false;
828            bool actWallpaperService = false;
829            bool actAccessibilityService = false;
830            bool actPrintService = false;
831            bool actHostApduService = false;
832            bool actOffHostApduService = false;
833            bool actDocumentsProvider = false;
834            bool actNotificationListenerService = false;
835            bool actDreamService = false;
836            bool actCamera = false;
837            bool actCameraSecure = false;
838            bool catLauncher = false;
839            bool hasMetaHostPaymentCategory = false;
840            bool hasMetaOffHostPaymentCategory = false;
841
842            // These permissions are required by services implementing services
843            // the system binds to (IME, Accessibility, PrintServices, etc.)
844            bool hasBindDeviceAdminPermission = false;
845            bool hasBindInputMethodPermission = false;
846            bool hasBindAccessibilityServicePermission = false;
847            bool hasBindPrintServicePermission = false;
848            bool hasBindNfcServicePermission = false;
849            bool hasRequiredSafAttributes = false;
850            bool hasBindNotificationListenerServicePermission = false;
851            bool hasBindDreamServicePermission = false;
852
853            // These two implement the implicit permissions that are granted
854            // to pre-1.6 applications.
855            bool hasWriteExternalStoragePermission = false;
856            bool hasReadPhoneStatePermission = false;
857
858            // If an app requests write storage, they will also get read storage.
859            bool hasReadExternalStoragePermission = false;
860
861            // Implement transition to read and write call log.
862            bool hasReadContactsPermission = false;
863            bool hasWriteContactsPermission = false;
864            bool hasReadCallLogPermission = false;
865            bool hasWriteCallLogPermission = false;
866
867            // This next group of variables is used to implement a group of
868            // backward-compatibility heuristics necessitated by the addition of
869            // some new uses-feature constants in 2.1 and 2.2. In most cases, the
870            // heuristic is "if an app requests a permission but doesn't explicitly
871            // request the corresponding <uses-feature>, presume it's there anyway".
872            bool specCameraFeature = false; // camera-related
873            bool specCameraAutofocusFeature = false;
874            bool reqCameraAutofocusFeature = false;
875            bool reqCameraFlashFeature = false;
876            bool hasCameraPermission = false;
877            bool specLocationFeature = false; // location-related
878            bool specNetworkLocFeature = false;
879            bool reqNetworkLocFeature = false;
880            bool specGpsFeature = false;
881            bool reqGpsFeature = false;
882            bool hasMockLocPermission = false;
883            bool hasCoarseLocPermission = false;
884            bool hasGpsPermission = false;
885            bool hasGeneralLocPermission = false;
886            bool specBluetoothFeature = false; // Bluetooth API-related
887            bool hasBluetoothPermission = false;
888            bool specMicrophoneFeature = false; // microphone-related
889            bool hasRecordAudioPermission = false;
890            bool specWiFiFeature = false;
891            bool hasWiFiPermission = false;
892            bool specTelephonyFeature = false; // telephony-related
893            bool reqTelephonySubFeature = false;
894            bool hasTelephonyPermission = false;
895            bool specTouchscreenFeature = false; // touchscreen-related
896            bool specMultitouchFeature = false;
897            bool reqDistinctMultitouchFeature = false;
898            bool specScreenPortraitFeature = false;
899            bool specScreenLandscapeFeature = false;
900            bool reqScreenPortraitFeature = false;
901            bool reqScreenLandscapeFeature = false;
902            // 2.2 also added some other features that apps can request, but that
903            // have no corresponding permission, so we cannot implement any
904            // back-compatibility heuristic for them. The below are thus unnecessary
905            // (but are retained here for documentary purposes.)
906            //bool specCompassFeature = false;
907            //bool specAccelerometerFeature = false;
908            //bool specProximityFeature = false;
909            //bool specAmbientLightFeature = false;
910            //bool specLiveWallpaperFeature = false;
911
912            int targetSdk = 0;
913            int smallScreen = 1;
914            int normalScreen = 1;
915            int largeScreen = 1;
916            int xlargeScreen = 1;
917            int anyDensity = 1;
918            int requiresSmallestWidthDp = 0;
919            int compatibleWidthLimitDp = 0;
920            int largestWidthLimitDp = 0;
921            String8 pkg;
922            String8 activityName;
923            String8 activityLabel;
924            String8 activityIcon;
925            String8 activityBanner;
926            String8 receiverName;
927            String8 serviceName;
928            Vector<String8> supportedInput;
929            while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
930                if (code == ResXMLTree::END_TAG) {
931                    depth--;
932                    if (depth < 2) {
933                        if (withinSupportsInput && !supportedInput.isEmpty()) {
934                            printf("supports-input: '");
935                            const size_t N = supportedInput.size();
936                            for (size_t i=0; i<N; i++) {
937                                printf("%s", ResTable::normalizeForOutput(
938                                        supportedInput[i].string()).string());
939                                if (i != N - 1) {
940                                    printf("' '");
941                                } else {
942                                    printf("'\n");
943                                }
944                            }
945                            supportedInput.clear();
946                        }
947                        withinApplication = false;
948                        withinSupportsInput = false;
949                    } else if (depth < 3) {
950                        if (withinActivity && isMainActivity) {
951                            String8 aName(getComponentName(pkg, activityName));
952                            if (isLauncherActivity) {
953                                printf("launchable-activity:");
954                                if (aName.length() > 0) {
955                                    printf(" name='%s' ",
956                                            ResTable::normalizeForOutput(aName.string()).string());
957                                }
958                                printf(" label='%s' icon='%s'\n",
959                                        ResTable::normalizeForOutput(activityLabel.string()).string(),
960                                        ResTable::normalizeForOutput(activityIcon.string()).string());
961                            }
962                            if (isLeanbackLauncherActivity) {
963                                printf("leanback-launchable-activity:");
964                                if (aName.length() > 0) {
965                                    printf(" name='%s' ",
966                                            ResTable::normalizeForOutput(aName.string()).string());
967                                }
968                                printf(" label='%s' icon='%s' banner='%s'\n",
969                                        ResTable::normalizeForOutput(activityLabel.string()).string(),
970                                        ResTable::normalizeForOutput(activityIcon.string()).string(),
971                                        ResTable::normalizeForOutput(activityBanner.string()).string());
972                            }
973                        }
974                        if (!hasIntentFilter) {
975                            hasOtherActivities |= withinActivity;
976                            hasOtherReceivers |= withinReceiver;
977                            hasOtherServices |= withinService;
978                        } else {
979                            if (withinService) {
980                                hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
981                                        hasBindNfcServicePermission);
982                                hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
983                                        hasBindNfcServicePermission);
984                            }
985                        }
986                        withinActivity = false;
987                        withinService = false;
988                        withinReceiver = false;
989                        withinProvider = false;
990                        hasIntentFilter = false;
991                        isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
992                    } else if (depth < 4) {
993                        if (withinIntentFilter) {
994                            if (withinActivity) {
995                                hasMainActivity |= actMainActivity;
996                                hasLauncher |= catLauncher;
997                                hasCameraActivity |= actCamera;
998                                hasCameraSecureActivity |= actCameraSecure;
999                                hasOtherActivities |= !actMainActivity && !actCamera && !actCameraSecure;
1000                            } else if (withinReceiver) {
1001                                hasWidgetReceivers |= actWidgetReceivers;
1002                                hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
1003                                        hasBindDeviceAdminPermission);
1004                                hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled);
1005                            } else if (withinService) {
1006                                hasImeService |= actImeService;
1007                                hasWallpaperService |= actWallpaperService;
1008                                hasAccessibilityService |= (actAccessibilityService &&
1009                                        hasBindAccessibilityServicePermission);
1010                                hasPrintService |= (actPrintService && hasBindPrintServicePermission);
1011                                hasNotificationListenerService |= actNotificationListenerService &&
1012                                        hasBindNotificationListenerServicePermission;
1013                                hasDreamService |= actDreamService && hasBindDreamServicePermission;
1014                                hasOtherServices |= (!actImeService && !actWallpaperService &&
1015                                        !actAccessibilityService && !actPrintService &&
1016                                        !actHostApduService && !actOffHostApduService &&
1017                                        !actNotificationListenerService);
1018                            } else if (withinProvider) {
1019                                hasDocumentsProvider |= actDocumentsProvider && hasRequiredSafAttributes;
1020                            }
1021                        }
1022                        withinIntentFilter = false;
1023                    }
1024                    continue;
1025                }
1026                if (code != ResXMLTree::START_TAG) {
1027                    continue;
1028                }
1029                depth++;
1030
1031                const char16_t* ctag16 = tree.getElementName(&len);
1032                if (ctag16 == NULL) {
1033                    fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
1034                    goto bail;
1035                }
1036                String8 tag(ctag16);
1037                //printf("Depth %d,  %s\n", depth, tag.string());
1038                if (depth == 1) {
1039                    if (tag != "manifest") {
1040                        fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
1041                        goto bail;
1042                    }
1043                    pkg = getAttribute(tree, NULL, "package", NULL);
1044                    printf("package: name='%s' ",
1045                            ResTable::normalizeForOutput(pkg.string()).string());
1046                    int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
1047                    if (error != "") {
1048                        fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
1049                        goto bail;
1050                    }
1051                    if (versionCode > 0) {
1052                        printf("versionCode='%d' ", versionCode);
1053                    } else {
1054                        printf("versionCode='' ");
1055                    }
1056                    String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
1057                    if (error != "") {
1058                        fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
1059                        goto bail;
1060                    }
1061                    printf("versionName='%s'\n",
1062                            ResTable::normalizeForOutput(versionName.string()).string());
1063                } else if (depth == 2) {
1064                    withinApplication = false;
1065                    if (tag == "application") {
1066                        withinApplication = true;
1067
1068                        String8 label;
1069                        const size_t NL = locales.size();
1070                        for (size_t i=0; i<NL; i++) {
1071                            const char* localeStr =  locales[i].string();
1072                            assets.setLocale(localeStr != NULL ? localeStr : "");
1073                            String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1074                            if (llabel != "") {
1075                                if (localeStr == NULL || strlen(localeStr) == 0) {
1076                                    label = llabel;
1077                                    printf("application-label:'%s'\n",
1078                                            ResTable::normalizeForOutput(llabel.string()).string());
1079                                } else {
1080                                    if (label == "") {
1081                                        label = llabel;
1082                                    }
1083                                    printf("application-label-%s:'%s'\n", localeStr,
1084                                           ResTable::normalizeForOutput(llabel.string()).string());
1085                                }
1086                            }
1087                        }
1088
1089                        ResTable_config tmpConfig = config;
1090                        const size_t ND = densities.size();
1091                        for (size_t i=0; i<ND; i++) {
1092                            tmpConfig.density = densities[i];
1093                            assets.setConfiguration(tmpConfig);
1094                            String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1095                            if (icon != "") {
1096                                printf("application-icon-%d:'%s'\n", densities[i],
1097                                        ResTable::normalizeForOutput(icon.string()).string());
1098                            }
1099                        }
1100                        assets.setConfiguration(config);
1101
1102                        String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1103                        if (error != "") {
1104                            fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
1105                            goto bail;
1106                        }
1107                        int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
1108                        if (error != "") {
1109                            fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
1110                            goto bail;
1111                        }
1112                        printf("application: label='%s' ",
1113                                ResTable::normalizeForOutput(label.string()).string());
1114                        printf("icon='%s'\n", ResTable::normalizeForOutput(icon.string()).string());
1115                        if (testOnly != 0) {
1116                            printf("testOnly='%d'\n", testOnly);
1117                        }
1118
1119                        int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0);
1120                        if (error != "") {
1121                            fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string());
1122                            goto bail;
1123                        }
1124                        if (debuggable != 0) {
1125                            printf("application-debuggable\n");
1126                        }
1127                    } else if (tag == "uses-sdk") {
1128                        int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
1129                        if (error != "") {
1130                            error = "";
1131                            String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
1132                            if (error != "") {
1133                                fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
1134                                        error.string());
1135                                goto bail;
1136                            }
1137                            if (name == "Donut") targetSdk = 4;
1138                            printf("sdkVersion:'%s'\n",
1139                                    ResTable::normalizeForOutput(name.string()).string());
1140                        } else if (code != -1) {
1141                            targetSdk = code;
1142                            printf("sdkVersion:'%d'\n", code);
1143                        }
1144                        code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
1145                        if (code != -1) {
1146                            printf("maxSdkVersion:'%d'\n", code);
1147                        }
1148                        code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
1149                        if (error != "") {
1150                            error = "";
1151                            String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
1152                            if (error != "") {
1153                                fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
1154                                        error.string());
1155                                goto bail;
1156                            }
1157                            if (name == "Donut" && targetSdk < 4) targetSdk = 4;
1158                            printf("targetSdkVersion:'%s'\n",
1159                                    ResTable::normalizeForOutput(name.string()).string());
1160                        } else if (code != -1) {
1161                            if (targetSdk < code) {
1162                                targetSdk = code;
1163                            }
1164                            printf("targetSdkVersion:'%d'\n", code);
1165                        }
1166                    } else if (tag == "uses-configuration") {
1167                        int32_t reqTouchScreen = getIntegerAttribute(tree,
1168                                REQ_TOUCH_SCREEN_ATTR, NULL, 0);
1169                        int32_t reqKeyboardType = getIntegerAttribute(tree,
1170                                REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
1171                        int32_t reqHardKeyboard = getIntegerAttribute(tree,
1172                                REQ_HARD_KEYBOARD_ATTR, NULL, 0);
1173                        int32_t reqNavigation = getIntegerAttribute(tree,
1174                                REQ_NAVIGATION_ATTR, NULL, 0);
1175                        int32_t reqFiveWayNav = getIntegerAttribute(tree,
1176                                REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
1177                        printf("uses-configuration:");
1178                        if (reqTouchScreen != 0) {
1179                            printf(" reqTouchScreen='%d'", reqTouchScreen);
1180                        }
1181                        if (reqKeyboardType != 0) {
1182                            printf(" reqKeyboardType='%d'", reqKeyboardType);
1183                        }
1184                        if (reqHardKeyboard != 0) {
1185                            printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1186                        }
1187                        if (reqNavigation != 0) {
1188                            printf(" reqNavigation='%d'", reqNavigation);
1189                        }
1190                        if (reqFiveWayNav != 0) {
1191                            printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1192                        }
1193                        printf("\n");
1194                    } else if (tag == "supports-input") {
1195                        withinSupportsInput = true;
1196                    } else if (tag == "supports-screens") {
1197                        smallScreen = getIntegerAttribute(tree,
1198                                SMALL_SCREEN_ATTR, NULL, 1);
1199                        normalScreen = getIntegerAttribute(tree,
1200                                NORMAL_SCREEN_ATTR, NULL, 1);
1201                        largeScreen = getIntegerAttribute(tree,
1202                                LARGE_SCREEN_ATTR, NULL, 1);
1203                        xlargeScreen = getIntegerAttribute(tree,
1204                                XLARGE_SCREEN_ATTR, NULL, 1);
1205                        anyDensity = getIntegerAttribute(tree,
1206                                ANY_DENSITY_ATTR, NULL, 1);
1207                        requiresSmallestWidthDp = getIntegerAttribute(tree,
1208                                REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
1209                        compatibleWidthLimitDp = getIntegerAttribute(tree,
1210                                COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
1211                        largestWidthLimitDp = getIntegerAttribute(tree,
1212                                LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
1213                    } else if (tag == "uses-feature") {
1214                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1215
1216                        if (name != "" && error == "") {
1217                            int req = getIntegerAttribute(tree,
1218                                    REQUIRED_ATTR, NULL, 1);
1219
1220                            if (name == "android.hardware.camera") {
1221                                specCameraFeature = true;
1222                            } else if (name == "android.hardware.camera.autofocus") {
1223                                // these have no corresponding permission to check for,
1224                                // but should imply the foundational camera permission
1225                                reqCameraAutofocusFeature = reqCameraAutofocusFeature || req;
1226                                specCameraAutofocusFeature = true;
1227                            } else if (req && (name == "android.hardware.camera.flash")) {
1228                                // these have no corresponding permission to check for,
1229                                // but should imply the foundational camera permission
1230                                reqCameraFlashFeature = true;
1231                            } else if (name == "android.hardware.location") {
1232                                specLocationFeature = true;
1233                            } else if (name == "android.hardware.location.network") {
1234                                specNetworkLocFeature = true;
1235                                reqNetworkLocFeature = reqNetworkLocFeature || req;
1236                            } else if (name == "android.hardware.location.gps") {
1237                                specGpsFeature = true;
1238                                reqGpsFeature = reqGpsFeature || req;
1239                            } else if (name == "android.hardware.bluetooth") {
1240                                specBluetoothFeature = true;
1241                            } else if (name == "android.hardware.touchscreen") {
1242                                specTouchscreenFeature = true;
1243                            } else if (name == "android.hardware.touchscreen.multitouch") {
1244                                specMultitouchFeature = true;
1245                            } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
1246                                reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req;
1247                            } else if (name == "android.hardware.microphone") {
1248                                specMicrophoneFeature = true;
1249                            } else if (name == "android.hardware.wifi") {
1250                                specWiFiFeature = true;
1251                            } else if (name == "android.hardware.telephony") {
1252                                specTelephonyFeature = true;
1253                            } else if (req && (name == "android.hardware.telephony.gsm" ||
1254                                               name == "android.hardware.telephony.cdma")) {
1255                                // these have no corresponding permission to check for,
1256                                // but should imply the foundational telephony permission
1257                                reqTelephonySubFeature = true;
1258                            } else if (name == "android.hardware.screen.portrait") {
1259                                specScreenPortraitFeature = true;
1260                            } else if (name == "android.hardware.screen.landscape") {
1261                                specScreenLandscapeFeature = true;
1262                            }
1263                            printf("uses-feature%s:'%s'\n",
1264                                    req ? "" : "-not-required",
1265                                            ResTable::normalizeForOutput(name.string()).string());
1266                        } else {
1267                            int vers = getIntegerAttribute(tree,
1268                                    GL_ES_VERSION_ATTR, &error);
1269                            if (error == "") {
1270                                printf("uses-gl-es:'0x%x'\n", vers);
1271                            }
1272                        }
1273                    } else if (tag == "uses-permission") {
1274                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1275                        if (name != "" && error == "") {
1276                            if (name == "android.permission.CAMERA") {
1277                                hasCameraPermission = true;
1278                            } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
1279                                hasGpsPermission = true;
1280                            } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
1281                                hasMockLocPermission = true;
1282                            } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
1283                                hasCoarseLocPermission = true;
1284                            } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
1285                                       name == "android.permission.INSTALL_LOCATION_PROVIDER") {
1286                                hasGeneralLocPermission = true;
1287                            } else if (name == "android.permission.BLUETOOTH" ||
1288                                       name == "android.permission.BLUETOOTH_ADMIN") {
1289                                hasBluetoothPermission = true;
1290                            } else if (name == "android.permission.RECORD_AUDIO") {
1291                                hasRecordAudioPermission = true;
1292                            } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1293                                       name == "android.permission.CHANGE_WIFI_STATE" ||
1294                                       name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
1295                                hasWiFiPermission = true;
1296                            } else if (name == "android.permission.CALL_PHONE" ||
1297                                       name == "android.permission.CALL_PRIVILEGED" ||
1298                                       name == "android.permission.MODIFY_PHONE_STATE" ||
1299                                       name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1300                                       name == "android.permission.READ_SMS" ||
1301                                       name == "android.permission.RECEIVE_SMS" ||
1302                                       name == "android.permission.RECEIVE_MMS" ||
1303                                       name == "android.permission.RECEIVE_WAP_PUSH" ||
1304                                       name == "android.permission.SEND_SMS" ||
1305                                       name == "android.permission.WRITE_APN_SETTINGS" ||
1306                                       name == "android.permission.WRITE_SMS") {
1307                                hasTelephonyPermission = true;
1308                            } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1309                                hasWriteExternalStoragePermission = true;
1310                            } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1311                                hasReadExternalStoragePermission = true;
1312                            } else if (name == "android.permission.READ_PHONE_STATE") {
1313                                hasReadPhoneStatePermission = true;
1314                            } else if (name == "android.permission.READ_CONTACTS") {
1315                                hasReadContactsPermission = true;
1316                            } else if (name == "android.permission.WRITE_CONTACTS") {
1317                                hasWriteContactsPermission = true;
1318                            } else if (name == "android.permission.READ_CALL_LOG") {
1319                                hasReadCallLogPermission = true;
1320                            } else if (name == "android.permission.WRITE_CALL_LOG") {
1321                                hasWriteCallLogPermission = true;
1322                            }
1323
1324                            printUsesPermission(name,
1325                                    getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1) == 0,
1326                                    getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1));
1327                       } else {
1328                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1329                                    error.string());
1330                            goto bail;
1331                        }
1332                    } else if (tag == "uses-package") {
1333                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1334                        if (name != "" && error == "") {
1335                            printf("uses-package:'%s'\n",
1336                                    ResTable::normalizeForOutput(name.string()).string());
1337                        } else {
1338                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1339                                    error.string());
1340                                goto bail;
1341                        }
1342                    } else if (tag == "original-package") {
1343                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1344                        if (name != "" && error == "") {
1345                            printf("original-package:'%s'\n",
1346                                    ResTable::normalizeForOutput(name.string()).string());
1347                        } else {
1348                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1349                                    error.string());
1350                                goto bail;
1351                        }
1352                    } else if (tag == "supports-gl-texture") {
1353                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1354                        if (name != "" && error == "") {
1355                            printf("supports-gl-texture:'%s'\n",
1356                                    ResTable::normalizeForOutput(name.string()).string());
1357                        } else {
1358                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1359                                    error.string());
1360                                goto bail;
1361                        }
1362                    } else if (tag == "compatible-screens") {
1363                        printCompatibleScreens(tree, &error);
1364                        if (error != "") {
1365                            fprintf(stderr, "ERROR getting compatible screens: %s\n",
1366                                    error.string());
1367                            goto bail;
1368                        }
1369                        depth--;
1370                    } else if (tag == "package-verifier") {
1371                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1372                        if (name != "" && error == "") {
1373                            String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
1374                            if (publicKey != "" && error == "") {
1375                                printf("package-verifier: name='%s' publicKey='%s'\n",
1376                                        ResTable::normalizeForOutput(name.string()).string(),
1377                                        ResTable::normalizeForOutput(publicKey.string()).string());
1378                            }
1379                        }
1380                    }
1381                } else if (depth == 3) {
1382                    withinActivity = false;
1383                    withinReceiver = false;
1384                    withinService = false;
1385                    withinProvider = false;
1386                    hasIntentFilter = false;
1387                    hasMetaHostPaymentCategory = false;
1388                    hasMetaOffHostPaymentCategory = false;
1389                    hasBindDeviceAdminPermission = false;
1390                    hasBindInputMethodPermission = false;
1391                    hasBindAccessibilityServicePermission = false;
1392                    hasBindPrintServicePermission = false;
1393                    hasBindNfcServicePermission = false;
1394                    hasRequiredSafAttributes = false;
1395                    hasBindNotificationListenerServicePermission = false;
1396                    hasBindDreamServicePermission = false;
1397                    if (withinApplication) {
1398                        if(tag == "activity") {
1399                            withinActivity = true;
1400                            activityName = getAttribute(tree, NAME_ATTR, &error);
1401                            if (error != "") {
1402                                fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1403                                        error.string());
1404                                goto bail;
1405                            }
1406
1407                            activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1408                            if (error != "") {
1409                                fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
1410                                        error.string());
1411                                goto bail;
1412                            }
1413
1414                            activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1415                            if (error != "") {
1416                                fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1417                                        error.string());
1418                                goto bail;
1419                            }
1420
1421                            activityBanner = getResolvedAttribute(&res, tree, BANNER_ATTR, &error);
1422                            if (error != "") {
1423                                fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
1424                                        error.string());
1425                                goto bail;
1426                            }
1427
1428                            int32_t orien = getResolvedIntegerAttribute(&res, tree,
1429                                    SCREEN_ORIENTATION_ATTR, &error);
1430                            if (error == "") {
1431                                if (orien == 0 || orien == 6 || orien == 8) {
1432                                    // Requests landscape, sensorLandscape, or reverseLandscape.
1433                                    reqScreenLandscapeFeature = true;
1434                                } else if (orien == 1 || orien == 7 || orien == 9) {
1435                                    // Requests portrait, sensorPortrait, or reversePortrait.
1436                                    reqScreenPortraitFeature = true;
1437                                }
1438                            }
1439                        } else if (tag == "uses-library") {
1440                            String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1441                            if (error != "") {
1442                                fprintf(stderr,
1443                                        "ERROR getting 'android:name' attribute for uses-library"
1444                                        " %s\n", error.string());
1445                                goto bail;
1446                            }
1447                            int req = getIntegerAttribute(tree,
1448                                    REQUIRED_ATTR, NULL, 1);
1449                            printf("uses-library%s:'%s'\n",
1450                                    req ? "" : "-not-required", ResTable::normalizeForOutput(
1451                                            libraryName.string()).string());
1452                        } else if (tag == "receiver") {
1453                            withinReceiver = true;
1454                            receiverName = getAttribute(tree, NAME_ATTR, &error);
1455
1456                            if (error != "") {
1457                                fprintf(stderr,
1458                                        "ERROR getting 'android:name' attribute for receiver:"
1459                                        " %s\n", error.string());
1460                                goto bail;
1461                            }
1462
1463                            String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1464                            if (error == "") {
1465                                if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1466                                    hasBindDeviceAdminPermission = true;
1467                                }
1468                            } else {
1469                                fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1470                                        " receiver '%s': %s\n", receiverName.string(), error.string());
1471                            }
1472                        } else if (tag == "service") {
1473                            withinService = true;
1474                            serviceName = getAttribute(tree, NAME_ATTR, &error);
1475
1476                            if (error != "") {
1477                                fprintf(stderr, "ERROR getting 'android:name' attribute for "
1478                                        "service:%s\n", error.string());
1479                                goto bail;
1480                            }
1481
1482                            String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1483                            if (error == "") {
1484                                if (permission == "android.permission.BIND_INPUT_METHOD") {
1485                                    hasBindInputMethodPermission = true;
1486                                } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") {
1487                                    hasBindAccessibilityServicePermission = true;
1488                                } else if (permission == "android.permission.BIND_PRINT_SERVICE") {
1489                                    hasBindPrintServicePermission = true;
1490                                } else if (permission == "android.permission.BIND_NFC_SERVICE") {
1491                                    hasBindNfcServicePermission = true;
1492                                } else if (permission == "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
1493                                    hasBindNotificationListenerServicePermission = true;
1494                                } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
1495                                    hasBindDreamServicePermission = true;
1496                                }
1497                            } else {
1498                                fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1499                                        " service '%s': %s\n", serviceName.string(), error.string());
1500                            }
1501                        } else if (tag == "provider") {
1502                            withinProvider = true;
1503
1504                            bool exported = getResolvedIntegerAttribute(&res, tree, EXPORTED_ATTR, &error);
1505                            if (error != "") {
1506                                fprintf(stderr, "ERROR getting 'android:exported' attribute for provider:"
1507                                        " %s\n", error.string());
1508                                goto bail;
1509                            }
1510
1511                            bool grantUriPermissions = getResolvedIntegerAttribute(&res, tree,
1512                                    GRANT_URI_PERMISSIONS_ATTR, &error);
1513                            if (error != "") {
1514                                fprintf(stderr, "ERROR getting 'android:grantUriPermissions' attribute for provider:"
1515                                        " %s\n", error.string());
1516                                goto bail;
1517                            }
1518
1519                            String8 permission = getResolvedAttribute(&res, tree, PERMISSION_ATTR, &error);
1520                            if (error != "") {
1521                                fprintf(stderr, "ERROR getting 'android:permission' attribute for provider:"
1522                                        " %s\n", error.string());
1523                                goto bail;
1524                            }
1525
1526                            hasRequiredSafAttributes |= exported && grantUriPermissions &&
1527                                permission == "android.permission.MANAGE_DOCUMENTS";
1528
1529                        } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
1530                            String8 metaDataName = getResolvedAttribute(&res, tree, NAME_ATTR, &error);
1531                            if (error != "") {
1532                                fprintf(stderr, "ERROR getting 'android:name' attribute for "
1533                                        "meta-data:%s\n", error.string());
1534                                goto bail;
1535                            }
1536                            printf("meta-data: name='%s' ",
1537                                    ResTable::normalizeForOutput(metaDataName.string()).string());
1538                            printResolvedResourceAttribute(&res, tree, VALUE_ATTR, String8("value"),
1539                                    &error);
1540                            if (error != "") {
1541                                // Try looking for a RESOURCE_ATTR
1542                                error = "";
1543                                printResolvedResourceAttribute(&res, tree, RESOURCE_ATTR,
1544                                        String8("resource"), &error);
1545                                if (error != "") {
1546                                    fprintf(stderr, "ERROR getting 'android:value' or "
1547                                            "'android:resource' attribute for "
1548                                            "meta-data:%s\n", error.string());
1549                                    goto bail;
1550                                }
1551                            }
1552                            printf("\n");
1553                        } else if (withinSupportsInput && tag == "input-type") {
1554                            String8 name = getAttribute(tree, NAME_ATTR, &error);
1555                            if (name != "" && error == "") {
1556                                supportedInput.add(name);
1557                            } else {
1558                                fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1559                                        error.string());
1560                                goto bail;
1561                            }
1562                        }
1563                    }
1564                } else if (depth == 4) {
1565                    if (tag == "intent-filter") {
1566                        hasIntentFilter = true;
1567                        withinIntentFilter = true;
1568                        actMainActivity = false;
1569                        actWidgetReceivers = false;
1570                        actImeService = false;
1571                        actWallpaperService = false;
1572                        actAccessibilityService = false;
1573                        actPrintService = false;
1574                        actDeviceAdminEnabled = false;
1575                        actHostApduService = false;
1576                        actOffHostApduService = false;
1577                        actDocumentsProvider = false;
1578                        actNotificationListenerService = false;
1579                        actDreamService = false;
1580                        actCamera = false;
1581                        actCameraSecure = false;
1582                        catLauncher = false;
1583                    } else if (withinService && tag == "meta-data") {
1584                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1585                        if (error != "") {
1586                            fprintf(stderr, "ERROR getting 'android:name' attribute for"
1587                                    " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1588                            goto bail;
1589                        }
1590
1591                        if (name == "android.nfc.cardemulation.host_apdu_service" ||
1592                                name == "android.nfc.cardemulation.off_host_apdu_service") {
1593                            bool offHost = true;
1594                            if (name == "android.nfc.cardemulation.host_apdu_service") {
1595                                offHost = false;
1596                            }
1597
1598                            String8 xmlPath = getResolvedAttribute(&res, tree, RESOURCE_ATTR, &error);
1599                            if (error != "") {
1600                                fprintf(stderr, "ERROR getting 'android:resource' attribute for"
1601                                        " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1602                                goto bail;
1603                            }
1604
1605                            Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1606                                    offHost, &error);
1607                            if (error != "") {
1608                                fprintf(stderr, "ERROR getting AID category for service '%s'\n",
1609                                        serviceName.string());
1610                                goto bail;
1611                            }
1612
1613                            const size_t catLen = categories.size();
1614                            for (size_t i = 0; i < catLen; i++) {
1615                                bool paymentCategory = (categories[i] == "payment");
1616                                if (offHost) {
1617                                    hasMetaOffHostPaymentCategory |= paymentCategory;
1618                                } else {
1619                                    hasMetaHostPaymentCategory |= paymentCategory;
1620                                }
1621                            }
1622                        }
1623                    }
1624                } else if ((depth == 5) && withinIntentFilter) {
1625                    String8 action;
1626                    if (tag == "action") {
1627                        action = getAttribute(tree, NAME_ATTR, &error);
1628                        if (error != "") {
1629                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1630                                    error.string());
1631                            goto bail;
1632                        }
1633
1634                        if (withinActivity) {
1635                            if (action == "android.intent.action.MAIN") {
1636                                isMainActivity = true;
1637                                actMainActivity = true;
1638                            } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
1639                                    action == "android.media.action.VIDEO_CAMERA") {
1640                                actCamera = true;
1641                            } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
1642                                actCameraSecure = true;
1643                            }
1644                        } else if (withinReceiver) {
1645                            if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1646                                actWidgetReceivers = true;
1647                            } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1648                                actDeviceAdminEnabled = true;
1649                            }
1650                        } else if (withinService) {
1651                            if (action == "android.view.InputMethod") {
1652                                actImeService = true;
1653                            } else if (action == "android.service.wallpaper.WallpaperService") {
1654                                actWallpaperService = true;
1655                            } else if (action == "android.accessibilityservice.AccessibilityService") {
1656                                actAccessibilityService = true;
1657                            } else if (action == "android.printservice.PrintService") {
1658                                actPrintService = true;
1659                            } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
1660                                actHostApduService = true;
1661                            } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
1662                                actOffHostApduService = true;
1663                            } else if (action == "android.service.notification.NotificationListenerService") {
1664                                actNotificationListenerService = true;
1665                            } else if (action == "android.service.dreams.DreamService") {
1666                                actDreamService = true;
1667                            }
1668                        } else if (withinProvider) {
1669                            if (action == "android.content.action.DOCUMENTS_PROVIDER") {
1670                                actDocumentsProvider = true;
1671                            }
1672                        }
1673                        if (action == "android.intent.action.SEARCH") {
1674                            isSearchable = true;
1675                        }
1676                    }
1677
1678                    if (tag == "category") {
1679                        String8 category = getAttribute(tree, NAME_ATTR, &error);
1680                        if (error != "") {
1681                            fprintf(stderr, "ERROR getting 'name' attribute: %s\n",
1682                                    error.string());
1683                            goto bail;
1684                        }
1685                        if (withinActivity) {
1686                            if (category == "android.intent.category.LAUNCHER") {
1687                                isLauncherActivity = true;
1688                            } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
1689                                isLeanbackLauncherActivity = true;
1690                            } else if (category == "android.intent.category.HOME") {
1691                                catLauncher = true;
1692                            }
1693                        }
1694                    }
1695                }
1696            }
1697
1698            // Pre-1.6 implicitly granted permission compatibility logic
1699            if (targetSdk < 4) {
1700                if (!hasWriteExternalStoragePermission) {
1701                    printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
1702                    printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
1703                            String8("targetSdkVersion < 4"));
1704                    hasWriteExternalStoragePermission = true;
1705                }
1706                if (!hasReadPhoneStatePermission) {
1707                    printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
1708                    printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
1709                            String8("targetSdkVersion < 4"));
1710                }
1711            }
1712
1713            // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1714            // force them to always take READ_EXTERNAL_STORAGE as well.  We always
1715            // do this (regardless of target API version) because we can't have
1716            // an app with write permission but not read permission.
1717            if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
1718                printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"));
1719                printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
1720                        String8("requested WRITE_EXTERNAL_STORAGE"));
1721            }
1722
1723            // Pre-JellyBean call log permission compatibility.
1724            if (targetSdk < 16) {
1725                if (!hasReadCallLogPermission && hasReadContactsPermission) {
1726                    printUsesPermission(String8("android.permission.READ_CALL_LOG"));
1727                    printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
1728                            String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
1729                }
1730                if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
1731                    printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
1732                    printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
1733                            String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
1734                }
1735            }
1736
1737            /* The following blocks handle printing "inferred" uses-features, based
1738             * on whether related features or permissions are used by the app.
1739             * Note that the various spec*Feature variables denote whether the
1740             * relevant tag was *present* in the AndroidManfest, not that it was
1741             * present and set to true.
1742             */
1743            // Camera-related back-compatibility logic
1744            if (!specCameraFeature) {
1745                if (reqCameraFlashFeature) {
1746                    // if app requested a sub-feature (autofocus or flash) and didn't
1747                    // request the base camera feature, we infer that it meant to
1748                    printf("uses-feature:'android.hardware.camera'\n");
1749                    printf("uses-implied-feature:'android.hardware.camera'," \
1750                            "'requested android.hardware.camera.flash feature'\n");
1751                } else if (reqCameraAutofocusFeature) {
1752                    // if app requested a sub-feature (autofocus or flash) and didn't
1753                    // request the base camera feature, we infer that it meant to
1754                    printf("uses-feature:'android.hardware.camera'\n");
1755                    printf("uses-implied-feature:'android.hardware.camera'," \
1756                            "'requested android.hardware.camera.autofocus feature'\n");
1757                } else if (hasCameraPermission) {
1758                    // if app wants to use camera but didn't request the feature, we infer
1759                    // that it meant to, and further that it wants autofocus
1760                    // (which was the 1.0 - 1.5 behavior)
1761                    printf("uses-feature:'android.hardware.camera'\n");
1762                    if (!specCameraAutofocusFeature) {
1763                        printf("uses-feature:'android.hardware.camera.autofocus'\n");
1764                        printf("uses-implied-feature:'android.hardware.camera.autofocus'," \
1765                                "'requested android.permission.CAMERA permission'\n");
1766                    }
1767                }
1768            }
1769
1770            // Location-related back-compatibility logic
1771            if (!specLocationFeature &&
1772                (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission ||
1773                 hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) {
1774                // if app either takes a location-related permission or requests one of the
1775                // sub-features, we infer that it also meant to request the base location feature
1776                printf("uses-feature:'android.hardware.location'\n");
1777                printf("uses-implied-feature:'android.hardware.location'," \
1778                        "'requested a location access permission'\n");
1779            }
1780            if (!specGpsFeature && hasGpsPermission) {
1781                // if app takes GPS (FINE location) perm but does not request the GPS
1782                // feature, we infer that it meant to
1783                printf("uses-feature:'android.hardware.location.gps'\n");
1784                printf("uses-implied-feature:'android.hardware.location.gps'," \
1785                        "'requested android.permission.ACCESS_FINE_LOCATION permission'\n");
1786            }
1787            if (!specNetworkLocFeature && hasCoarseLocPermission) {
1788                // if app takes Network location (COARSE location) perm but does not request the
1789                // network location feature, we infer that it meant to
1790                printf("uses-feature:'android.hardware.location.network'\n");
1791                printf("uses-implied-feature:'android.hardware.location.network'," \
1792                        "'requested android.permission.ACCESS_COARSE_LOCATION permission'\n");
1793            }
1794
1795            // Bluetooth-related compatibility logic
1796            if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
1797                // if app takes a Bluetooth permission but does not request the Bluetooth
1798                // feature, we infer that it meant to
1799                printf("uses-feature:'android.hardware.bluetooth'\n");
1800                printf("uses-implied-feature:'android.hardware.bluetooth'," \
1801                        "'requested android.permission.BLUETOOTH or android.permission.BLUETOOTH_ADMIN " \
1802                        "permission and targetSdkVersion > 4'\n");
1803            }
1804
1805            // Microphone-related compatibility logic
1806            if (!specMicrophoneFeature && hasRecordAudioPermission) {
1807                // if app takes the record-audio permission but does not request the microphone
1808                // feature, we infer that it meant to
1809                printf("uses-feature:'android.hardware.microphone'\n");
1810                printf("uses-implied-feature:'android.hardware.microphone'," \
1811                        "'requested android.permission.RECORD_AUDIO permission'\n");
1812            }
1813
1814            // WiFi-related compatibility logic
1815            if (!specWiFiFeature && hasWiFiPermission) {
1816                // if app takes one of the WiFi permissions but does not request the WiFi
1817                // feature, we infer that it meant to
1818                printf("uses-feature:'android.hardware.wifi'\n");
1819                printf("uses-implied-feature:'android.hardware.wifi'," \
1820                        "'requested android.permission.ACCESS_WIFI_STATE, " \
1821                        "android.permission.CHANGE_WIFI_STATE, or " \
1822                        "android.permission.CHANGE_WIFI_MULTICAST_STATE permission'\n");
1823            }
1824
1825            // Telephony-related compatibility logic
1826            if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) {
1827                // if app takes one of the telephony permissions or requests a sub-feature but
1828                // does not request the base telephony feature, we infer that it meant to
1829                printf("uses-feature:'android.hardware.telephony'\n");
1830                printf("uses-implied-feature:'android.hardware.telephony'," \
1831                        "'requested a telephony-related permission or feature'\n");
1832            }
1833
1834            // Touchscreen-related back-compatibility logic
1835            if (!specTouchscreenFeature) { // not a typo!
1836                // all apps are presumed to require a touchscreen, unless they explicitly say
1837                // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
1838                // Note that specTouchscreenFeature is true if the tag is present, regardless
1839                // of whether its value is true or false, so this is safe
1840                printf("uses-feature:'android.hardware.touchscreen'\n");
1841                printf("uses-implied-feature:'android.hardware.touchscreen'," \
1842                        "'assumed you require a touch screen unless explicitly made optional'\n");
1843            }
1844            if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
1845                // if app takes one of the telephony permissions or requests a sub-feature but
1846                // does not request the base telephony feature, we infer that it meant to
1847                printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
1848                printf("uses-implied-feature:'android.hardware.touchscreen.multitouch'," \
1849                        "'requested android.hardware.touchscreen.multitouch.distinct feature'\n");
1850            }
1851
1852            // Landscape/portrait-related compatibility logic
1853            if (!specScreenLandscapeFeature && !specScreenPortraitFeature) {
1854                // If the app has specified any activities in its manifest
1855                // that request a specific orientation, then assume that
1856                // orientation is required.
1857                if (reqScreenLandscapeFeature) {
1858                    printf("uses-feature:'android.hardware.screen.landscape'\n");
1859                    printf("uses-implied-feature:'android.hardware.screen.landscape'," \
1860                            "'one or more activities have specified a landscape orientation'\n");
1861                }
1862                if (reqScreenPortraitFeature) {
1863                    printf("uses-feature:'android.hardware.screen.portrait'\n");
1864                    printf("uses-implied-feature:'android.hardware.screen.portrait'," \
1865                            "'one or more activities have specified a portrait orientation'\n");
1866                }
1867            }
1868
1869            if (hasWidgetReceivers) {
1870                printComponentPresence("app-widget");
1871            }
1872            if (hasDeviceAdminReceiver) {
1873                printComponentPresence("device-admin");
1874            }
1875            if (hasImeService) {
1876                printComponentPresence("ime");
1877            }
1878            if (hasWallpaperService) {
1879                printComponentPresence("wallpaper");
1880            }
1881            if (hasAccessibilityService) {
1882                printComponentPresence("accessibility");
1883            }
1884            if (hasPrintService) {
1885                printComponentPresence("print-service");
1886            }
1887            if (hasPaymentService) {
1888                printComponentPresence("payment");
1889            }
1890            if (isSearchable) {
1891                printComponentPresence("search");
1892            }
1893            if (hasDocumentsProvider) {
1894                printComponentPresence("document-provider");
1895            }
1896            if (hasLauncher) {
1897                printComponentPresence("launcher");
1898            }
1899            if (hasNotificationListenerService) {
1900                printComponentPresence("notification-listener");
1901            }
1902            if (hasDreamService) {
1903                printComponentPresence("dream");
1904            }
1905            if (hasCameraActivity) {
1906                printComponentPresence("camera");
1907            }
1908            if (hasCameraSecureActivity) {
1909                printComponentPresence("camera-secure");
1910            }
1911
1912            if (hasMainActivity) {
1913                printf("main\n");
1914            }
1915            if (hasOtherActivities) {
1916                printf("other-activities\n");
1917            }
1918             if (hasOtherReceivers) {
1919                printf("other-receivers\n");
1920            }
1921            if (hasOtherServices) {
1922                printf("other-services\n");
1923            }
1924
1925            // For modern apps, if screen size buckets haven't been specified
1926            // but the new width ranges have, then infer the buckets from them.
1927            if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1928                    && requiresSmallestWidthDp > 0) {
1929                int compatWidth = compatibleWidthLimitDp;
1930                if (compatWidth <= 0) {
1931                    compatWidth = requiresSmallestWidthDp;
1932                }
1933                if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1934                    smallScreen = -1;
1935                } else {
1936                    smallScreen = 0;
1937                }
1938                if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1939                    normalScreen = -1;
1940                } else {
1941                    normalScreen = 0;
1942                }
1943                if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1944                    largeScreen = -1;
1945                } else {
1946                    largeScreen = 0;
1947                }
1948                if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1949                    xlargeScreen = -1;
1950                } else {
1951                    xlargeScreen = 0;
1952                }
1953            }
1954
1955            // Determine default values for any unspecified screen sizes,
1956            // based on the target SDK of the package.  As of 4 (donut)
1957            // the screen size support was introduced, so all default to
1958            // enabled.
1959            if (smallScreen > 0) {
1960                smallScreen = targetSdk >= 4 ? -1 : 0;
1961            }
1962            if (normalScreen > 0) {
1963                normalScreen = -1;
1964            }
1965            if (largeScreen > 0) {
1966                largeScreen = targetSdk >= 4 ? -1 : 0;
1967            }
1968            if (xlargeScreen > 0) {
1969                // Introduced in Gingerbread.
1970                xlargeScreen = targetSdk >= 9 ? -1 : 0;
1971            }
1972            if (anyDensity > 0) {
1973                anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1974                        || compatibleWidthLimitDp > 0) ? -1 : 0;
1975            }
1976            printf("supports-screens:");
1977            if (smallScreen != 0) {
1978                printf(" 'small'");
1979            }
1980            if (normalScreen != 0) {
1981                printf(" 'normal'");
1982            }
1983            if (largeScreen != 0) {
1984                printf(" 'large'");
1985            }
1986            if (xlargeScreen != 0) {
1987                printf(" 'xlarge'");
1988            }
1989            printf("\n");
1990            printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
1991            if (requiresSmallestWidthDp > 0) {
1992                printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1993            }
1994            if (compatibleWidthLimitDp > 0) {
1995                printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1996            }
1997            if (largestWidthLimitDp > 0) {
1998                printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1999            }
2000
2001            printf("locales:");
2002            const size_t NL = locales.size();
2003            for (size_t i=0; i<NL; i++) {
2004                const char* localeStr =  locales[i].string();
2005                if (localeStr == NULL || strlen(localeStr) == 0) {
2006                    localeStr = "--_--";
2007                }
2008                printf(" '%s'", localeStr);
2009            }
2010            printf("\n");
2011
2012            printf("densities:");
2013            const size_t ND = densities.size();
2014            for (size_t i=0; i<ND; i++) {
2015                printf(" '%d'", densities[i]);
2016            }
2017            printf("\n");
2018
2019            AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
2020            if (dir != NULL) {
2021                if (dir->getFileCount() > 0) {
2022                    printf("native-code:");
2023                    for (size_t i=0; i<dir->getFileCount(); i++) {
2024                        printf(" '%s'", ResTable::normalizeForOutput(
2025                                dir->getFileName(i).string()).string());
2026                    }
2027                    printf("\n");
2028                }
2029                delete dir;
2030            }
2031        } else if (strcmp("badger", option) == 0) {
2032            printf("%s", CONSOLE_DATA);
2033        } else if (strcmp("configurations", option) == 0) {
2034            Vector<ResTable_config> configs;
2035            res.getConfigurations(&configs);
2036            const size_t N = configs.size();
2037            for (size_t i=0; i<N; i++) {
2038                printf("%s\n", configs[i].toString().string());
2039            }
2040        } else {
2041            fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2042            goto bail;
2043        }
2044    }
2045
2046    result = NO_ERROR;
2047
2048bail:
2049    if (asset) {
2050        delete asset;
2051    }
2052    return (result != NO_ERROR);
2053}
2054
2055
2056/*
2057 * Handle the "add" command, which wants to add files to a new or
2058 * pre-existing archive.
2059 */
2060int doAdd(Bundle* bundle)
2061{
2062    ZipFile* zip = NULL;
2063    status_t result = UNKNOWN_ERROR;
2064    const char* zipFileName;
2065
2066    if (bundle->getUpdate()) {
2067        /* avoid confusion */
2068        fprintf(stderr, "ERROR: can't use '-u' with add\n");
2069        goto bail;
2070    }
2071
2072    if (bundle->getFileSpecCount() < 1) {
2073        fprintf(stderr, "ERROR: must specify zip file name\n");
2074        goto bail;
2075    }
2076    zipFileName = bundle->getFileSpecEntry(0);
2077
2078    if (bundle->getFileSpecCount() < 2) {
2079        fprintf(stderr, "NOTE: nothing to do\n");
2080        goto bail;
2081    }
2082
2083    zip = openReadWrite(zipFileName, true);
2084    if (zip == NULL) {
2085        fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2086        goto bail;
2087    }
2088
2089    for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2090        const char* fileName = bundle->getFileSpecEntry(i);
2091
2092        if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
2093            printf(" '%s'... (from gzip)\n", fileName);
2094            result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
2095        } else {
2096            if (bundle->getJunkPath()) {
2097                String8 storageName = String8(fileName).getPathLeaf();
2098                printf(" '%s' as '%s'...\n", fileName,
2099                        ResTable::normalizeForOutput(storageName.string()).string());
2100                result = zip->add(fileName, storageName.string(),
2101                                  bundle->getCompressionMethod(), NULL);
2102            } else {
2103                printf(" '%s'...\n", fileName);
2104                result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2105            }
2106        }
2107        if (result != NO_ERROR) {
2108            fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2109            if (result == NAME_NOT_FOUND) {
2110                fprintf(stderr, ": file not found\n");
2111            } else if (result == ALREADY_EXISTS) {
2112                fprintf(stderr, ": already exists in archive\n");
2113            } else {
2114                fprintf(stderr, "\n");
2115            }
2116            goto bail;
2117        }
2118    }
2119
2120    result = NO_ERROR;
2121
2122bail:
2123    delete zip;
2124    return (result != NO_ERROR);
2125}
2126
2127
2128/*
2129 * Delete files from an existing archive.
2130 */
2131int doRemove(Bundle* bundle)
2132{
2133    ZipFile* zip = NULL;
2134    status_t result = UNKNOWN_ERROR;
2135    const char* zipFileName;
2136
2137    if (bundle->getFileSpecCount() < 1) {
2138        fprintf(stderr, "ERROR: must specify zip file name\n");
2139        goto bail;
2140    }
2141    zipFileName = bundle->getFileSpecEntry(0);
2142
2143    if (bundle->getFileSpecCount() < 2) {
2144        fprintf(stderr, "NOTE: nothing to do\n");
2145        goto bail;
2146    }
2147
2148    zip = openReadWrite(zipFileName, false);
2149    if (zip == NULL) {
2150        fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2151            zipFileName);
2152        goto bail;
2153    }
2154
2155    for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2156        const char* fileName = bundle->getFileSpecEntry(i);
2157        ZipEntry* entry;
2158
2159        entry = zip->getEntryByName(fileName);
2160        if (entry == NULL) {
2161            printf(" '%s' NOT FOUND\n", fileName);
2162            continue;
2163        }
2164
2165        result = zip->remove(entry);
2166
2167        if (result != NO_ERROR) {
2168            fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2169                bundle->getFileSpecEntry(i), zipFileName);
2170            goto bail;
2171        }
2172    }
2173
2174    /* update the archive */
2175    zip->flush();
2176
2177bail:
2178    delete zip;
2179    return (result != NO_ERROR);
2180}
2181
2182static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
2183    const size_t numDirs = dir->getDirs().size();
2184    for (size_t i = 0; i < numDirs; i++) {
2185        bool ignore = ignoreConfig;
2186        const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2187        const char* dirStr = subDir->getLeaf().string();
2188        if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2189            ignore = true;
2190        }
2191        status_t err = addResourcesToBuilder(subDir, builder, ignore);
2192        if (err != NO_ERROR) {
2193            return err;
2194        }
2195    }
2196
2197    const size_t numFiles = dir->getFiles().size();
2198    for (size_t i = 0; i < numFiles; i++) {
2199        sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2200        const size_t numConfigs = gp->getFiles().size();
2201        for (size_t j = 0; j < numConfigs; j++) {
2202            status_t err = NO_ERROR;
2203            if (ignoreConfig) {
2204                err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2205            } else {
2206                err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2207            }
2208            if (err != NO_ERROR) {
2209                fprintf(stderr, "Failed to add %s (%s) to builder.\n",
2210                        gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
2211                return err;
2212            }
2213        }
2214    }
2215    return NO_ERROR;
2216}
2217
2218static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2219    if (split->isBase()) {
2220        return original;
2221    }
2222
2223    String8 ext(original.getPathExtension());
2224    if (ext == String8(".apk")) {
2225        return String8::format("%s_%s%s",
2226                original.getBasePath().string(),
2227                split->getDirectorySafeName().string(),
2228                ext.string());
2229    }
2230
2231    return String8::format("%s_%s", original.string(),
2232            split->getDirectorySafeName().string());
2233}
2234
2235/*
2236 * Package up an asset directory and associated application files.
2237 */
2238int doPackage(Bundle* bundle)
2239{
2240    const char* outputAPKFile;
2241    int retVal = 1;
2242    status_t err;
2243    sp<AaptAssets> assets;
2244    int N;
2245    FILE* fp;
2246    String8 dependencyFile;
2247    sp<ApkBuilder> builder;
2248
2249    // -c en_XA or/and ar_XB means do pseudolocalization
2250    sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2251    err = configFilter->parse(bundle->getConfigurations());
2252    if (err != NO_ERROR) {
2253        goto bail;
2254    }
2255    if (configFilter->containsPseudo()) {
2256        bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2257    }
2258    if (configFilter->containsPseudoBidi()) {
2259        bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
2260    }
2261
2262    N = bundle->getFileSpecCount();
2263    if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
2264            && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
2265        fprintf(stderr, "ERROR: no input files\n");
2266        goto bail;
2267    }
2268
2269    outputAPKFile = bundle->getOutputAPKFile();
2270
2271    // Make sure the filenames provided exist and are of the appropriate type.
2272    if (outputAPKFile) {
2273        FileType type;
2274        type = getFileType(outputAPKFile);
2275        if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2276            fprintf(stderr,
2277                "ERROR: output file '%s' exists but is not regular file\n",
2278                outputAPKFile);
2279            goto bail;
2280        }
2281    }
2282
2283    // Load the assets.
2284    assets = new AaptAssets();
2285
2286    // Set up the resource gathering in assets if we're going to generate
2287    // dependency files. Every time we encounter a resource while slurping
2288    // the tree, we'll add it to these stores so we have full resource paths
2289    // to write to a dependency file.
2290    if (bundle->getGenDependencies()) {
2291        sp<FilePathStore> resPathStore = new FilePathStore;
2292        assets->setFullResPaths(resPathStore);
2293        sp<FilePathStore> assetPathStore = new FilePathStore;
2294        assets->setFullAssetPaths(assetPathStore);
2295    }
2296
2297    err = assets->slurpFromArgs(bundle);
2298    if (err < 0) {
2299        goto bail;
2300    }
2301
2302    if (bundle->getVerbose()) {
2303        assets->print(String8());
2304    }
2305
2306    // Create the ApkBuilder, which will collect the compiled files
2307    // to write to the final APK (or sets of APKs if we are building
2308    // a Split APK.
2309    builder = new ApkBuilder(configFilter);
2310
2311    // If we are generating a Split APK, find out which configurations to split on.
2312    if (bundle->getSplitConfigurations().size() > 0) {
2313        const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2314        const size_t numSplits = splitStrs.size();
2315        for (size_t i = 0; i < numSplits; i++) {
2316            std::set<ConfigDescription> configs;
2317            if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2318                fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2319                goto bail;
2320            }
2321
2322            err = builder->createSplitForConfigs(configs);
2323            if (err != NO_ERROR) {
2324                goto bail;
2325            }
2326        }
2327    }
2328
2329    // If they asked for any fileAs that need to be compiled, do so.
2330    if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
2331        err = buildResources(bundle, assets, builder);
2332        if (err != 0) {
2333            goto bail;
2334        }
2335    }
2336
2337    // At this point we've read everything and processed everything.  From here
2338    // on out it's just writing output files.
2339    if (SourcePos::hasErrors()) {
2340        goto bail;
2341    }
2342
2343    // Update symbols with information about which ones are needed as Java symbols.
2344    assets->applyJavaSymbols();
2345    if (SourcePos::hasErrors()) {
2346        goto bail;
2347    }
2348
2349    // If we've been asked to generate a dependency file, do that here
2350    if (bundle->getGenDependencies()) {
2351        // If this is the packaging step, generate the dependency file next to
2352        // the output apk (e.g. bin/resources.ap_.d)
2353        if (outputAPKFile) {
2354            dependencyFile = String8(outputAPKFile);
2355            // Add the .d extension to the dependency file.
2356            dependencyFile.append(".d");
2357        } else {
2358            // Else if this is the R.java dependency generation step,
2359            // generate the dependency file in the R.java package subdirectory
2360            // e.g. gen/com/foo/app/R.java.d
2361            dependencyFile = String8(bundle->getRClassDir());
2362            dependencyFile.appendPath("R.java.d");
2363        }
2364        // Make sure we have a clean dependency file to start with
2365        fp = fopen(dependencyFile, "w");
2366        fclose(fp);
2367    }
2368
2369    // Write out R.java constants
2370    if (!assets->havePrivateSymbols()) {
2371        if (bundle->getCustomPackage() == NULL) {
2372            // Write the R.java file into the appropriate class directory
2373            // e.g. gen/com/foo/app/R.java
2374            err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
2375        } else {
2376            const String8 customPkg(bundle->getCustomPackage());
2377            err = writeResourceSymbols(bundle, assets, customPkg, true);
2378        }
2379        if (err < 0) {
2380            goto bail;
2381        }
2382        // If we have library files, we're going to write our R.java file into
2383        // the appropriate class directory for those libraries as well.
2384        // e.g. gen/com/foo/app/lib/R.java
2385        if (bundle->getExtraPackages() != NULL) {
2386            // Split on colon
2387            String8 libs(bundle->getExtraPackages());
2388            char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2389            while (packageString != NULL) {
2390                // Write the R.java file out with the correct package name
2391                err = writeResourceSymbols(bundle, assets, String8(packageString), true);
2392                if (err < 0) {
2393                    goto bail;
2394                }
2395                packageString = strtok(NULL, ":");
2396            }
2397            libs.unlockBuffer();
2398        }
2399    } else {
2400        err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
2401        if (err < 0) {
2402            goto bail;
2403        }
2404        err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
2405        if (err < 0) {
2406            goto bail;
2407        }
2408    }
2409
2410    // Write out the ProGuard file
2411    err = writeProguardFile(bundle, assets);
2412    if (err < 0) {
2413        goto bail;
2414    }
2415
2416    // Write the apk
2417    if (outputAPKFile) {
2418        // Gather all resources and add them to the APK Builder. The builder will then
2419        // figure out which Split they belong in.
2420        err = addResourcesToBuilder(assets, builder);
2421        if (err != NO_ERROR) {
2422            goto bail;
2423        }
2424
2425        const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2426        const size_t numSplits = splits.size();
2427        for (size_t i = 0; i < numSplits; i++) {
2428            const sp<ApkSplit>& split = splits[i];
2429            String8 outputPath = buildApkName(String8(outputAPKFile), split);
2430            err = writeAPK(bundle, outputPath, split);
2431            if (err != NO_ERROR) {
2432                fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2433                goto bail;
2434            }
2435        }
2436    }
2437
2438    // If we've been asked to generate a dependency file, we need to finish up here.
2439    // the writeResourceSymbols and writeAPK functions have already written the target
2440    // half of the dependency file, now we need to write the prerequisites. (files that
2441    // the R.java file or .ap_ file depend on)
2442    if (bundle->getGenDependencies()) {
2443        // Now that writeResourceSymbols or writeAPK has taken care of writing
2444        // the targets to our dependency file, we'll write the prereqs
2445        fp = fopen(dependencyFile, "a+");
2446        fprintf(fp, " : ");
2447        bool includeRaw = (outputAPKFile != NULL);
2448        err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2449        // Also manually add the AndroidManifeset since it's not under res/ or assets/
2450        // and therefore was not added to our pathstores during slurping
2451        fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2452        fclose(fp);
2453    }
2454
2455    retVal = 0;
2456bail:
2457    if (SourcePos::hasErrors()) {
2458        SourcePos::printErrors(stderr);
2459    }
2460    return retVal;
2461}
2462
2463/*
2464 * Do PNG Crunching
2465 * PRECONDITIONS
2466 *  -S flag points to a source directory containing drawable* folders
2467 *  -C flag points to destination directory. The folder structure in the
2468 *     source directory will be mirrored to the destination (cache) directory
2469 *
2470 * POSTCONDITIONS
2471 *  Destination directory will be updated to match the PNG files in
2472 *  the source directory.
2473 */
2474int doCrunch(Bundle* bundle)
2475{
2476    fprintf(stdout, "Crunching PNG Files in ");
2477    fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2478    fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2479
2480    updatePreProcessedCache(bundle);
2481
2482    return NO_ERROR;
2483}
2484
2485/*
2486 * Do PNG Crunching on a single flag
2487 *  -i points to a single png file
2488 *  -o points to a single png output file
2489 */
2490int doSingleCrunch(Bundle* bundle)
2491{
2492    fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2493    fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2494
2495    String8 input(bundle->getSingleCrunchInputFile());
2496    String8 output(bundle->getSingleCrunchOutputFile());
2497
2498    if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2499        // we can't return the status_t as it gets truncate to the lower 8 bits.
2500        return 42;
2501    }
2502
2503    return NO_ERROR;
2504}
2505
2506char CONSOLE_DATA[2925] = {
2507    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2508    32, 32, 32, 32, 32, 32, 32, 95, 46, 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, 32, 32, 32,
2510    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2511    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2512    86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2513    62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2514    32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2515    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2516    81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2517    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2518    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2519    32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2520    59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2521    32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2522    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2523    59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2524    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2525    10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2526    32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2527    58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2528    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2529    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2530    47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2531    121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2532    32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2533    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2534    81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2535    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2536    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2537    32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2538    59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2539    32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2540    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2541    59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2542    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2543    32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2544    32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2545    70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2546    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2547    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2548    32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2549    81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2550    32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2551    32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2552    81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2553    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2554    32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2555    59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2556    60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2557    32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2558    61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2559    61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2560    46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2561    32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2562    59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2563    109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2564    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2565    67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2566    59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2567    61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2568    32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2569    59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2570    73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2571    59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2572    32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2573    59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2574    46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2575    97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2576    46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2577    119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2578    59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2579    32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2580    32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2581    119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2582    59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2583    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2584    32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2585    81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2586    87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2587    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2588    81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2589    45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2590    32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2591    32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2592    81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2593    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2594    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2595    59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2596    59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2597    32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2598    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2599    81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2600    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2601    10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2602    32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2603    81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2604    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2605    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2606    81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2607    32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2608    32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2609    32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2610    81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2611    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2612    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2613    58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2614    61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2615    32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2616    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2617    81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2618    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2619    32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2620    32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2621    81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2622    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2623    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2624    59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2625    59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2626    32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2627    32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2628    58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2629    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2630    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2631    61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2632    59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2633    32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2634    32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2635    32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2636    61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2637    32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2638    32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2639    59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2640    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2641    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2642    59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2643    59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2644    32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2645    32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2646    32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2647    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2648    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2649    46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2650    46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2651    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2652    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2653    59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2654    59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2655    32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2656    32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2657    32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2658    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2659    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2660    59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2661    59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2662    32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2663    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2664    32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2665    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2666    10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2667    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2668    32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2669    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2670  };
2671