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