Command.cpp revision 25d35a9d68fed6304352d0bfc2e0d581ed47f8b5
1//
2// Copyright 2006 The Android Open Source Project
3//
4// Android Asset Packaging Tool main entry point.
5//
6#include "ApkBuilder.h"
7#include "Bundle.h"
8#include "Images.h"
9#include "Main.h"
10#include "ResourceFilter.h"
11#include "ResourceTable.h"
12#include "XMLNode.h"
13
14#include <utils/Errors.h>
15#include <utils/KeyedVector.h>
16#include <utils/List.h>
17#include <utils/Log.h>
18#include <utils/SortedVector.h>
19#include <utils/threads.h>
20#include <utils/Vector.h>
21
22#include <errno.h>
23#include <fcntl.h>
24
25using namespace android;
26
27/*
28 * Show version info.  All the cool kids do it.
29 */
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
718    if (bundle->getFileSpecCount() < 1) {
719        fprintf(stderr, "ERROR: no dump option specified\n");
720        return 1;
721    }
722
723    if (bundle->getFileSpecCount() < 2) {
724        fprintf(stderr, "ERROR: no dump file specified\n");
725        return 1;
726    }
727
728    const char* option = bundle->getFileSpecEntry(0);
729    const char* filename = bundle->getFileSpecEntry(1);
730
731    AssetManager assets;
732    int32_t assetsCookie;
733    if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
734        fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
735        return 1;
736    }
737
738    // Make a dummy config for retrieving resources...  we need to supply
739    // non-default values for some configs so that we can retrieve resources
740    // in the app that don't have a default.  The most important of these is
741    // the API version because key resources like icons will have an implicit
742    // version if they are using newer config types like density.
743    ResTable_config config;
744    memset(&config, 0, sizeof(ResTable_config));
745    config.language[0] = 'e';
746    config.language[1] = 'n';
747    config.country[0] = 'U';
748    config.country[1] = 'S';
749    config.orientation = ResTable_config::ORIENTATION_PORT;
750    config.density = ResTable_config::DENSITY_MEDIUM;
751    config.sdkVersion = 10000; // Very high.
752    config.screenWidthDp = 320;
753    config.screenHeightDp = 480;
754    config.smallestScreenWidthDp = 320;
755    config.screenLayout |= ResTable_config::SCREENSIZE_NORMAL;
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        return 1;
762    } else if (res.getError() != NO_ERROR) {
763        fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
764        return 1;
765    }
766
767    const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie);
768    if (dynamicRefTable == NULL) {
769        fprintf(stderr, "ERROR: failed to find dynamic reference table for asset cookie %d\n",
770                assetsCookie);
771        return 1;
772    }
773
774    Asset* asset = NULL;
775
776    if (strcmp("resources", option) == 0) {
777#ifndef HAVE_ANDROID_OS
778        res.print(bundle->getValues());
779#endif
780
781    } else if (strcmp("strings", option) == 0) {
782        const ResStringPool* pool = res.getTableStringBlock(0);
783        printStringPool(pool);
784
785    } else if (strcmp("xmltree", option) == 0) {
786        if (bundle->getFileSpecCount() < 3) {
787            fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
788            goto bail;
789        }
790
791        for (int i=2; i<bundle->getFileSpecCount(); i++) {
792            const char* resname = bundle->getFileSpecEntry(i);
793            ResXMLTree tree(dynamicRefTable);
794            asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
795            if (asset == NULL) {
796                fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
797                goto bail;
798            }
799
800            if (tree.setTo(asset->getBuffer(true),
801                           asset->getLength()) != NO_ERROR) {
802                fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
803                goto bail;
804            }
805            tree.restart();
806            printXMLBlock(&tree);
807            tree.uninit();
808            delete asset;
809            asset = NULL;
810        }
811
812    } else if (strcmp("xmlstrings", option) == 0) {
813        if (bundle->getFileSpecCount() < 3) {
814            fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
815            goto bail;
816        }
817
818        for (int i=2; i<bundle->getFileSpecCount(); i++) {
819            const char* resname = bundle->getFileSpecEntry(i);
820            asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
821            if (asset == NULL) {
822                fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
823                goto bail;
824            }
825
826            ResXMLTree tree(dynamicRefTable);
827            if (tree.setTo(asset->getBuffer(true),
828                           asset->getLength()) != NO_ERROR) {
829                fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
830                goto bail;
831            }
832            printStringPool(&tree.getStrings());
833            delete asset;
834            asset = NULL;
835        }
836
837    } else {
838        asset = assets.openNonAsset(assetsCookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
839        if (asset == NULL) {
840            fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
841            goto bail;
842        }
843
844        ResXMLTree tree(dynamicRefTable);
845        if (tree.setTo(asset->getBuffer(true),
846                       asset->getLength()) != NO_ERROR) {
847            fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
848            goto bail;
849        }
850        tree.restart();
851
852        if (strcmp("permissions", option) == 0) {
853            size_t len;
854            ResXMLTree::event_code_t code;
855            int depth = 0;
856            while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
857                if (code == ResXMLTree::END_TAG) {
858                    depth--;
859                    continue;
860                }
861                if (code != ResXMLTree::START_TAG) {
862                    continue;
863                }
864                depth++;
865                const char16_t* ctag16 = tree.getElementName(&len);
866                if (ctag16 == NULL) {
867                    fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
868                    goto bail;
869                }
870                String8 tag(ctag16);
871                //printf("Depth %d tag %s\n", depth, tag.string());
872                if (depth == 1) {
873                    if (tag != "manifest") {
874                        fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
875                        goto bail;
876                    }
877                    String8 pkg = getAttribute(tree, NULL, "package", NULL);
878                    printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
879                } else if (depth == 2 && tag == "permission") {
880                    String8 error;
881                    String8 name = getAttribute(tree, NAME_ATTR, &error);
882                    if (error != "") {
883                        fprintf(stderr, "ERROR: %s\n", error.string());
884                        goto bail;
885                    }
886                    printf("permission: %s\n",
887                            ResTable::normalizeForOutput(name.string()).string());
888                } else if (depth == 2 && tag == "uses-permission") {
889                    String8 error;
890                    String8 name = getAttribute(tree, NAME_ATTR, &error);
891                    if (error != "") {
892                        fprintf(stderr, "ERROR: %s\n", error.string());
893                        goto bail;
894                    }
895                    printUsesPermission(name,
896                            getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1) == 0,
897                            getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1));
898                }
899            }
900        } else if (strcmp("badging", option) == 0) {
901            Vector<String8> locales;
902            res.getLocales(&locales);
903
904            Vector<ResTable_config> configs;
905            res.getConfigurations(&configs);
906            SortedVector<int> densities;
907            const size_t NC = configs.size();
908            for (size_t i=0; i<NC; i++) {
909                int dens = configs[i].density;
910                if (dens == 0) {
911                    dens = 160;
912                }
913                densities.add(dens);
914            }
915
916            size_t len;
917            ResXMLTree::event_code_t code;
918            int depth = 0;
919            String8 error;
920            bool withinActivity = false;
921            bool isMainActivity = false;
922            bool isLauncherActivity = false;
923            bool isLeanbackLauncherActivity = false;
924            bool isSearchable = false;
925            bool withinApplication = false;
926            bool withinSupportsInput = false;
927            bool withinFeatureGroup = false;
928            bool withinReceiver = false;
929            bool withinService = false;
930            bool withinProvider = false;
931            bool withinIntentFilter = false;
932            bool hasMainActivity = false;
933            bool hasOtherActivities = false;
934            bool hasOtherReceivers = false;
935            bool hasOtherServices = false;
936            bool hasIntentFilter = false;
937
938            bool hasWallpaperService = false;
939            bool hasImeService = false;
940            bool hasAccessibilityService = false;
941            bool hasPrintService = false;
942            bool hasWidgetReceivers = false;
943            bool hasDeviceAdminReceiver = false;
944            bool hasPaymentService = false;
945            bool hasDocumentsProvider = false;
946            bool hasCameraActivity = false;
947            bool hasCameraSecureActivity = false;
948            bool hasLauncher = false;
949            bool hasNotificationListenerService = false;
950            bool hasDreamService = false;
951
952            bool actMainActivity = false;
953            bool actWidgetReceivers = false;
954            bool actDeviceAdminEnabled = false;
955            bool actImeService = false;
956            bool actWallpaperService = false;
957            bool actAccessibilityService = false;
958            bool actPrintService = false;
959            bool actHostApduService = false;
960            bool actOffHostApduService = false;
961            bool actDocumentsProvider = false;
962            bool actNotificationListenerService = false;
963            bool actDreamService = false;
964            bool actCamera = false;
965            bool actCameraSecure = false;
966            bool catLauncher = false;
967            bool hasMetaHostPaymentCategory = false;
968            bool hasMetaOffHostPaymentCategory = false;
969
970            // These permissions are required by services implementing services
971            // the system binds to (IME, Accessibility, PrintServices, etc.)
972            bool hasBindDeviceAdminPermission = false;
973            bool hasBindInputMethodPermission = false;
974            bool hasBindAccessibilityServicePermission = false;
975            bool hasBindPrintServicePermission = false;
976            bool hasBindNfcServicePermission = false;
977            bool hasRequiredSafAttributes = false;
978            bool hasBindNotificationListenerServicePermission = false;
979            bool hasBindDreamServicePermission = false;
980
981            // These two implement the implicit permissions that are granted
982            // to pre-1.6 applications.
983            bool hasWriteExternalStoragePermission = false;
984            bool hasReadPhoneStatePermission = false;
985
986            // If an app requests write storage, they will also get read storage.
987            bool hasReadExternalStoragePermission = false;
988
989            // Implement transition to read and write call log.
990            bool hasReadContactsPermission = false;
991            bool hasWriteContactsPermission = false;
992            bool hasReadCallLogPermission = false;
993            bool hasWriteCallLogPermission = false;
994
995            // This next group of variables is used to implement a group of
996            // backward-compatibility heuristics necessitated by the addition of
997            // some new uses-feature constants in 2.1 and 2.2. In most cases, the
998            // heuristic is "if an app requests a permission but doesn't explicitly
999            // request the corresponding <uses-feature>, presume it's there anyway".
1000
1001            // 2.2 also added some other features that apps can request, but that
1002            // have no corresponding permission, so we cannot implement any
1003            // back-compatibility heuristic for them. The below are thus unnecessary
1004            // (but are retained here for documentary purposes.)
1005            //bool specCompassFeature = false;
1006            //bool specAccelerometerFeature = false;
1007            //bool specProximityFeature = false;
1008            //bool specAmbientLightFeature = false;
1009            //bool specLiveWallpaperFeature = false;
1010
1011            int targetSdk = 0;
1012            int smallScreen = 1;
1013            int normalScreen = 1;
1014            int largeScreen = 1;
1015            int xlargeScreen = 1;
1016            int anyDensity = 1;
1017            int requiresSmallestWidthDp = 0;
1018            int compatibleWidthLimitDp = 0;
1019            int largestWidthLimitDp = 0;
1020            String8 pkg;
1021            String8 activityName;
1022            String8 activityLabel;
1023            String8 activityIcon;
1024            String8 activityBanner;
1025            String8 receiverName;
1026            String8 serviceName;
1027            Vector<String8> supportedInput;
1028
1029            FeatureGroup commonFeatures;
1030            Vector<FeatureGroup> featureGroups;
1031            KeyedVector<String8, ImpliedFeature> impliedFeatures;
1032
1033            while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
1034                if (code == ResXMLTree::END_TAG) {
1035                    depth--;
1036                    if (depth < 2) {
1037                        if (withinSupportsInput && !supportedInput.isEmpty()) {
1038                            printf("supports-input: '");
1039                            const size_t N = supportedInput.size();
1040                            for (size_t i=0; i<N; i++) {
1041                                printf("%s", ResTable::normalizeForOutput(
1042                                        supportedInput[i].string()).string());
1043                                if (i != N - 1) {
1044                                    printf("' '");
1045                                } else {
1046                                    printf("'\n");
1047                                }
1048                            }
1049                            supportedInput.clear();
1050                        }
1051                        withinApplication = false;
1052                        withinSupportsInput = false;
1053                        withinFeatureGroup = false;
1054                    } else if (depth < 3) {
1055                        if (withinActivity && isMainActivity) {
1056                            String8 aName(getComponentName(pkg, activityName));
1057                            if (isLauncherActivity) {
1058                                printf("launchable-activity:");
1059                                if (aName.length() > 0) {
1060                                    printf(" name='%s' ",
1061                                            ResTable::normalizeForOutput(aName.string()).string());
1062                                }
1063                                printf(" label='%s' icon='%s'\n",
1064                                        ResTable::normalizeForOutput(activityLabel.string()).string(),
1065                                        ResTable::normalizeForOutput(activityIcon.string()).string());
1066                            }
1067                            if (isLeanbackLauncherActivity) {
1068                                printf("leanback-launchable-activity:");
1069                                if (aName.length() > 0) {
1070                                    printf(" name='%s' ",
1071                                            ResTable::normalizeForOutput(aName.string()).string());
1072                                }
1073                                printf(" label='%s' icon='%s' banner='%s'\n",
1074                                        ResTable::normalizeForOutput(activityLabel.string()).string(),
1075                                        ResTable::normalizeForOutput(activityIcon.string()).string(),
1076                                        ResTable::normalizeForOutput(activityBanner.string()).string());
1077                            }
1078                        }
1079                        if (!hasIntentFilter) {
1080                            hasOtherActivities |= withinActivity;
1081                            hasOtherReceivers |= withinReceiver;
1082                            hasOtherServices |= withinService;
1083                        } else {
1084                            if (withinService) {
1085                                hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
1086                                        hasBindNfcServicePermission);
1087                                hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
1088                                        hasBindNfcServicePermission);
1089                            }
1090                        }
1091                        withinActivity = false;
1092                        withinService = false;
1093                        withinReceiver = false;
1094                        withinProvider = false;
1095                        hasIntentFilter = false;
1096                        isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
1097                    } else if (depth < 4) {
1098                        if (withinIntentFilter) {
1099                            if (withinActivity) {
1100                                hasMainActivity |= actMainActivity;
1101                                hasLauncher |= catLauncher;
1102                                hasCameraActivity |= actCamera;
1103                                hasCameraSecureActivity |= actCameraSecure;
1104                                hasOtherActivities |= !actMainActivity && !actCamera && !actCameraSecure;
1105                            } else if (withinReceiver) {
1106                                hasWidgetReceivers |= actWidgetReceivers;
1107                                hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
1108                                        hasBindDeviceAdminPermission);
1109                                hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled);
1110                            } else if (withinService) {
1111                                hasImeService |= actImeService;
1112                                hasWallpaperService |= actWallpaperService;
1113                                hasAccessibilityService |= (actAccessibilityService &&
1114                                        hasBindAccessibilityServicePermission);
1115                                hasPrintService |= (actPrintService && hasBindPrintServicePermission);
1116                                hasNotificationListenerService |= actNotificationListenerService &&
1117                                        hasBindNotificationListenerServicePermission;
1118                                hasDreamService |= actDreamService && hasBindDreamServicePermission;
1119                                hasOtherServices |= (!actImeService && !actWallpaperService &&
1120                                        !actAccessibilityService && !actPrintService &&
1121                                        !actHostApduService && !actOffHostApduService &&
1122                                        !actNotificationListenerService);
1123                            } else if (withinProvider) {
1124                                hasDocumentsProvider |= actDocumentsProvider && hasRequiredSafAttributes;
1125                            }
1126                        }
1127                        withinIntentFilter = false;
1128                    }
1129                    continue;
1130                }
1131                if (code != ResXMLTree::START_TAG) {
1132                    continue;
1133                }
1134                depth++;
1135
1136                const char16_t* ctag16 = tree.getElementName(&len);
1137                if (ctag16 == NULL) {
1138                    fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
1139                    goto bail;
1140                }
1141                String8 tag(ctag16);
1142                //printf("Depth %d,  %s\n", depth, tag.string());
1143                if (depth == 1) {
1144                    if (tag != "manifest") {
1145                        fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
1146                        goto bail;
1147                    }
1148                    pkg = getAttribute(tree, NULL, "package", NULL);
1149                    printf("package: name='%s' ",
1150                            ResTable::normalizeForOutput(pkg.string()).string());
1151                    int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
1152                    if (error != "") {
1153                        fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
1154                        goto bail;
1155                    }
1156                    if (versionCode > 0) {
1157                        printf("versionCode='%d' ", versionCode);
1158                    } else {
1159                        printf("versionCode='' ");
1160                    }
1161                    String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
1162                    if (error != "") {
1163                        fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
1164                        goto bail;
1165                    }
1166                    printf("versionName='%s'",
1167                            ResTable::normalizeForOutput(versionName.string()).string());
1168
1169                    String8 splitName = getAttribute(tree, NULL, "split", NULL);
1170                    if (!splitName.isEmpty()) {
1171                        printf(" split='%s'", ResTable::normalizeForOutput(
1172                                    splitName.string()).string());
1173                    }
1174                    printf("\n");
1175                } else if (depth == 2) {
1176                    withinApplication = false;
1177                    if (tag == "application") {
1178                        withinApplication = true;
1179
1180                        String8 label;
1181                        const size_t NL = locales.size();
1182                        for (size_t i=0; i<NL; i++) {
1183                            const char* localeStr =  locales[i].string();
1184                            assets.setLocale(localeStr != NULL ? localeStr : "");
1185                            String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1186                            if (llabel != "") {
1187                                if (localeStr == NULL || strlen(localeStr) == 0) {
1188                                    label = llabel;
1189                                    printf("application-label:'%s'\n",
1190                                            ResTable::normalizeForOutput(llabel.string()).string());
1191                                } else {
1192                                    if (label == "") {
1193                                        label = llabel;
1194                                    }
1195                                    printf("application-label-%s:'%s'\n", localeStr,
1196                                           ResTable::normalizeForOutput(llabel.string()).string());
1197                                }
1198                            }
1199                        }
1200
1201                        ResTable_config tmpConfig = config;
1202                        const size_t ND = densities.size();
1203                        for (size_t i=0; i<ND; i++) {
1204                            tmpConfig.density = densities[i];
1205                            assets.setConfiguration(tmpConfig);
1206                            String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1207                            if (icon != "") {
1208                                printf("application-icon-%d:'%s'\n", densities[i],
1209                                        ResTable::normalizeForOutput(icon.string()).string());
1210                            }
1211                        }
1212                        assets.setConfiguration(config);
1213
1214                        String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1215                        if (error != "") {
1216                            fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
1217                            goto bail;
1218                        }
1219                        int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
1220                        if (error != "") {
1221                            fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
1222                            goto bail;
1223                        }
1224                        printf("application: label='%s' ",
1225                                ResTable::normalizeForOutput(label.string()).string());
1226                        printf("icon='%s'\n", ResTable::normalizeForOutput(icon.string()).string());
1227                        if (testOnly != 0) {
1228                            printf("testOnly='%d'\n", testOnly);
1229                        }
1230
1231                        int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0);
1232                        if (error != "") {
1233                            fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string());
1234                            goto bail;
1235                        }
1236                        if (debuggable != 0) {
1237                            printf("application-debuggable\n");
1238                        }
1239                    } else if (tag == "uses-sdk") {
1240                        int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
1241                        if (error != "") {
1242                            error = "";
1243                            String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
1244                            if (error != "") {
1245                                fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
1246                                        error.string());
1247                                goto bail;
1248                            }
1249                            if (name == "Donut") targetSdk = 4;
1250                            printf("sdkVersion:'%s'\n",
1251                                    ResTable::normalizeForOutput(name.string()).string());
1252                        } else if (code != -1) {
1253                            targetSdk = code;
1254                            printf("sdkVersion:'%d'\n", code);
1255                        }
1256                        code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
1257                        if (code != -1) {
1258                            printf("maxSdkVersion:'%d'\n", code);
1259                        }
1260                        code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
1261                        if (error != "") {
1262                            error = "";
1263                            String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
1264                            if (error != "") {
1265                                fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
1266                                        error.string());
1267                                goto bail;
1268                            }
1269                            if (name == "Donut" && targetSdk < 4) targetSdk = 4;
1270                            printf("targetSdkVersion:'%s'\n",
1271                                    ResTable::normalizeForOutput(name.string()).string());
1272                        } else if (code != -1) {
1273                            if (targetSdk < code) {
1274                                targetSdk = code;
1275                            }
1276                            printf("targetSdkVersion:'%d'\n", code);
1277                        }
1278                    } else if (tag == "uses-configuration") {
1279                        int32_t reqTouchScreen = getIntegerAttribute(tree,
1280                                REQ_TOUCH_SCREEN_ATTR, NULL, 0);
1281                        int32_t reqKeyboardType = getIntegerAttribute(tree,
1282                                REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
1283                        int32_t reqHardKeyboard = getIntegerAttribute(tree,
1284                                REQ_HARD_KEYBOARD_ATTR, NULL, 0);
1285                        int32_t reqNavigation = getIntegerAttribute(tree,
1286                                REQ_NAVIGATION_ATTR, NULL, 0);
1287                        int32_t reqFiveWayNav = getIntegerAttribute(tree,
1288                                REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
1289                        printf("uses-configuration:");
1290                        if (reqTouchScreen != 0) {
1291                            printf(" reqTouchScreen='%d'", reqTouchScreen);
1292                        }
1293                        if (reqKeyboardType != 0) {
1294                            printf(" reqKeyboardType='%d'", reqKeyboardType);
1295                        }
1296                        if (reqHardKeyboard != 0) {
1297                            printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1298                        }
1299                        if (reqNavigation != 0) {
1300                            printf(" reqNavigation='%d'", reqNavigation);
1301                        }
1302                        if (reqFiveWayNav != 0) {
1303                            printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1304                        }
1305                        printf("\n");
1306                    } else if (tag == "supports-input") {
1307                        withinSupportsInput = true;
1308                    } else if (tag == "supports-screens") {
1309                        smallScreen = getIntegerAttribute(tree,
1310                                SMALL_SCREEN_ATTR, NULL, 1);
1311                        normalScreen = getIntegerAttribute(tree,
1312                                NORMAL_SCREEN_ATTR, NULL, 1);
1313                        largeScreen = getIntegerAttribute(tree,
1314                                LARGE_SCREEN_ATTR, NULL, 1);
1315                        xlargeScreen = getIntegerAttribute(tree,
1316                                XLARGE_SCREEN_ATTR, NULL, 1);
1317                        anyDensity = getIntegerAttribute(tree,
1318                                ANY_DENSITY_ATTR, NULL, 1);
1319                        requiresSmallestWidthDp = getIntegerAttribute(tree,
1320                                REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
1321                        compatibleWidthLimitDp = getIntegerAttribute(tree,
1322                                COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
1323                        largestWidthLimitDp = getIntegerAttribute(tree,
1324                                LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
1325                    } else if (tag == "feature-group") {
1326                        withinFeatureGroup = true;
1327                        FeatureGroup group;
1328                        group.label = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1329                        if (error != "") {
1330                            fprintf(stderr, "ERROR getting 'android:label' attribute:"
1331                                    " %s\n", error.string());
1332                            goto bail;
1333                        }
1334                        featureGroups.add(group);
1335
1336                    } else if (tag == "uses-feature") {
1337                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1338                        if (name != "" && error == "") {
1339                            int req = getIntegerAttribute(tree,
1340                                    REQUIRED_ATTR, NULL, 1);
1341
1342                            commonFeatures.features.add(name, req);
1343                            if (req) {
1344                                addParentFeatures(&commonFeatures, name);
1345                            }
1346                        } else {
1347                            int vers = getIntegerAttribute(tree,
1348                                    GL_ES_VERSION_ATTR, &error);
1349                            if (error == "") {
1350                                if (vers > commonFeatures.openGLESVersion) {
1351                                    commonFeatures.openGLESVersion = vers;
1352                                }
1353                            }
1354                        }
1355                    } else if (tag == "uses-permission") {
1356                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1357                        if (name != "" && error == "") {
1358                            if (name == "android.permission.CAMERA") {
1359                                addImpliedFeature(&impliedFeatures, "android.hardware.camera",
1360                                        String8::format("requested %s permission", name.string())
1361                                        .string());
1362                            } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
1363                                addImpliedFeature(&impliedFeatures, "android.hardware.location.gps",
1364                                        String8::format("requested %s permission", name.string())
1365                                        .string());
1366                                addImpliedFeature(&impliedFeatures, "android.hardware.location",
1367                                        String8::format("requested %s permission", name.string())
1368                                        .string());
1369                            } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
1370                                addImpliedFeature(&impliedFeatures, "android.hardware.location",
1371                                        String8::format("requested %s permission", name.string())
1372                                        .string());
1373                            } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
1374                                addImpliedFeature(&impliedFeatures, "android.hardware.location.network",
1375                                        String8::format("requested %s permission", name.string())
1376                                        .string());
1377                                addImpliedFeature(&impliedFeatures, "android.hardware.location",
1378                                        String8::format("requested %s permission", name.string())
1379                                        .string());
1380                            } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
1381                                       name == "android.permission.INSTALL_LOCATION_PROVIDER") {
1382                                addImpliedFeature(&impliedFeatures, "android.hardware.location",
1383                                        String8::format("requested %s permission", name.string())
1384                                        .string());
1385                            } else if (name == "android.permission.BLUETOOTH" ||
1386                                       name == "android.permission.BLUETOOTH_ADMIN") {
1387                                if (targetSdk > 4) {
1388                                    addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
1389                                            String8::format("requested %s permission", name.string())
1390                                            .string());
1391                                    addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
1392                                            "targetSdkVersion > 4");
1393                                }
1394                            } else if (name == "android.permission.RECORD_AUDIO") {
1395                                addImpliedFeature(&impliedFeatures, "android.hardware.microphone",
1396                                        String8::format("requested %s permission", name.string())
1397                                        .string());
1398                            } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1399                                       name == "android.permission.CHANGE_WIFI_STATE" ||
1400                                       name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
1401                                addImpliedFeature(&impliedFeatures, "android.hardware.wifi",
1402                                        String8::format("requested %s permission", name.string())
1403                                        .string());
1404                            } else if (name == "android.permission.CALL_PHONE" ||
1405                                       name == "android.permission.CALL_PRIVILEGED" ||
1406                                       name == "android.permission.MODIFY_PHONE_STATE" ||
1407                                       name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1408                                       name == "android.permission.READ_SMS" ||
1409                                       name == "android.permission.RECEIVE_SMS" ||
1410                                       name == "android.permission.RECEIVE_MMS" ||
1411                                       name == "android.permission.RECEIVE_WAP_PUSH" ||
1412                                       name == "android.permission.SEND_SMS" ||
1413                                       name == "android.permission.WRITE_APN_SETTINGS" ||
1414                                       name == "android.permission.WRITE_SMS") {
1415                                addImpliedFeature(&impliedFeatures, "android.hardware.telephony",
1416                                        String8("requested a telephony permission").string());
1417                            } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1418                                hasWriteExternalStoragePermission = true;
1419                            } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1420                                hasReadExternalStoragePermission = true;
1421                            } else if (name == "android.permission.READ_PHONE_STATE") {
1422                                hasReadPhoneStatePermission = true;
1423                            } else if (name == "android.permission.READ_CONTACTS") {
1424                                hasReadContactsPermission = true;
1425                            } else if (name == "android.permission.WRITE_CONTACTS") {
1426                                hasWriteContactsPermission = true;
1427                            } else if (name == "android.permission.READ_CALL_LOG") {
1428                                hasReadCallLogPermission = true;
1429                            } else if (name == "android.permission.WRITE_CALL_LOG") {
1430                                hasWriteCallLogPermission = true;
1431                            }
1432
1433                            printUsesPermission(name,
1434                                    getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1) == 0,
1435                                    getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1));
1436                       } else {
1437                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1438                                    error.string());
1439                            goto bail;
1440                        }
1441                    } else if (tag == "uses-package") {
1442                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1443                        if (name != "" && error == "") {
1444                            printf("uses-package:'%s'\n",
1445                                    ResTable::normalizeForOutput(name.string()).string());
1446                        } else {
1447                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1448                                    error.string());
1449                                goto bail;
1450                        }
1451                    } else if (tag == "original-package") {
1452                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1453                        if (name != "" && error == "") {
1454                            printf("original-package:'%s'\n",
1455                                    ResTable::normalizeForOutput(name.string()).string());
1456                        } else {
1457                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1458                                    error.string());
1459                                goto bail;
1460                        }
1461                    } else if (tag == "supports-gl-texture") {
1462                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1463                        if (name != "" && error == "") {
1464                            printf("supports-gl-texture:'%s'\n",
1465                                    ResTable::normalizeForOutput(name.string()).string());
1466                        } else {
1467                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1468                                    error.string());
1469                                goto bail;
1470                        }
1471                    } else if (tag == "compatible-screens") {
1472                        printCompatibleScreens(tree, &error);
1473                        if (error != "") {
1474                            fprintf(stderr, "ERROR getting compatible screens: %s\n",
1475                                    error.string());
1476                            goto bail;
1477                        }
1478                        depth--;
1479                    } else if (tag == "package-verifier") {
1480                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1481                        if (name != "" && error == "") {
1482                            String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
1483                            if (publicKey != "" && error == "") {
1484                                printf("package-verifier: name='%s' publicKey='%s'\n",
1485                                        ResTable::normalizeForOutput(name.string()).string(),
1486                                        ResTable::normalizeForOutput(publicKey.string()).string());
1487                            }
1488                        }
1489                    }
1490                } else if (depth == 3) {
1491                    withinActivity = false;
1492                    withinReceiver = false;
1493                    withinService = false;
1494                    withinProvider = false;
1495                    hasIntentFilter = false;
1496                    hasMetaHostPaymentCategory = false;
1497                    hasMetaOffHostPaymentCategory = false;
1498                    hasBindDeviceAdminPermission = false;
1499                    hasBindInputMethodPermission = false;
1500                    hasBindAccessibilityServicePermission = false;
1501                    hasBindPrintServicePermission = false;
1502                    hasBindNfcServicePermission = false;
1503                    hasRequiredSafAttributes = false;
1504                    hasBindNotificationListenerServicePermission = false;
1505                    hasBindDreamServicePermission = false;
1506                    if (withinApplication) {
1507                        if(tag == "activity") {
1508                            withinActivity = true;
1509                            activityName = getAttribute(tree, NAME_ATTR, &error);
1510                            if (error != "") {
1511                                fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1512                                        error.string());
1513                                goto bail;
1514                            }
1515
1516                            activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1517                            if (error != "") {
1518                                fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
1519                                        error.string());
1520                                goto bail;
1521                            }
1522
1523                            activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1524                            if (error != "") {
1525                                fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1526                                        error.string());
1527                                goto bail;
1528                            }
1529
1530                            activityBanner = getResolvedAttribute(&res, tree, BANNER_ATTR, &error);
1531                            if (error != "") {
1532                                fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
1533                                        error.string());
1534                                goto bail;
1535                            }
1536
1537                            int32_t orien = getResolvedIntegerAttribute(&res, tree,
1538                                    SCREEN_ORIENTATION_ATTR, &error);
1539                            if (error == "") {
1540                                if (orien == 0 || orien == 6 || orien == 8) {
1541                                    // Requests landscape, sensorLandscape, or reverseLandscape.
1542                                    addImpliedFeature(&impliedFeatures, "android.hardware.screen.landscape",
1543                                            "one or more activities have specified a landscape orientation");
1544                                } else if (orien == 1 || orien == 7 || orien == 9) {
1545                                    // Requests portrait, sensorPortrait, or reversePortrait.
1546                                    addImpliedFeature(&impliedFeatures, "android.hardware.screen.portrait",
1547                                            "one or more activities have specified a portrait orientation");
1548                                }
1549                            }
1550                        } else if (tag == "uses-library") {
1551                            String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1552                            if (error != "") {
1553                                fprintf(stderr,
1554                                        "ERROR getting 'android:name' attribute for uses-library"
1555                                        " %s\n", error.string());
1556                                goto bail;
1557                            }
1558                            int req = getIntegerAttribute(tree,
1559                                    REQUIRED_ATTR, NULL, 1);
1560                            printf("uses-library%s:'%s'\n",
1561                                    req ? "" : "-not-required", ResTable::normalizeForOutput(
1562                                            libraryName.string()).string());
1563                        } else if (tag == "receiver") {
1564                            withinReceiver = true;
1565                            receiverName = getAttribute(tree, NAME_ATTR, &error);
1566
1567                            if (error != "") {
1568                                fprintf(stderr,
1569                                        "ERROR getting 'android:name' attribute for receiver:"
1570                                        " %s\n", error.string());
1571                                goto bail;
1572                            }
1573
1574                            String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1575                            if (error == "") {
1576                                if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1577                                    hasBindDeviceAdminPermission = true;
1578                                }
1579                            } else {
1580                                fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1581                                        " receiver '%s': %s\n", receiverName.string(), error.string());
1582                            }
1583                        } else if (tag == "service") {
1584                            withinService = true;
1585                            serviceName = getAttribute(tree, NAME_ATTR, &error);
1586
1587                            if (error != "") {
1588                                fprintf(stderr, "ERROR getting 'android:name' attribute for "
1589                                        "service:%s\n", error.string());
1590                                goto bail;
1591                            }
1592
1593                            String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1594                            if (error == "") {
1595                                if (permission == "android.permission.BIND_INPUT_METHOD") {
1596                                    hasBindInputMethodPermission = true;
1597                                } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") {
1598                                    hasBindAccessibilityServicePermission = true;
1599                                } else if (permission == "android.permission.BIND_PRINT_SERVICE") {
1600                                    hasBindPrintServicePermission = true;
1601                                } else if (permission == "android.permission.BIND_NFC_SERVICE") {
1602                                    hasBindNfcServicePermission = true;
1603                                } else if (permission == "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
1604                                    hasBindNotificationListenerServicePermission = true;
1605                                } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
1606                                    hasBindDreamServicePermission = true;
1607                                }
1608                            } else {
1609                                fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1610                                        " service '%s': %s\n", serviceName.string(), error.string());
1611                            }
1612                        } else if (tag == "provider") {
1613                            withinProvider = true;
1614
1615                            bool exported = getResolvedIntegerAttribute(&res, tree, EXPORTED_ATTR, &error);
1616                            if (error != "") {
1617                                fprintf(stderr, "ERROR getting 'android:exported' attribute for provider:"
1618                                        " %s\n", error.string());
1619                                goto bail;
1620                            }
1621
1622                            bool grantUriPermissions = getResolvedIntegerAttribute(&res, tree,
1623                                    GRANT_URI_PERMISSIONS_ATTR, &error);
1624                            if (error != "") {
1625                                fprintf(stderr, "ERROR getting 'android:grantUriPermissions' attribute for provider:"
1626                                        " %s\n", error.string());
1627                                goto bail;
1628                            }
1629
1630                            String8 permission = getResolvedAttribute(&res, tree, PERMISSION_ATTR, &error);
1631                            if (error != "") {
1632                                fprintf(stderr, "ERROR getting 'android:permission' attribute for provider:"
1633                                        " %s\n", error.string());
1634                                goto bail;
1635                            }
1636
1637                            hasRequiredSafAttributes |= exported && grantUriPermissions &&
1638                                permission == "android.permission.MANAGE_DOCUMENTS";
1639
1640                        } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
1641                            String8 metaDataName = getResolvedAttribute(&res, tree, NAME_ATTR, &error);
1642                            if (error != "") {
1643                                fprintf(stderr, "ERROR getting 'android:name' attribute for "
1644                                        "meta-data:%s\n", error.string());
1645                                goto bail;
1646                            }
1647                            printf("meta-data: name='%s' ",
1648                                    ResTable::normalizeForOutput(metaDataName.string()).string());
1649                            printResolvedResourceAttribute(&res, tree, VALUE_ATTR, String8("value"),
1650                                    &error);
1651                            if (error != "") {
1652                                // Try looking for a RESOURCE_ATTR
1653                                error = "";
1654                                printResolvedResourceAttribute(&res, tree, RESOURCE_ATTR,
1655                                        String8("resource"), &error);
1656                                if (error != "") {
1657                                    fprintf(stderr, "ERROR getting 'android:value' or "
1658                                            "'android:resource' attribute for "
1659                                            "meta-data:%s\n", error.string());
1660                                    goto bail;
1661                                }
1662                            }
1663                            printf("\n");
1664                        } else if (withinSupportsInput && tag == "input-type") {
1665                            String8 name = getAttribute(tree, NAME_ATTR, &error);
1666                            if (name != "" && error == "") {
1667                                supportedInput.add(name);
1668                            } else {
1669                                fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1670                                        error.string());
1671                                goto bail;
1672                            }
1673                        }
1674                    } else if (withinFeatureGroup && tag == "uses-feature") {
1675                        FeatureGroup& top = featureGroups.editTop();
1676
1677                        String8 name = getResolvedAttribute(&res, tree, NAME_ATTR, &error);
1678                        if (name != "" && error == "") {
1679                            int required = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
1680                            top.features.add(name, required);
1681                            if (required) {
1682                                addParentFeatures(&top, name);
1683                            }
1684
1685                        } else {
1686                            int vers = getIntegerAttribute(tree, GL_ES_VERSION_ATTR, &error);
1687                            if (error == "") {
1688                                if (vers > top.openGLESVersion) {
1689                                    top.openGLESVersion = vers;
1690                                }
1691                            }
1692                        }
1693                    }
1694                } else if (depth == 4) {
1695                    if (tag == "intent-filter") {
1696                        hasIntentFilter = true;
1697                        withinIntentFilter = true;
1698                        actMainActivity = false;
1699                        actWidgetReceivers = false;
1700                        actImeService = false;
1701                        actWallpaperService = false;
1702                        actAccessibilityService = false;
1703                        actPrintService = false;
1704                        actDeviceAdminEnabled = false;
1705                        actHostApduService = false;
1706                        actOffHostApduService = false;
1707                        actDocumentsProvider = false;
1708                        actNotificationListenerService = false;
1709                        actDreamService = false;
1710                        actCamera = false;
1711                        actCameraSecure = false;
1712                        catLauncher = false;
1713                    } else if (withinService && tag == "meta-data") {
1714                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1715                        if (error != "") {
1716                            fprintf(stderr, "ERROR getting 'android:name' attribute for"
1717                                    " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1718                            goto bail;
1719                        }
1720
1721                        if (name == "android.nfc.cardemulation.host_apdu_service" ||
1722                                name == "android.nfc.cardemulation.off_host_apdu_service") {
1723                            bool offHost = true;
1724                            if (name == "android.nfc.cardemulation.host_apdu_service") {
1725                                offHost = false;
1726                            }
1727
1728                            String8 xmlPath = getResolvedAttribute(&res, tree, RESOURCE_ATTR, &error);
1729                            if (error != "") {
1730                                fprintf(stderr, "ERROR getting 'android:resource' attribute for"
1731                                        " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1732                                goto bail;
1733                            }
1734
1735                            Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1736                                    offHost, &error);
1737                            if (error != "") {
1738                                fprintf(stderr, "ERROR getting AID category for service '%s'\n",
1739                                        serviceName.string());
1740                                goto bail;
1741                            }
1742
1743                            const size_t catLen = categories.size();
1744                            for (size_t i = 0; i < catLen; i++) {
1745                                bool paymentCategory = (categories[i] == "payment");
1746                                if (offHost) {
1747                                    hasMetaOffHostPaymentCategory |= paymentCategory;
1748                                } else {
1749                                    hasMetaHostPaymentCategory |= paymentCategory;
1750                                }
1751                            }
1752                        }
1753                    }
1754                } else if ((depth == 5) && withinIntentFilter) {
1755                    String8 action;
1756                    if (tag == "action") {
1757                        action = getAttribute(tree, NAME_ATTR, &error);
1758                        if (error != "") {
1759                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1760                                    error.string());
1761                            goto bail;
1762                        }
1763
1764                        if (withinActivity) {
1765                            if (action == "android.intent.action.MAIN") {
1766                                isMainActivity = true;
1767                                actMainActivity = true;
1768                            } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
1769                                    action == "android.media.action.VIDEO_CAMERA") {
1770                                actCamera = true;
1771                            } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
1772                                actCameraSecure = true;
1773                            }
1774                        } else if (withinReceiver) {
1775                            if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1776                                actWidgetReceivers = true;
1777                            } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1778                                actDeviceAdminEnabled = true;
1779                            }
1780                        } else if (withinService) {
1781                            if (action == "android.view.InputMethod") {
1782                                actImeService = true;
1783                            } else if (action == "android.service.wallpaper.WallpaperService") {
1784                                actWallpaperService = true;
1785                            } else if (action == "android.accessibilityservice.AccessibilityService") {
1786                                actAccessibilityService = true;
1787                            } else if (action == "android.printservice.PrintService") {
1788                                actPrintService = true;
1789                            } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
1790                                actHostApduService = true;
1791                            } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
1792                                actOffHostApduService = true;
1793                            } else if (action == "android.service.notification.NotificationListenerService") {
1794                                actNotificationListenerService = true;
1795                            } else if (action == "android.service.dreams.DreamService") {
1796                                actDreamService = true;
1797                            }
1798                        } else if (withinProvider) {
1799                            if (action == "android.content.action.DOCUMENTS_PROVIDER") {
1800                                actDocumentsProvider = true;
1801                            }
1802                        }
1803                        if (action == "android.intent.action.SEARCH") {
1804                            isSearchable = true;
1805                        }
1806                    }
1807
1808                    if (tag == "category") {
1809                        String8 category = getAttribute(tree, NAME_ATTR, &error);
1810                        if (error != "") {
1811                            fprintf(stderr, "ERROR getting 'name' attribute: %s\n",
1812                                    error.string());
1813                            goto bail;
1814                        }
1815                        if (withinActivity) {
1816                            if (category == "android.intent.category.LAUNCHER") {
1817                                isLauncherActivity = true;
1818                            } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
1819                                isLeanbackLauncherActivity = true;
1820                            } else if (category == "android.intent.category.HOME") {
1821                                catLauncher = true;
1822                            }
1823                        }
1824                    }
1825                }
1826            }
1827
1828            // Pre-1.6 implicitly granted permission compatibility logic
1829            if (targetSdk < 4) {
1830                if (!hasWriteExternalStoragePermission) {
1831                    printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
1832                    printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
1833                            String8("targetSdkVersion < 4"));
1834                    hasWriteExternalStoragePermission = true;
1835                }
1836                if (!hasReadPhoneStatePermission) {
1837                    printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
1838                    printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
1839                            String8("targetSdkVersion < 4"));
1840                }
1841            }
1842
1843            // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1844            // force them to always take READ_EXTERNAL_STORAGE as well.  We always
1845            // do this (regardless of target API version) because we can't have
1846            // an app with write permission but not read permission.
1847            if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
1848                printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"));
1849                printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
1850                        String8("requested WRITE_EXTERNAL_STORAGE"));
1851            }
1852
1853            // Pre-JellyBean call log permission compatibility.
1854            if (targetSdk < 16) {
1855                if (!hasReadCallLogPermission && hasReadContactsPermission) {
1856                    printUsesPermission(String8("android.permission.READ_CALL_LOG"));
1857                    printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
1858                            String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
1859                }
1860                if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
1861                    printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
1862                    printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
1863                            String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
1864                }
1865            }
1866
1867            addImpliedFeature(&impliedFeatures, "android.hardware.touchscreen",
1868                    "default feature for all apps");
1869
1870            const size_t numFeatureGroups = featureGroups.size();
1871            if (numFeatureGroups == 0) {
1872                // If no <feature-group> tags were defined, apply auto-implied features.
1873                printFeatureGroup(commonFeatures, &impliedFeatures);
1874
1875            } else {
1876                // <feature-group> tags are defined, so we ignore implied features and
1877                for (size_t i = 0; i < numFeatureGroups; i++) {
1878                    FeatureGroup& grp = featureGroups.editItemAt(i);
1879
1880                    if (commonFeatures.openGLESVersion > grp.openGLESVersion) {
1881                        grp.openGLESVersion = commonFeatures.openGLESVersion;
1882                    }
1883
1884                    // Merge the features defined in the top level (not inside a <feature-group>)
1885                    // with this feature group.
1886                    const size_t numCommonFeatures = commonFeatures.features.size();
1887                    for (size_t j = 0; j < numCommonFeatures; j++) {
1888                        if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
1889                            grp.features.add(commonFeatures.features.keyAt(j),
1890                                    commonFeatures.features[j]);
1891                        }
1892                    }
1893
1894                   if (!grp.features.isEmpty()) {
1895                        printFeatureGroup(grp);
1896                    }
1897                }
1898            }
1899
1900
1901            if (hasWidgetReceivers) {
1902                printComponentPresence("app-widget");
1903            }
1904            if (hasDeviceAdminReceiver) {
1905                printComponentPresence("device-admin");
1906            }
1907            if (hasImeService) {
1908                printComponentPresence("ime");
1909            }
1910            if (hasWallpaperService) {
1911                printComponentPresence("wallpaper");
1912            }
1913            if (hasAccessibilityService) {
1914                printComponentPresence("accessibility");
1915            }
1916            if (hasPrintService) {
1917                printComponentPresence("print-service");
1918            }
1919            if (hasPaymentService) {
1920                printComponentPresence("payment");
1921            }
1922            if (isSearchable) {
1923                printComponentPresence("search");
1924            }
1925            if (hasDocumentsProvider) {
1926                printComponentPresence("document-provider");
1927            }
1928            if (hasLauncher) {
1929                printComponentPresence("launcher");
1930            }
1931            if (hasNotificationListenerService) {
1932                printComponentPresence("notification-listener");
1933            }
1934            if (hasDreamService) {
1935                printComponentPresence("dream");
1936            }
1937            if (hasCameraActivity) {
1938                printComponentPresence("camera");
1939            }
1940            if (hasCameraSecureActivity) {
1941                printComponentPresence("camera-secure");
1942            }
1943
1944            if (hasMainActivity) {
1945                printf("main\n");
1946            }
1947            if (hasOtherActivities) {
1948                printf("other-activities\n");
1949            }
1950             if (hasOtherReceivers) {
1951                printf("other-receivers\n");
1952            }
1953            if (hasOtherServices) {
1954                printf("other-services\n");
1955            }
1956
1957            // For modern apps, if screen size buckets haven't been specified
1958            // but the new width ranges have, then infer the buckets from them.
1959            if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1960                    && requiresSmallestWidthDp > 0) {
1961                int compatWidth = compatibleWidthLimitDp;
1962                if (compatWidth <= 0) {
1963                    compatWidth = requiresSmallestWidthDp;
1964                }
1965                if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1966                    smallScreen = -1;
1967                } else {
1968                    smallScreen = 0;
1969                }
1970                if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1971                    normalScreen = -1;
1972                } else {
1973                    normalScreen = 0;
1974                }
1975                if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1976                    largeScreen = -1;
1977                } else {
1978                    largeScreen = 0;
1979                }
1980                if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1981                    xlargeScreen = -1;
1982                } else {
1983                    xlargeScreen = 0;
1984                }
1985            }
1986
1987            // Determine default values for any unspecified screen sizes,
1988            // based on the target SDK of the package.  As of 4 (donut)
1989            // the screen size support was introduced, so all default to
1990            // enabled.
1991            if (smallScreen > 0) {
1992                smallScreen = targetSdk >= 4 ? -1 : 0;
1993            }
1994            if (normalScreen > 0) {
1995                normalScreen = -1;
1996            }
1997            if (largeScreen > 0) {
1998                largeScreen = targetSdk >= 4 ? -1 : 0;
1999            }
2000            if (xlargeScreen > 0) {
2001                // Introduced in Gingerbread.
2002                xlargeScreen = targetSdk >= 9 ? -1 : 0;
2003            }
2004            if (anyDensity > 0) {
2005                anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
2006                        || compatibleWidthLimitDp > 0) ? -1 : 0;
2007            }
2008            printf("supports-screens:");
2009            if (smallScreen != 0) {
2010                printf(" 'small'");
2011            }
2012            if (normalScreen != 0) {
2013                printf(" 'normal'");
2014            }
2015            if (largeScreen != 0) {
2016                printf(" 'large'");
2017            }
2018            if (xlargeScreen != 0) {
2019                printf(" 'xlarge'");
2020            }
2021            printf("\n");
2022            printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
2023            if (requiresSmallestWidthDp > 0) {
2024                printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
2025            }
2026            if (compatibleWidthLimitDp > 0) {
2027                printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
2028            }
2029            if (largestWidthLimitDp > 0) {
2030                printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
2031            }
2032
2033            printf("locales:");
2034            const size_t NL = locales.size();
2035            for (size_t i=0; i<NL; i++) {
2036                const char* localeStr =  locales[i].string();
2037                if (localeStr == NULL || strlen(localeStr) == 0) {
2038                    localeStr = "--_--";
2039                }
2040                printf(" '%s'", localeStr);
2041            }
2042            printf("\n");
2043
2044            printf("densities:");
2045            const size_t ND = densities.size();
2046            for (size_t i=0; i<ND; i++) {
2047                printf(" '%d'", densities[i]);
2048            }
2049            printf("\n");
2050
2051            AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
2052            if (dir != NULL) {
2053                if (dir->getFileCount() > 0) {
2054                    printf("native-code:");
2055                    for (size_t i=0; i<dir->getFileCount(); i++) {
2056                        printf(" '%s'", ResTable::normalizeForOutput(
2057                                dir->getFileName(i).string()).string());
2058                    }
2059                    printf("\n");
2060                }
2061                delete dir;
2062            }
2063        } else if (strcmp("badger", option) == 0) {
2064            printf("%s", CONSOLE_DATA);
2065        } else if (strcmp("configurations", option) == 0) {
2066            Vector<ResTable_config> configs;
2067            res.getConfigurations(&configs);
2068            const size_t N = configs.size();
2069            for (size_t i=0; i<N; i++) {
2070                printf("%s\n", configs[i].toString().string());
2071            }
2072        } else {
2073            fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2074            goto bail;
2075        }
2076    }
2077
2078    result = NO_ERROR;
2079
2080bail:
2081    if (asset) {
2082        delete asset;
2083    }
2084    return (result != NO_ERROR);
2085}
2086
2087
2088/*
2089 * Handle the "add" command, which wants to add files to a new or
2090 * pre-existing archive.
2091 */
2092int doAdd(Bundle* bundle)
2093{
2094    ZipFile* zip = NULL;
2095    status_t result = UNKNOWN_ERROR;
2096    const char* zipFileName;
2097
2098    if (bundle->getUpdate()) {
2099        /* avoid confusion */
2100        fprintf(stderr, "ERROR: can't use '-u' with add\n");
2101        goto bail;
2102    }
2103
2104    if (bundle->getFileSpecCount() < 1) {
2105        fprintf(stderr, "ERROR: must specify zip file name\n");
2106        goto bail;
2107    }
2108    zipFileName = bundle->getFileSpecEntry(0);
2109
2110    if (bundle->getFileSpecCount() < 2) {
2111        fprintf(stderr, "NOTE: nothing to do\n");
2112        goto bail;
2113    }
2114
2115    zip = openReadWrite(zipFileName, true);
2116    if (zip == NULL) {
2117        fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2118        goto bail;
2119    }
2120
2121    for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2122        const char* fileName = bundle->getFileSpecEntry(i);
2123
2124        if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
2125            printf(" '%s'... (from gzip)\n", fileName);
2126            result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
2127        } else {
2128            if (bundle->getJunkPath()) {
2129                String8 storageName = String8(fileName).getPathLeaf();
2130                printf(" '%s' as '%s'...\n", fileName,
2131                        ResTable::normalizeForOutput(storageName.string()).string());
2132                result = zip->add(fileName, storageName.string(),
2133                                  bundle->getCompressionMethod(), NULL);
2134            } else {
2135                printf(" '%s'...\n", fileName);
2136                result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2137            }
2138        }
2139        if (result != NO_ERROR) {
2140            fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2141            if (result == NAME_NOT_FOUND) {
2142                fprintf(stderr, ": file not found\n");
2143            } else if (result == ALREADY_EXISTS) {
2144                fprintf(stderr, ": already exists in archive\n");
2145            } else {
2146                fprintf(stderr, "\n");
2147            }
2148            goto bail;
2149        }
2150    }
2151
2152    result = NO_ERROR;
2153
2154bail:
2155    delete zip;
2156    return (result != NO_ERROR);
2157}
2158
2159
2160/*
2161 * Delete files from an existing archive.
2162 */
2163int doRemove(Bundle* bundle)
2164{
2165    ZipFile* zip = NULL;
2166    status_t result = UNKNOWN_ERROR;
2167    const char* zipFileName;
2168
2169    if (bundle->getFileSpecCount() < 1) {
2170        fprintf(stderr, "ERROR: must specify zip file name\n");
2171        goto bail;
2172    }
2173    zipFileName = bundle->getFileSpecEntry(0);
2174
2175    if (bundle->getFileSpecCount() < 2) {
2176        fprintf(stderr, "NOTE: nothing to do\n");
2177        goto bail;
2178    }
2179
2180    zip = openReadWrite(zipFileName, false);
2181    if (zip == NULL) {
2182        fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2183            zipFileName);
2184        goto bail;
2185    }
2186
2187    for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2188        const char* fileName = bundle->getFileSpecEntry(i);
2189        ZipEntry* entry;
2190
2191        entry = zip->getEntryByName(fileName);
2192        if (entry == NULL) {
2193            printf(" '%s' NOT FOUND\n", fileName);
2194            continue;
2195        }
2196
2197        result = zip->remove(entry);
2198
2199        if (result != NO_ERROR) {
2200            fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2201                bundle->getFileSpecEntry(i), zipFileName);
2202            goto bail;
2203        }
2204    }
2205
2206    /* update the archive */
2207    zip->flush();
2208
2209bail:
2210    delete zip;
2211    return (result != NO_ERROR);
2212}
2213
2214static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
2215    const size_t numDirs = dir->getDirs().size();
2216    for (size_t i = 0; i < numDirs; i++) {
2217        bool ignore = ignoreConfig;
2218        const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2219        const char* dirStr = subDir->getLeaf().string();
2220        if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2221            ignore = true;
2222        }
2223        status_t err = addResourcesToBuilder(subDir, builder, ignore);
2224        if (err != NO_ERROR) {
2225            return err;
2226        }
2227    }
2228
2229    const size_t numFiles = dir->getFiles().size();
2230    for (size_t i = 0; i < numFiles; i++) {
2231        sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2232        const size_t numConfigs = gp->getFiles().size();
2233        for (size_t j = 0; j < numConfigs; j++) {
2234            status_t err = NO_ERROR;
2235            if (ignoreConfig) {
2236                err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2237            } else {
2238                err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2239            }
2240            if (err != NO_ERROR) {
2241                fprintf(stderr, "Failed to add %s (%s) to builder.\n",
2242                        gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
2243                return err;
2244            }
2245        }
2246    }
2247    return NO_ERROR;
2248}
2249
2250static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2251    if (split->isBase()) {
2252        return original;
2253    }
2254
2255    String8 ext(original.getPathExtension());
2256    if (ext == String8(".apk")) {
2257        return String8::format("%s_%s%s",
2258                original.getBasePath().string(),
2259                split->getDirectorySafeName().string(),
2260                ext.string());
2261    }
2262
2263    return String8::format("%s_%s", original.string(),
2264            split->getDirectorySafeName().string());
2265}
2266
2267/*
2268 * Package up an asset directory and associated application files.
2269 */
2270int doPackage(Bundle* bundle)
2271{
2272    const char* outputAPKFile;
2273    int retVal = 1;
2274    status_t err;
2275    sp<AaptAssets> assets;
2276    int N;
2277    FILE* fp;
2278    String8 dependencyFile;
2279    sp<ApkBuilder> builder;
2280
2281    // -c en_XA or/and ar_XB means do pseudolocalization
2282    sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2283    err = configFilter->parse(bundle->getConfigurations());
2284    if (err != NO_ERROR) {
2285        goto bail;
2286    }
2287    if (configFilter->containsPseudo()) {
2288        bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2289    }
2290    if (configFilter->containsPseudoBidi()) {
2291        bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
2292    }
2293
2294    N = bundle->getFileSpecCount();
2295    if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
2296            && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
2297        fprintf(stderr, "ERROR: no input files\n");
2298        goto bail;
2299    }
2300
2301    outputAPKFile = bundle->getOutputAPKFile();
2302
2303    // Make sure the filenames provided exist and are of the appropriate type.
2304    if (outputAPKFile) {
2305        FileType type;
2306        type = getFileType(outputAPKFile);
2307        if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2308            fprintf(stderr,
2309                "ERROR: output file '%s' exists but is not regular file\n",
2310                outputAPKFile);
2311            goto bail;
2312        }
2313    }
2314
2315    // Load the assets.
2316    assets = new AaptAssets();
2317
2318    // Set up the resource gathering in assets if we're going to generate
2319    // dependency files. Every time we encounter a resource while slurping
2320    // the tree, we'll add it to these stores so we have full resource paths
2321    // to write to a dependency file.
2322    if (bundle->getGenDependencies()) {
2323        sp<FilePathStore> resPathStore = new FilePathStore;
2324        assets->setFullResPaths(resPathStore);
2325        sp<FilePathStore> assetPathStore = new FilePathStore;
2326        assets->setFullAssetPaths(assetPathStore);
2327    }
2328
2329    err = assets->slurpFromArgs(bundle);
2330    if (err < 0) {
2331        goto bail;
2332    }
2333
2334    if (bundle->getVerbose()) {
2335        assets->print(String8());
2336    }
2337
2338    // Create the ApkBuilder, which will collect the compiled files
2339    // to write to the final APK (or sets of APKs if we are building
2340    // a Split APK.
2341    builder = new ApkBuilder(configFilter);
2342
2343    // If we are generating a Split APK, find out which configurations to split on.
2344    if (bundle->getSplitConfigurations().size() > 0) {
2345        const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2346        const size_t numSplits = splitStrs.size();
2347        for (size_t i = 0; i < numSplits; i++) {
2348            std::set<ConfigDescription> configs;
2349            if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2350                fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2351                goto bail;
2352            }
2353
2354            err = builder->createSplitForConfigs(configs);
2355            if (err != NO_ERROR) {
2356                goto bail;
2357            }
2358        }
2359    }
2360
2361    // If they asked for any fileAs that need to be compiled, do so.
2362    if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
2363        err = buildResources(bundle, assets, builder);
2364        if (err != 0) {
2365            goto bail;
2366        }
2367    }
2368
2369    // At this point we've read everything and processed everything.  From here
2370    // on out it's just writing output files.
2371    if (SourcePos::hasErrors()) {
2372        goto bail;
2373    }
2374
2375    // Update symbols with information about which ones are needed as Java symbols.
2376    assets->applyJavaSymbols();
2377    if (SourcePos::hasErrors()) {
2378        goto bail;
2379    }
2380
2381    // If we've been asked to generate a dependency file, do that here
2382    if (bundle->getGenDependencies()) {
2383        // If this is the packaging step, generate the dependency file next to
2384        // the output apk (e.g. bin/resources.ap_.d)
2385        if (outputAPKFile) {
2386            dependencyFile = String8(outputAPKFile);
2387            // Add the .d extension to the dependency file.
2388            dependencyFile.append(".d");
2389        } else {
2390            // Else if this is the R.java dependency generation step,
2391            // generate the dependency file in the R.java package subdirectory
2392            // e.g. gen/com/foo/app/R.java.d
2393            dependencyFile = String8(bundle->getRClassDir());
2394            dependencyFile.appendPath("R.java.d");
2395        }
2396        // Make sure we have a clean dependency file to start with
2397        fp = fopen(dependencyFile, "w");
2398        fclose(fp);
2399    }
2400
2401    // Write out R.java constants
2402    if (!assets->havePrivateSymbols()) {
2403        if (bundle->getCustomPackage() == NULL) {
2404            // Write the R.java file into the appropriate class directory
2405            // e.g. gen/com/foo/app/R.java
2406            err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
2407        } else {
2408            const String8 customPkg(bundle->getCustomPackage());
2409            err = writeResourceSymbols(bundle, assets, customPkg, true);
2410        }
2411        if (err < 0) {
2412            goto bail;
2413        }
2414        // If we have library files, we're going to write our R.java file into
2415        // the appropriate class directory for those libraries as well.
2416        // e.g. gen/com/foo/app/lib/R.java
2417        if (bundle->getExtraPackages() != NULL) {
2418            // Split on colon
2419            String8 libs(bundle->getExtraPackages());
2420            char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2421            while (packageString != NULL) {
2422                // Write the R.java file out with the correct package name
2423                err = writeResourceSymbols(bundle, assets, String8(packageString), true);
2424                if (err < 0) {
2425                    goto bail;
2426                }
2427                packageString = strtok(NULL, ":");
2428            }
2429            libs.unlockBuffer();
2430        }
2431    } else {
2432        err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
2433        if (err < 0) {
2434            goto bail;
2435        }
2436        err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
2437        if (err < 0) {
2438            goto bail;
2439        }
2440    }
2441
2442    // Write out the ProGuard file
2443    err = writeProguardFile(bundle, assets);
2444    if (err < 0) {
2445        goto bail;
2446    }
2447
2448    // Write the apk
2449    if (outputAPKFile) {
2450        // Gather all resources and add them to the APK Builder. The builder will then
2451        // figure out which Split they belong in.
2452        err = addResourcesToBuilder(assets, builder);
2453        if (err != NO_ERROR) {
2454            goto bail;
2455        }
2456
2457        const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2458        const size_t numSplits = splits.size();
2459        for (size_t i = 0; i < numSplits; i++) {
2460            const sp<ApkSplit>& split = splits[i];
2461            String8 outputPath = buildApkName(String8(outputAPKFile), split);
2462            err = writeAPK(bundle, outputPath, split);
2463            if (err != NO_ERROR) {
2464                fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2465                goto bail;
2466            }
2467        }
2468    }
2469
2470    // If we've been asked to generate a dependency file, we need to finish up here.
2471    // the writeResourceSymbols and writeAPK functions have already written the target
2472    // half of the dependency file, now we need to write the prerequisites. (files that
2473    // the R.java file or .ap_ file depend on)
2474    if (bundle->getGenDependencies()) {
2475        // Now that writeResourceSymbols or writeAPK has taken care of writing
2476        // the targets to our dependency file, we'll write the prereqs
2477        fp = fopen(dependencyFile, "a+");
2478        fprintf(fp, " : ");
2479        bool includeRaw = (outputAPKFile != NULL);
2480        err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2481        // Also manually add the AndroidManifeset since it's not under res/ or assets/
2482        // and therefore was not added to our pathstores during slurping
2483        fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2484        fclose(fp);
2485    }
2486
2487    retVal = 0;
2488bail:
2489    if (SourcePos::hasErrors()) {
2490        SourcePos::printErrors(stderr);
2491    }
2492    return retVal;
2493}
2494
2495/*
2496 * Do PNG Crunching
2497 * PRECONDITIONS
2498 *  -S flag points to a source directory containing drawable* folders
2499 *  -C flag points to destination directory. The folder structure in the
2500 *     source directory will be mirrored to the destination (cache) directory
2501 *
2502 * POSTCONDITIONS
2503 *  Destination directory will be updated to match the PNG files in
2504 *  the source directory.
2505 */
2506int doCrunch(Bundle* bundle)
2507{
2508    fprintf(stdout, "Crunching PNG Files in ");
2509    fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2510    fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2511
2512    updatePreProcessedCache(bundle);
2513
2514    return NO_ERROR;
2515}
2516
2517/*
2518 * Do PNG Crunching on a single flag
2519 *  -i points to a single png file
2520 *  -o points to a single png output file
2521 */
2522int doSingleCrunch(Bundle* bundle)
2523{
2524    fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2525    fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2526
2527    String8 input(bundle->getSingleCrunchInputFile());
2528    String8 output(bundle->getSingleCrunchOutputFile());
2529
2530    if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2531        // we can't return the status_t as it gets truncate to the lower 8 bits.
2532        return 42;
2533    }
2534
2535    return NO_ERROR;
2536}
2537
2538char CONSOLE_DATA[2925] = {
2539    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2540    32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2541    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2542    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2543    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2544    86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2545    62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2546    32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2547    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2548    81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2549    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2550    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2551    32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2552    59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2553    32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2554    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2555    59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2556    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2557    10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2558    32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2559    58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2560    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2561    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2562    47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2563    121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2564    32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2565    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2566    81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2567    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2568    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2569    32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2570    59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2571    32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2572    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2573    59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2574    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2575    32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2576    32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2577    70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2578    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2579    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2580    32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2581    81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2582    32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2583    32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2584    81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2585    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2586    32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2587    59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2588    60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2589    32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2590    61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2591    61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2592    46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2593    32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2594    59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2595    109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2596    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2597    67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2598    59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2599    61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2600    32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2601    59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2602    73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2603    59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2604    32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2605    59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2606    46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2607    97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2608    46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2609    119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2610    59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2611    32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2612    32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2613    119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2614    59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2615    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2616    32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2617    81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2618    87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2619    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2620    81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2621    45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2622    32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2623    32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2624    81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2625    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2626    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2627    59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2628    59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2629    32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2630    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2631    81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2632    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2633    10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2634    32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2635    81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2636    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2637    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2638    81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2639    32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2640    32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2641    32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2642    81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2643    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2644    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2645    58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2646    61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2647    32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2648    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2649    81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2650    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2651    32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2652    32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2653    81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2654    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2655    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2656    59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2657    59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2658    32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2659    32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2660    58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2661    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2662    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2663    61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2664    59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2665    32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2666    32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2667    32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2668    61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2669    32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2670    32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2671    59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2672    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2673    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2674    59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2675    59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2676    32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2677    32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2678    32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2679    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2680    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2681    46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2682    46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2683    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2684    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2685    59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2686    59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2687    32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2688    32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2689    32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2690    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2691    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2692    59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2693    59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2694    32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2695    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2696    32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2697    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2698    10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2699    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2700    32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2701    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2702  };
2703