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