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