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