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