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