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 = 10000; // 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             size_t len;
973             ResXMLTree::event_code_t code;
974             int depth = 0;
975             String8 error;
976             bool withinActivity = false;
977             bool isMainActivity = false;
978             bool isLauncherActivity = false;
979             bool isLeanbackLauncherActivity = false;
980             bool isSearchable = false;
981             bool withinApplication = false;
982             bool withinSupportsInput = false;
983             bool withinFeatureGroup = false;
984             bool withinReceiver = false;
985             bool withinService = false;
986             bool withinProvider = false;
987             bool withinIntentFilter = false;
988             bool hasMainActivity = false;
989             bool hasOtherActivities = false;
990             bool hasOtherReceivers = false;
991             bool hasOtherServices = false;
992             bool hasIntentFilter = false;
993 
994             bool hasWallpaperService = false;
995             bool hasImeService = false;
996             bool hasAccessibilityService = false;
997             bool hasPrintService = false;
998             bool hasWidgetReceivers = false;
999             bool hasDeviceAdminReceiver = false;
1000             bool hasPaymentService = false;
1001             bool hasDocumentsProvider = false;
1002             bool hasCameraActivity = false;
1003             bool hasCameraSecureActivity = false;
1004             bool hasLauncher = false;
1005             bool hasNotificationListenerService = false;
1006             bool hasDreamService = false;
1007 
1008             bool actMainActivity = false;
1009             bool actWidgetReceivers = false;
1010             bool actDeviceAdminEnabled = false;
1011             bool actImeService = false;
1012             bool actWallpaperService = false;
1013             bool actAccessibilityService = false;
1014             bool actPrintService = false;
1015             bool actHostApduService = false;
1016             bool actOffHostApduService = false;
1017             bool actDocumentsProvider = false;
1018             bool actNotificationListenerService = false;
1019             bool actDreamService = false;
1020             bool actCamera = false;
1021             bool actCameraSecure = false;
1022             bool catLauncher = false;
1023             bool hasMetaHostPaymentCategory = false;
1024             bool hasMetaOffHostPaymentCategory = false;
1025 
1026             // These permissions are required by services implementing services
1027             // the system binds to (IME, Accessibility, PrintServices, etc.)
1028             bool hasBindDeviceAdminPermission = false;
1029             bool hasBindInputMethodPermission = false;
1030             bool hasBindAccessibilityServicePermission = false;
1031             bool hasBindPrintServicePermission = false;
1032             bool hasBindNfcServicePermission = false;
1033             bool hasRequiredSafAttributes = false;
1034             bool hasBindNotificationListenerServicePermission = false;
1035             bool hasBindDreamServicePermission = false;
1036 
1037             // These two implement the implicit permissions that are granted
1038             // to pre-1.6 applications.
1039             bool hasWriteExternalStoragePermission = false;
1040             int32_t writeExternalStoragePermissionMaxSdkVersion = -1;
1041             bool hasReadPhoneStatePermission = false;
1042 
1043             // If an app requests write storage, they will also get read storage.
1044             bool hasReadExternalStoragePermission = false;
1045 
1046             // Implement transition to read and write call log.
1047             bool hasReadContactsPermission = false;
1048             bool hasWriteContactsPermission = false;
1049             bool hasReadCallLogPermission = false;
1050             bool hasWriteCallLogPermission = false;
1051 
1052             // If an app declares itself as multiArch, we report the
1053             // native libraries differently.
1054             bool hasMultiArch = false;
1055 
1056             // This next group of variables is used to implement a group of
1057             // backward-compatibility heuristics necessitated by the addition of
1058             // some new uses-feature constants in 2.1 and 2.2. In most cases, the
1059             // heuristic is "if an app requests a permission but doesn't explicitly
1060             // request the corresponding <uses-feature>, presume it's there anyway".
1061 
1062             // 2.2 also added some other features that apps can request, but that
1063             // have no corresponding permission, so we cannot implement any
1064             // back-compatibility heuristic for them. The below are thus unnecessary
1065             // (but are retained here for documentary purposes.)
1066             //bool specCompassFeature = false;
1067             //bool specAccelerometerFeature = false;
1068             //bool specProximityFeature = false;
1069             //bool specAmbientLightFeature = false;
1070             //bool specLiveWallpaperFeature = false;
1071 
1072             int targetSdk = 0;
1073             int smallScreen = 1;
1074             int normalScreen = 1;
1075             int largeScreen = 1;
1076             int xlargeScreen = 1;
1077             int anyDensity = 1;
1078             int requiresSmallestWidthDp = 0;
1079             int compatibleWidthLimitDp = 0;
1080             int largestWidthLimitDp = 0;
1081             String8 pkg;
1082             String8 activityName;
1083             String8 activityLabel;
1084             String8 activityIcon;
1085             String8 activityBanner;
1086             String8 receiverName;
1087             String8 serviceName;
1088             Vector<String8> supportedInput;
1089 
1090             FeatureGroup commonFeatures;
1091             Vector<FeatureGroup> featureGroups;
1092             KeyedVector<String8, ImpliedFeature> impliedFeatures;
1093 
1094             while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
1095                     code != ResXMLTree::BAD_DOCUMENT) {
1096                 if (code == ResXMLTree::END_TAG) {
1097                     depth--;
1098                     if (depth < 2) {
1099                         if (withinSupportsInput && !supportedInput.isEmpty()) {
1100                             printf("supports-input: '");
1101                             const size_t N = supportedInput.size();
1102                             for (size_t i=0; i<N; i++) {
1103                                 printf("%s", ResTable::normalizeForOutput(
1104                                         supportedInput[i].string()).string());
1105                                 if (i != N - 1) {
1106                                     printf("' '");
1107                                 } else {
1108                                     printf("'\n");
1109                                 }
1110                             }
1111                             supportedInput.clear();
1112                         }
1113                         withinApplication = false;
1114                         withinSupportsInput = false;
1115                         withinFeatureGroup = false;
1116                     } else if (depth < 3) {
1117                         if (withinActivity && isMainActivity) {
1118                             String8 aName(getComponentName(pkg, activityName));
1119                             if (isLauncherActivity) {
1120                                 printf("launchable-activity:");
1121                                 if (aName.length() > 0) {
1122                                     printf(" name='%s' ",
1123                                             ResTable::normalizeForOutput(aName.string()).string());
1124                                 }
1125                                 printf(" label='%s' icon='%s'\n",
1126                                        ResTable::normalizeForOutput(activityLabel.string())
1127                                                 .string(),
1128                                        ResTable::normalizeForOutput(activityIcon.string())
1129                                                 .string());
1130                             }
1131                             if (isLeanbackLauncherActivity) {
1132                                 printf("leanback-launchable-activity:");
1133                                 if (aName.length() > 0) {
1134                                     printf(" name='%s' ",
1135                                             ResTable::normalizeForOutput(aName.string()).string());
1136                                 }
1137                                 printf(" label='%s' icon='%s' banner='%s'\n",
1138                                        ResTable::normalizeForOutput(activityLabel.string())
1139                                                 .string(),
1140                                        ResTable::normalizeForOutput(activityIcon.string())
1141                                                 .string(),
1142                                        ResTable::normalizeForOutput(activityBanner.string())
1143                                                 .string());
1144                             }
1145                         }
1146                         if (!hasIntentFilter) {
1147                             hasOtherActivities |= withinActivity;
1148                             hasOtherReceivers |= withinReceiver;
1149                             hasOtherServices |= withinService;
1150                         } else {
1151                             if (withinService) {
1152                                 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
1153                                         hasBindNfcServicePermission);
1154                                 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
1155                                         hasBindNfcServicePermission);
1156                             }
1157                         }
1158                         withinActivity = false;
1159                         withinService = false;
1160                         withinReceiver = false;
1161                         withinProvider = false;
1162                         hasIntentFilter = false;
1163                         isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
1164                     } else if (depth < 4) {
1165                         if (withinIntentFilter) {
1166                             if (withinActivity) {
1167                                 hasMainActivity |= actMainActivity;
1168                                 hasLauncher |= catLauncher;
1169                                 hasCameraActivity |= actCamera;
1170                                 hasCameraSecureActivity |= actCameraSecure;
1171                                 hasOtherActivities |=
1172                                         !actMainActivity && !actCamera && !actCameraSecure;
1173                             } else if (withinReceiver) {
1174                                 hasWidgetReceivers |= actWidgetReceivers;
1175                                 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
1176                                         hasBindDeviceAdminPermission);
1177                                 hasOtherReceivers |=
1178                                         (!actWidgetReceivers && !actDeviceAdminEnabled);
1179                             } else if (withinService) {
1180                                 hasImeService |= actImeService;
1181                                 hasWallpaperService |= actWallpaperService;
1182                                 hasAccessibilityService |= (actAccessibilityService &&
1183                                         hasBindAccessibilityServicePermission);
1184                                 hasPrintService |=
1185                                         (actPrintService && hasBindPrintServicePermission);
1186                                 hasNotificationListenerService |= actNotificationListenerService &&
1187                                         hasBindNotificationListenerServicePermission;
1188                                 hasDreamService |= actDreamService && hasBindDreamServicePermission;
1189                                 hasOtherServices |= (!actImeService && !actWallpaperService &&
1190                                         !actAccessibilityService && !actPrintService &&
1191                                         !actHostApduService && !actOffHostApduService &&
1192                                         !actNotificationListenerService);
1193                             } else if (withinProvider) {
1194                                 hasDocumentsProvider |=
1195                                         actDocumentsProvider && hasRequiredSafAttributes;
1196                             }
1197                         }
1198                         withinIntentFilter = false;
1199                     }
1200                     continue;
1201                 }
1202                 if (code != ResXMLTree::START_TAG) {
1203                     continue;
1204                 }
1205                 depth++;
1206 
1207                 const char16_t* ctag16 = tree.getElementName(&len);
1208                 if (ctag16 == NULL) {
1209                     SourcePos(manifestFile, tree.getLineNumber()).error(
1210                             "ERROR: failed to get XML element name (bad string pool)");
1211                     goto bail;
1212                 }
1213                 String8 tag(ctag16);
1214                 //printf("Depth %d,  %s\n", depth, tag.string());
1215                 if (depth == 1) {
1216                     if (tag != "manifest") {
1217                         SourcePos(manifestFile, tree.getLineNumber()).error(
1218                                 "ERROR: manifest does not start with <manifest> tag");
1219                         goto bail;
1220                     }
1221                     pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
1222                     printf("package: name='%s' ",
1223                             ResTable::normalizeForOutput(pkg.string()).string());
1224                     int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR,
1225                             &error);
1226                     if (error != "") {
1227                         SourcePos(manifestFile, tree.getLineNumber()).error(
1228                                 "ERROR getting 'android:versionCode' attribute: %s",
1229                                 error.string());
1230                         goto bail;
1231                     }
1232                     if (versionCode > 0) {
1233                         printf("versionCode='%d' ", versionCode);
1234                     } else {
1235                         printf("versionCode='' ");
1236                     }
1237                     String8 versionName = AaptXml::getResolvedAttribute(res, tree,
1238                             VERSION_NAME_ATTR, &error);
1239                     if (error != "") {
1240                         SourcePos(manifestFile, tree.getLineNumber()).error(
1241                                 "ERROR getting 'android:versionName' attribute: %s",
1242                                 error.string());
1243                         goto bail;
1244                     }
1245                     printf("versionName='%s'",
1246                             ResTable::normalizeForOutput(versionName.string()).string());
1247 
1248                     String8 splitName = AaptXml::getAttribute(tree, NULL, "split");
1249                     if (!splitName.isEmpty()) {
1250                         printf(" split='%s'", ResTable::normalizeForOutput(
1251                                     splitName.string()).string());
1252                     }
1253 
1254                     String8 platformBuildVersionName = AaptXml::getAttribute(tree, NULL,
1255                             "platformBuildVersionName");
1256                     if (platformBuildVersionName != "") {
1257                         printf(" platformBuildVersionName='%s'", platformBuildVersionName.string());
1258                     }
1259 
1260                     String8 platformBuildVersionCode = AaptXml::getAttribute(tree, NULL,
1261                             "platformBuildVersionCode");
1262                     if (platformBuildVersionCode != "") {
1263                         printf(" platformBuildVersionCode='%s'", platformBuildVersionCode.string());
1264                     }
1265 
1266                     int32_t compileSdkVersion = AaptXml::getIntegerAttribute(tree,
1267                             COMPILE_SDK_VERSION_ATTR, &error);
1268                     if (error != "") {
1269                         SourcePos(manifestFile, tree.getLineNumber()).error(
1270                                 "ERROR getting 'android:compileSdkVersion' attribute: %s",
1271                                 error.string());
1272                         goto bail;
1273                     }
1274                     if (compileSdkVersion > 0) {
1275                         printf(" compileSdkVersion='%d'", compileSdkVersion);
1276                     }
1277 
1278                     String8 compileSdkVersionCodename = AaptXml::getResolvedAttribute(res, tree,
1279                             COMPILE_SDK_VERSION_CODENAME_ATTR, &error);
1280                     if (compileSdkVersionCodename != "") {
1281                         printf(" compileSdkVersionCodename='%s'", ResTable::normalizeForOutput(
1282                                 compileSdkVersionCodename.string()).string());
1283                     }
1284 
1285                     printf("\n");
1286 
1287                     int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
1288                             INSTALL_LOCATION_ATTR, &error);
1289                     if (error != "") {
1290                         SourcePos(manifestFile, tree.getLineNumber()).error(
1291                                 "ERROR getting 'android:installLocation' attribute: %s",
1292                                 error.string());
1293                         goto bail;
1294                     }
1295 
1296                     if (installLocation >= 0) {
1297                         printf("install-location:'");
1298                         switch (installLocation) {
1299                             case 0:
1300                                 printf("auto");
1301                                 break;
1302                             case 1:
1303                                 printf("internalOnly");
1304                                 break;
1305                             case 2:
1306                                 printf("preferExternal");
1307                                 break;
1308                             default:
1309                                 fprintf(stderr, "Invalid installLocation %d\n", installLocation);
1310                                 goto bail;
1311                         }
1312                         printf("'\n");
1313                     }
1314                 } else if (depth == 2) {
1315                     withinApplication = false;
1316                     if (tag == "application") {
1317                         withinApplication = true;
1318 
1319                         String8 label;
1320                         const size_t NL = locales.size();
1321                         for (size_t i=0; i<NL; i++) {
1322                             const char* localeStr =  locales[i].string();
1323                             assets.setConfiguration(config, localeStr != NULL ? localeStr : "");
1324                             String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1325                                     &error);
1326                             if (llabel != "") {
1327                                 if (localeStr == NULL || strlen(localeStr) == 0) {
1328                                     label = llabel;
1329                                     printf("application-label:'%s'\n",
1330                                             ResTable::normalizeForOutput(llabel.string()).string());
1331                                 } else {
1332                                     if (label == "") {
1333                                         label = llabel;
1334                                     }
1335                                     printf("application-label-%s:'%s'\n", localeStr,
1336                                            ResTable::normalizeForOutput(llabel.string()).string());
1337                                 }
1338                             }
1339                         }
1340 
1341                         ResTable_config tmpConfig = config;
1342                         const size_t ND = densities.size();
1343                         for (size_t i=0; i<ND; i++) {
1344                             tmpConfig.density = densities[i];
1345                             assets.setConfiguration(tmpConfig);
1346                             String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1347                                     &error);
1348                             if (icon != "") {
1349                                 printf("application-icon-%d:'%s'\n", densities[i],
1350                                         ResTable::normalizeForOutput(icon.string()).string());
1351                             }
1352                         }
1353                         assets.setConfiguration(config);
1354 
1355                         String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error);
1356                         if (error != "") {
1357                             SourcePos(manifestFile, tree.getLineNumber()).error(
1358                                     "ERROR getting 'android:icon' attribute: %s", error.string());
1359                             goto bail;
1360                         }
1361                         int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0,
1362                                 &error);
1363                         if (error != "") {
1364                             SourcePos(manifestFile, tree.getLineNumber()).error(
1365                                     "ERROR getting 'android:testOnly' attribute: %s",
1366                                     error.string());
1367                             goto bail;
1368                         }
1369 
1370                         String8 banner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1371                                                                        &error);
1372                         if (error != "") {
1373                             SourcePos(manifestFile, tree.getLineNumber()).error(
1374                                     "ERROR getting 'android:banner' attribute: %s", error.string());
1375                             goto bail;
1376                         }
1377                         printf("application: label='%s' ",
1378                                 ResTable::normalizeForOutput(label.string()).string());
1379                         printf("icon='%s'", ResTable::normalizeForOutput(icon.string()).string());
1380                         if (banner != "") {
1381                             printf(" banner='%s'",
1382                                    ResTable::normalizeForOutput(banner.string()).string());
1383                         }
1384                         printf("\n");
1385                         if (testOnly != 0) {
1386                             printf("testOnly='%d'\n", testOnly);
1387                         }
1388 
1389                         int32_t isGame = AaptXml::getResolvedIntegerAttribute(res, tree,
1390                                 ISGAME_ATTR, 0, &error);
1391                         if (error != "") {
1392                             SourcePos(manifestFile, tree.getLineNumber()).error(
1393                                     "ERROR getting 'android:isGame' attribute: %s", error.string());
1394                             goto bail;
1395                         }
1396                         if (isGame != 0) {
1397                             printf("application-isGame\n");
1398                         }
1399 
1400                         int32_t debuggable = AaptXml::getResolvedIntegerAttribute(res, tree,
1401                                 DEBUGGABLE_ATTR, 0, &error);
1402                         if (error != "") {
1403                             SourcePos(manifestFile, tree.getLineNumber()).error(
1404                                     "ERROR getting 'android:debuggable' attribute: %s",
1405                                     error.string());
1406                             goto bail;
1407                         }
1408                         if (debuggable != 0) {
1409                             printf("application-debuggable\n");
1410                         }
1411 
1412                         // We must search by name because the multiArch flag hasn't been API
1413                         // frozen yet.
1414                         int32_t multiArchIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
1415                                 "multiArch");
1416                         if (multiArchIndex >= 0) {
1417                             Res_value value;
1418                             if (tree.getAttributeValue(multiArchIndex, &value) != NO_ERROR) {
1419                                 if (value.dataType >= Res_value::TYPE_FIRST_INT &&
1420                                         value.dataType <= Res_value::TYPE_LAST_INT) {
1421                                     hasMultiArch = value.data;
1422                                 }
1423                             }
1424                         }
1425                     } else if (tag == "uses-sdk") {
1426                         int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR,
1427                                                                     &error);
1428                         if (error != "") {
1429                             error = "";
1430                             String8 name = AaptXml::getResolvedAttribute(res, tree,
1431                                     MIN_SDK_VERSION_ATTR, &error);
1432                             if (error != "") {
1433                                 SourcePos(manifestFile, tree.getLineNumber()).error(
1434                                         "ERROR getting 'android:minSdkVersion' attribute: %s",
1435                                         error.string());
1436                                 goto bail;
1437                             }
1438                             if (name == "Donut") targetSdk = 4;
1439                             printf("sdkVersion:'%s'\n",
1440                                     ResTable::normalizeForOutput(name.string()).string());
1441                         } else if (code != -1) {
1442                             targetSdk = code;
1443                             printf("sdkVersion:'%d'\n", code);
1444                         }
1445                         code = AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR);
1446                         if (code != -1) {
1447                             printf("maxSdkVersion:'%d'\n", code);
1448                         }
1449                         code = AaptXml::getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
1450                         if (error != "") {
1451                             error = "";
1452                             String8 name = AaptXml::getResolvedAttribute(res, tree,
1453                                     TARGET_SDK_VERSION_ATTR, &error);
1454                             if (error != "") {
1455                                 SourcePos(manifestFile, tree.getLineNumber()).error(
1456                                         "ERROR getting 'android:targetSdkVersion' attribute: %s",
1457                                         error.string());
1458                                 goto bail;
1459                             }
1460                             if (name == "Donut" && targetSdk < 4) targetSdk = 4;
1461                             printf("targetSdkVersion:'%s'\n",
1462                                     ResTable::normalizeForOutput(name.string()).string());
1463                         } else if (code != -1) {
1464                             if (targetSdk < code) {
1465                                 targetSdk = code;
1466                             }
1467                             printf("targetSdkVersion:'%d'\n", code);
1468                         }
1469                     } else if (tag == "uses-configuration") {
1470                         int32_t reqTouchScreen = AaptXml::getIntegerAttribute(tree,
1471                                 REQ_TOUCH_SCREEN_ATTR, 0);
1472                         int32_t reqKeyboardType = AaptXml::getIntegerAttribute(tree,
1473                                 REQ_KEYBOARD_TYPE_ATTR, 0);
1474                         int32_t reqHardKeyboard = AaptXml::getIntegerAttribute(tree,
1475                                 REQ_HARD_KEYBOARD_ATTR, 0);
1476                         int32_t reqNavigation = AaptXml::getIntegerAttribute(tree,
1477                                 REQ_NAVIGATION_ATTR, 0);
1478                         int32_t reqFiveWayNav = AaptXml::getIntegerAttribute(tree,
1479                                 REQ_FIVE_WAY_NAV_ATTR, 0);
1480                         printf("uses-configuration:");
1481                         if (reqTouchScreen != 0) {
1482                             printf(" reqTouchScreen='%d'", reqTouchScreen);
1483                         }
1484                         if (reqKeyboardType != 0) {
1485                             printf(" reqKeyboardType='%d'", reqKeyboardType);
1486                         }
1487                         if (reqHardKeyboard != 0) {
1488                             printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1489                         }
1490                         if (reqNavigation != 0) {
1491                             printf(" reqNavigation='%d'", reqNavigation);
1492                         }
1493                         if (reqFiveWayNav != 0) {
1494                             printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1495                         }
1496                         printf("\n");
1497                     } else if (tag == "supports-input") {
1498                         withinSupportsInput = true;
1499                     } else if (tag == "supports-screens") {
1500                         smallScreen = AaptXml::getIntegerAttribute(tree,
1501                                 SMALL_SCREEN_ATTR, 1);
1502                         normalScreen = AaptXml::getIntegerAttribute(tree,
1503                                 NORMAL_SCREEN_ATTR, 1);
1504                         largeScreen = AaptXml::getIntegerAttribute(tree,
1505                                 LARGE_SCREEN_ATTR, 1);
1506                         xlargeScreen = AaptXml::getIntegerAttribute(tree,
1507                                 XLARGE_SCREEN_ATTR, 1);
1508                         anyDensity = AaptXml::getIntegerAttribute(tree,
1509                                 ANY_DENSITY_ATTR, 1);
1510                         requiresSmallestWidthDp = AaptXml::getIntegerAttribute(tree,
1511                                 REQUIRES_SMALLEST_WIDTH_DP_ATTR, 0);
1512                         compatibleWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1513                                 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, 0);
1514                         largestWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1515                                 LARGEST_WIDTH_LIMIT_DP_ATTR, 0);
1516                     } else if (tag == "feature-group") {
1517                         withinFeatureGroup = true;
1518                         FeatureGroup group;
1519                         group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error);
1520                         if (error != "") {
1521                             SourcePos(manifestFile, tree.getLineNumber()).error(
1522                                     "ERROR getting 'android:label' attribute: %s", error.string());
1523                             goto bail;
1524                         }
1525                         featureGroups.add(group);
1526 
1527                     } else if (tag == "uses-feature") {
1528                         String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1529                         if (name != "" && error == "") {
1530                             const char* androidSchema =
1531                                     "http://schemas.android.com/apk/res/android";
1532 
1533                             int32_t req = AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1,
1534                                                                        &error);
1535                             if (error != "") {
1536                                 SourcePos(manifestFile, tree.getLineNumber()).error(
1537                                         "failed to read attribute 'android:required': %s",
1538                                         error.string());
1539                                 goto bail;
1540                             }
1541 
1542                             int32_t version = AaptXml::getIntegerAttribute(tree, androidSchema,
1543                                                                            "version", 0, &error);
1544                             if (error != "") {
1545                                 SourcePos(manifestFile, tree.getLineNumber()).error(
1546                                         "failed to read attribute 'android:version': %s",
1547                                         error.string());
1548                                 goto bail;
1549                             }
1550 
1551                             commonFeatures.features.add(name, Feature(req != 0, version));
1552                             if (req) {
1553                                 addParentFeatures(&commonFeatures, name);
1554                             }
1555                         } else {
1556                             int vers = AaptXml::getIntegerAttribute(tree,
1557                                     GL_ES_VERSION_ATTR, &error);
1558                             if (error == "") {
1559                                 if (vers > commonFeatures.openGLESVersion) {
1560                                     commonFeatures.openGLESVersion = vers;
1561                                 }
1562                             }
1563                         }
1564                     } else if (tag == "uses-permission") {
1565                         String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1566                         if (error != "") {
1567                             SourcePos(manifestFile, tree.getLineNumber()).error(
1568                                     "ERROR getting 'android:name' attribute: %s", error.string());
1569                             goto bail;
1570                         }
1571 
1572                         if (name == "") {
1573                             SourcePos(manifestFile, tree.getLineNumber()).error(
1574                                     "ERROR: missing 'android:name' for uses-permission");
1575                             goto bail;
1576                         }
1577 
1578                         addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, false);
1579 
1580                         const int32_t maxSdkVersion =
1581                                 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, -1);
1582                         const String8 requiredFeature = AaptXml::getAttribute(tree,
1583                                 REQUIRED_FEATURE_ATTR, &error);
1584                         const String8 requiredNotFeature = AaptXml::getAttribute(tree,
1585                                 REQUIRED_NOT_FEATURE_ATTR, &error);
1586 
1587                         if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1588                             hasWriteExternalStoragePermission = true;
1589                             writeExternalStoragePermissionMaxSdkVersion = maxSdkVersion;
1590                         } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1591                             hasReadExternalStoragePermission = true;
1592                         } else if (name == "android.permission.READ_PHONE_STATE") {
1593                             hasReadPhoneStatePermission = true;
1594                         } else if (name == "android.permission.READ_CONTACTS") {
1595                             hasReadContactsPermission = true;
1596                         } else if (name == "android.permission.WRITE_CONTACTS") {
1597                             hasWriteContactsPermission = true;
1598                         } else if (name == "android.permission.READ_CALL_LOG") {
1599                             hasReadCallLogPermission = true;
1600                         } else if (name == "android.permission.WRITE_CALL_LOG") {
1601                             hasWriteCallLogPermission = true;
1602                         }
1603 
1604                         printUsesPermission(name,
1605                                 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
1606                                 maxSdkVersion, requiredFeature, requiredNotFeature);
1607 
1608                     } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
1609                         String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1610                         if (error != "") {
1611                             SourcePos(manifestFile, tree.getLineNumber()).error(
1612                                     "ERROR getting 'android:name' attribute: %s", error.string());
1613                             goto bail;
1614                         }
1615 
1616                         if (name == "") {
1617                             SourcePos(manifestFile, tree.getLineNumber()).error(
1618                                     "ERROR: missing 'android:name' for uses-permission-sdk-23");
1619                             goto bail;
1620                         }
1621 
1622                         addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, true);
1623 
1624                         printUsesPermissionSdk23(
1625                                 name, AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
1626 
1627                     } else if (tag == "uses-package") {
1628                         String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1629                         if (name != "" && error == "") {
1630                             printf("uses-package:'%s'\n",
1631                                     ResTable::normalizeForOutput(name.string()).string());
1632                         } else {
1633                             SourcePos(manifestFile, tree.getLineNumber()).error(
1634                                     "ERROR getting 'android:name' attribute: %s", error.string());
1635                             goto bail;
1636                         }
1637                     } else if (tag == "original-package") {
1638                         String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1639                         if (name != "" && error == "") {
1640                             printf("original-package:'%s'\n",
1641                                     ResTable::normalizeForOutput(name.string()).string());
1642                         } else {
1643                             SourcePos(manifestFile, tree.getLineNumber()).error(
1644                                     "ERROR getting 'android:name' attribute: %s", error.string());
1645                             goto bail;
1646                         }
1647                     } else if (tag == "supports-gl-texture") {
1648                         String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1649                         if (name != "" && error == "") {
1650                             printf("supports-gl-texture:'%s'\n",
1651                                     ResTable::normalizeForOutput(name.string()).string());
1652                         } else {
1653                             SourcePos(manifestFile, tree.getLineNumber()).error(
1654                                     "ERROR getting 'android:name' attribute: %s", error.string());
1655                             goto bail;
1656                         }
1657                     } else if (tag == "compatible-screens") {
1658                         printCompatibleScreens(tree, &error);
1659                         if (error != "") {
1660                             SourcePos(manifestFile, tree.getLineNumber()).error(
1661                                     "ERROR getting compatible screens: %s", error.string());
1662                             goto bail;
1663                         }
1664                         depth--;
1665                     } else if (tag == "package-verifier") {
1666                         String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1667                         if (name != "" && error == "") {
1668                             String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR,
1669                                                                       &error);
1670                             if (publicKey != "" && error == "") {
1671                                 printf("package-verifier: name='%s' publicKey='%s'\n",
1672                                         ResTable::normalizeForOutput(name.string()).string(),
1673                                         ResTable::normalizeForOutput(publicKey.string()).string());
1674                             }
1675                         }
1676                     }
1677                 } else if (depth == 3) {
1678                     withinActivity = false;
1679                     withinReceiver = false;
1680                     withinService = false;
1681                     withinProvider = false;
1682                     hasIntentFilter = false;
1683                     hasMetaHostPaymentCategory = false;
1684                     hasMetaOffHostPaymentCategory = false;
1685                     hasBindDeviceAdminPermission = false;
1686                     hasBindInputMethodPermission = false;
1687                     hasBindAccessibilityServicePermission = false;
1688                     hasBindPrintServicePermission = false;
1689                     hasBindNfcServicePermission = false;
1690                     hasRequiredSafAttributes = false;
1691                     hasBindNotificationListenerServicePermission = false;
1692                     hasBindDreamServicePermission = false;
1693                     if (withinApplication) {
1694                         if(tag == "activity") {
1695                             withinActivity = true;
1696                             activityName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1697                             if (error != "") {
1698                                 SourcePos(manifestFile, tree.getLineNumber()).error(
1699                                         "ERROR getting 'android:name' attribute: %s",
1700                                         error.string());
1701                                 goto bail;
1702                             }
1703 
1704                             activityLabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1705                                     &error);
1706                             if (error != "") {
1707                                 SourcePos(manifestFile, tree.getLineNumber()).error(
1708                                         "ERROR getting 'android:label' attribute: %s",
1709                                         error.string());
1710                                 goto bail;
1711                             }
1712 
1713                             activityIcon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1714                                     &error);
1715                             if (error != "") {
1716                                 SourcePos(manifestFile, tree.getLineNumber()).error(
1717                                         "ERROR getting 'android:icon' attribute: %s",
1718                                         error.string());
1719                                 goto bail;
1720                             }
1721 
1722                             activityBanner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1723                                     &error);
1724                             if (error != "") {
1725                                 SourcePos(manifestFile, tree.getLineNumber()).error(
1726                                         "ERROR getting 'android:banner' attribute: %s",
1727                                         error.string());
1728                                 goto bail;
1729                             }
1730 
1731                             int32_t orien = AaptXml::getResolvedIntegerAttribute(res, tree,
1732                                     SCREEN_ORIENTATION_ATTR, &error);
1733                             if (error == "") {
1734                                 if (orien == 0 || orien == 6 || orien == 8) {
1735                                     // Requests landscape, sensorLandscape, or reverseLandscape.
1736                                     addImpliedFeature(
1737                                             &impliedFeatures, "android.hardware.screen.landscape",
1738                                             String8("one or more activities have specified a "
1739                                                     "landscape orientation"),
1740                                             false);
1741                                 } else if (orien == 1 || orien == 7 || orien == 9) {
1742                                     // Requests portrait, sensorPortrait, or reversePortrait.
1743                                     addImpliedFeature(
1744                                             &impliedFeatures, "android.hardware.screen.portrait",
1745                                             String8("one or more activities have specified a "
1746                                                     "portrait orientation"),
1747                                             false);
1748                                 }
1749                             }
1750                         } else if (tag == "uses-library") {
1751                             String8 libraryName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1752                             if (error != "") {
1753                                 SourcePos(manifestFile, tree.getLineNumber()).error(
1754                                         "ERROR getting 'android:name' attribute for uses-library"
1755                                         " %s", error.string());
1756                                 goto bail;
1757                             }
1758                             int req = AaptXml::getIntegerAttribute(tree,
1759                                     REQUIRED_ATTR, 1);
1760                             printf("uses-library%s:'%s'\n",
1761                                     req ? "" : "-not-required", ResTable::normalizeForOutput(
1762                                             libraryName.string()).string());
1763                         } else if (tag == "receiver") {
1764                             withinReceiver = true;
1765                             receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1766 
1767                             if (error != "") {
1768                                 SourcePos(manifestFile, tree.getLineNumber()).error(
1769                                         "ERROR getting 'android:name' attribute for receiver:"
1770                                         " %s", error.string());
1771                                 goto bail;
1772                             }
1773 
1774                             String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1775                                     &error);
1776                             if (error == "") {
1777                                 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1778                                     hasBindDeviceAdminPermission = true;
1779                                 }
1780                             } else {
1781                                 SourcePos(manifestFile, tree.getLineNumber()).error(
1782                                         "ERROR getting 'android:permission' attribute for"
1783                                         " receiver '%s': %s",
1784                                         receiverName.string(), error.string());
1785                             }
1786                         } else if (tag == "service") {
1787                             withinService = true;
1788                             serviceName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1789 
1790                             if (error != "") {
1791                                 SourcePos(manifestFile, tree.getLineNumber()).error(
1792                                         "ERROR getting 'android:name' attribute for "
1793                                         "service:%s", error.string());
1794                                 goto bail;
1795                             }
1796 
1797                             String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1798                                     &error);
1799                             if (error == "") {
1800                                 if (permission == "android.permission.BIND_INPUT_METHOD") {
1801                                     hasBindInputMethodPermission = true;
1802                                 } else if (permission ==
1803                                         "android.permission.BIND_ACCESSIBILITY_SERVICE") {
1804                                     hasBindAccessibilityServicePermission = true;
1805                                 } else if (permission ==
1806                                         "android.permission.BIND_PRINT_SERVICE") {
1807                                     hasBindPrintServicePermission = true;
1808                                 } else if (permission ==
1809                                         "android.permission.BIND_NFC_SERVICE") {
1810                                     hasBindNfcServicePermission = true;
1811                                 } else if (permission ==
1812                                         "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
1813                                     hasBindNotificationListenerServicePermission = true;
1814                                 } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
1815                                     hasBindDreamServicePermission = true;
1816                                 }
1817                             } else {
1818                                 SourcePos(manifestFile, tree.getLineNumber()).error(
1819                                         "ERROR getting 'android:permission' attribute for "
1820                                         "service '%s': %s", serviceName.string(), error.string());
1821                             }
1822                         } else if (tag == "provider") {
1823                             withinProvider = true;
1824 
1825                             bool exported = AaptXml::getResolvedIntegerAttribute(res, tree,
1826                                     EXPORTED_ATTR, &error);
1827                             if (error != "") {
1828                                 SourcePos(manifestFile, tree.getLineNumber()).error(
1829                                         "ERROR getting 'android:exported' attribute for provider:"
1830                                         " %s", error.string());
1831                                 goto bail;
1832                             }
1833 
1834                             bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute(
1835                                     res, tree, GRANT_URI_PERMISSIONS_ATTR, &error);
1836                             if (error != "") {
1837                                 SourcePos(manifestFile, tree.getLineNumber()).error(
1838                                         "ERROR getting 'android:grantUriPermissions' attribute for "
1839                                         "provider: %s", error.string());
1840                                 goto bail;
1841                             }
1842 
1843                             String8 permission = AaptXml::getResolvedAttribute(res, tree,
1844                                     PERMISSION_ATTR, &error);
1845                             if (error != "") {
1846                                 SourcePos(manifestFile, tree.getLineNumber()).error(
1847                                         "ERROR getting 'android:permission' attribute for "
1848                                         "provider: %s", error.string());
1849                                 goto bail;
1850                             }
1851 
1852                             hasRequiredSafAttributes |= exported && grantUriPermissions &&
1853                                 permission == "android.permission.MANAGE_DOCUMENTS";
1854 
1855                         } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
1856                             String8 metaDataName = AaptXml::getResolvedAttribute(res, tree,
1857                                     NAME_ATTR, &error);
1858                             if (error != "") {
1859                                 SourcePos(manifestFile, tree.getLineNumber()).error(
1860                                         "ERROR getting 'android:name' attribute for "
1861                                         "meta-data: %s", error.string());
1862                                 goto bail;
1863                             }
1864                             printf("meta-data: name='%s' ",
1865                                     ResTable::normalizeForOutput(metaDataName.string()).string());
1866                             printResolvedResourceAttribute(res, tree, VALUE_ATTR, String8("value"),
1867                                     &error);
1868                             if (error != "") {
1869                                 // Try looking for a RESOURCE_ATTR
1870                                 error = "";
1871                                 printResolvedResourceAttribute(res, tree, RESOURCE_ATTR,
1872                                         String8("resource"), &error);
1873                                 if (error != "") {
1874                                     SourcePos(manifestFile, tree.getLineNumber()).error(
1875                                             "ERROR getting 'android:value' or "
1876                                             "'android:resource' attribute for "
1877                                             "meta-data: %s", error.string());
1878                                     goto bail;
1879                                 }
1880                             }
1881                             printf("\n");
1882                         } else if (withinSupportsInput && tag == "input-type") {
1883                             String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1884                             if (name != "" && error == "") {
1885                                 supportedInput.add(name);
1886                             } else {
1887                                 SourcePos(manifestFile, tree.getLineNumber()).error(
1888                                         "ERROR getting 'android:name' attribute: %s",
1889                                         error.string());
1890                                 goto bail;
1891                             }
1892                         }
1893                     } else if (withinFeatureGroup && tag == "uses-feature") {
1894                         const String8 androidSchema("http://schemas.android.com/apk/res/android");
1895                         FeatureGroup& top = featureGroups.editTop();
1896 
1897                         String8 name = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error);
1898                         if (name != "" && error == "") {
1899                             Feature feature(true);
1900 
1901                             int32_t featureVers = AaptXml::getIntegerAttribute(
1902                                     tree, androidSchema.string(), "version", 0, &error);
1903                             if (error == "") {
1904                                 feature.version = featureVers;
1905                             } else {
1906                                 SourcePos(manifestFile, tree.getLineNumber()).error(
1907                                         "failed to read attribute 'android:version': %s",
1908                                         error.string());
1909                                 goto bail;
1910                             }
1911 
1912                             top.features.add(name, feature);
1913                             addParentFeatures(&top, name);
1914 
1915                         } else {
1916                             int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR,
1917                                     &error);
1918                             if (error == "") {
1919                                 if (vers > top.openGLESVersion) {
1920                                     top.openGLESVersion = vers;
1921                                 }
1922                             }
1923                         }
1924                     }
1925                 } else if (depth == 4) {
1926                     if (tag == "intent-filter") {
1927                         hasIntentFilter = true;
1928                         withinIntentFilter = true;
1929                         actMainActivity = false;
1930                         actWidgetReceivers = false;
1931                         actImeService = false;
1932                         actWallpaperService = false;
1933                         actAccessibilityService = false;
1934                         actPrintService = false;
1935                         actDeviceAdminEnabled = false;
1936                         actHostApduService = false;
1937                         actOffHostApduService = false;
1938                         actDocumentsProvider = false;
1939                         actNotificationListenerService = false;
1940                         actDreamService = false;
1941                         actCamera = false;
1942                         actCameraSecure = false;
1943                         catLauncher = false;
1944                     } else if (withinService && tag == "meta-data") {
1945                         String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1946                         if (error != "") {
1947                             SourcePos(manifestFile, tree.getLineNumber()).error(
1948                                     "ERROR getting 'android:name' attribute for "
1949                                     "meta-data tag in service '%s': %s", serviceName.string(),
1950                                     error.string());
1951                             goto bail;
1952                         }
1953 
1954                         if (name == "android.nfc.cardemulation.host_apdu_service" ||
1955                                 name == "android.nfc.cardemulation.off_host_apdu_service") {
1956                             bool offHost = true;
1957                             if (name == "android.nfc.cardemulation.host_apdu_service") {
1958                                 offHost = false;
1959                             }
1960 
1961                             String8 xmlPath = AaptXml::getResolvedAttribute(res, tree,
1962                                     RESOURCE_ATTR, &error);
1963                             if (error != "") {
1964                                 SourcePos(manifestFile, tree.getLineNumber()).error(
1965                                         "ERROR getting 'android:resource' attribute for "
1966                                         "meta-data tag in service '%s': %s",
1967                                         serviceName.string(), error.string());
1968                                 goto bail;
1969                             }
1970 
1971                             Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1972                                     offHost, &error);
1973                             if (error != "") {
1974                                 SourcePos(manifestFile, tree.getLineNumber()).error(
1975                                         "ERROR getting AID category for service '%s'",
1976                                         serviceName.string());
1977                                 goto bail;
1978                             }
1979 
1980                             const size_t catLen = categories.size();
1981                             for (size_t i = 0; i < catLen; i++) {
1982                                 bool paymentCategory = (categories[i] == "payment");
1983                                 if (offHost) {
1984                                     hasMetaOffHostPaymentCategory |= paymentCategory;
1985                                 } else {
1986                                     hasMetaHostPaymentCategory |= paymentCategory;
1987                                 }
1988                             }
1989                         }
1990                     }
1991                 } else if ((depth == 5) && withinIntentFilter) {
1992                     String8 action;
1993                     if (tag == "action") {
1994                         action = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1995                         if (error != "") {
1996                             SourcePos(manifestFile, tree.getLineNumber()).error(
1997                                     "ERROR getting 'android:name' attribute: %s", error.string());
1998                             goto bail;
1999                         }
2000 
2001                         if (withinActivity) {
2002                             if (action == "android.intent.action.MAIN") {
2003                                 isMainActivity = true;
2004                                 actMainActivity = true;
2005                             } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
2006                                     action == "android.media.action.VIDEO_CAMERA") {
2007                                 actCamera = true;
2008                             } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
2009                                 actCameraSecure = true;
2010                             }
2011                         } else if (withinReceiver) {
2012                             if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
2013                                 actWidgetReceivers = true;
2014                             } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
2015                                 actDeviceAdminEnabled = true;
2016                             }
2017                         } else if (withinService) {
2018                             if (action == "android.view.InputMethod") {
2019                                 actImeService = true;
2020                             } else if (action == "android.service.wallpaper.WallpaperService") {
2021                                 actWallpaperService = true;
2022                             } else if (action ==
2023                                     "android.accessibilityservice.AccessibilityService") {
2024                                 actAccessibilityService = true;
2025                             } else if (action =="android.printservice.PrintService") {
2026                                 actPrintService = true;
2027                             } else if (action ==
2028                                     "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
2029                                 actHostApduService = true;
2030                             } else if (action ==
2031                                     "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
2032                                 actOffHostApduService = true;
2033                             } else if (action ==
2034                                     "android.service.notification.NotificationListenerService") {
2035                                 actNotificationListenerService = true;
2036                             } else if (action == "android.service.dreams.DreamService") {
2037                                 actDreamService = true;
2038                             }
2039                         } else if (withinProvider) {
2040                             if (action == "android.content.action.DOCUMENTS_PROVIDER") {
2041                                 actDocumentsProvider = true;
2042                             }
2043                         }
2044                         if (action == "android.intent.action.SEARCH") {
2045                             isSearchable = true;
2046                         }
2047                     }
2048 
2049                     if (tag == "category") {
2050                         String8 category = AaptXml::getAttribute(tree, NAME_ATTR, &error);
2051                         if (error != "") {
2052                             SourcePos(manifestFile, tree.getLineNumber()).error(
2053                                     "ERROR getting 'name' attribute: %s", error.string());
2054                             goto bail;
2055                         }
2056                         if (withinActivity) {
2057                             if (category == "android.intent.category.LAUNCHER") {
2058                                 isLauncherActivity = true;
2059                             } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
2060                                 isLeanbackLauncherActivity = true;
2061                             } else if (category == "android.intent.category.HOME") {
2062                                 catLauncher = true;
2063                             }
2064                         }
2065                     }
2066                 }
2067             }
2068 
2069             // Pre-1.6 implicitly granted permission compatibility logic
2070             if (targetSdk < 4) {
2071                 if (!hasWriteExternalStoragePermission) {
2072                     printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
2073                     printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
2074                             String8("targetSdkVersion < 4"));
2075                     hasWriteExternalStoragePermission = true;
2076                 }
2077                 if (!hasReadPhoneStatePermission) {
2078                     printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
2079                     printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
2080                             String8("targetSdkVersion < 4"));
2081                 }
2082             }
2083 
2084             // If the application has requested WRITE_EXTERNAL_STORAGE, we will
2085             // force them to always take READ_EXTERNAL_STORAGE as well.  We always
2086             // do this (regardless of target API version) because we can't have
2087             // an app with write permission but not read permission.
2088             if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
2089                 printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
2090                         false /* optional */, writeExternalStoragePermissionMaxSdkVersion);
2091                 printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
2092                         String8("requested WRITE_EXTERNAL_STORAGE"),
2093                         writeExternalStoragePermissionMaxSdkVersion);
2094             }
2095 
2096             // Pre-JellyBean call log permission compatibility.
2097             if (targetSdk < 16) {
2098                 if (!hasReadCallLogPermission && hasReadContactsPermission) {
2099                     printUsesPermission(String8("android.permission.READ_CALL_LOG"));
2100                     printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
2101                             String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
2102                 }
2103                 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
2104                     printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
2105                     printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
2106                             String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
2107                 }
2108             }
2109 
2110             // If the app hasn't declared the touchscreen as a feature requirement (either
2111             // directly or implied, required or not), then the faketouch feature is implied.
2112             if (!hasFeature("android.hardware.touchscreen", commonFeatures, impliedFeatures)) {
2113                 addImpliedFeature(&impliedFeatures, "android.hardware.faketouch",
2114                                   String8("default feature for all apps"), false);
2115             }
2116 
2117             const size_t numFeatureGroups = featureGroups.size();
2118             if (numFeatureGroups == 0) {
2119                 // If no <feature-group> tags were defined, apply auto-implied features.
2120                 printDefaultFeatureGroup(commonFeatures, impliedFeatures);
2121 
2122             } else {
2123                 // <feature-group> tags are defined, so we ignore implied features and
2124                 for (size_t i = 0; i < numFeatureGroups; i++) {
2125                     FeatureGroup& grp = featureGroups.editItemAt(i);
2126 
2127                     if (commonFeatures.openGLESVersion > grp.openGLESVersion) {
2128                         grp.openGLESVersion = commonFeatures.openGLESVersion;
2129                     }
2130 
2131                     // Merge the features defined in the top level (not inside a <feature-group>)
2132                     // with this feature group.
2133                     const size_t numCommonFeatures = commonFeatures.features.size();
2134                     for (size_t j = 0; j < numCommonFeatures; j++) {
2135                         if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
2136                             grp.features.add(commonFeatures.features.keyAt(j),
2137                                     commonFeatures.features[j]);
2138                         }
2139                     }
2140 
2141                     if (!grp.features.isEmpty()) {
2142                         printFeatureGroup(grp);
2143                     }
2144                 }
2145             }
2146 
2147 
2148             if (hasWidgetReceivers) {
2149                 printComponentPresence("app-widget");
2150             }
2151             if (hasDeviceAdminReceiver) {
2152                 printComponentPresence("device-admin");
2153             }
2154             if (hasImeService) {
2155                 printComponentPresence("ime");
2156             }
2157             if (hasWallpaperService) {
2158                 printComponentPresence("wallpaper");
2159             }
2160             if (hasAccessibilityService) {
2161                 printComponentPresence("accessibility");
2162             }
2163             if (hasPrintService) {
2164                 printComponentPresence("print-service");
2165             }
2166             if (hasPaymentService) {
2167                 printComponentPresence("payment");
2168             }
2169             if (isSearchable) {
2170                 printComponentPresence("search");
2171             }
2172             if (hasDocumentsProvider) {
2173                 printComponentPresence("document-provider");
2174             }
2175             if (hasLauncher) {
2176                 printComponentPresence("launcher");
2177             }
2178             if (hasNotificationListenerService) {
2179                 printComponentPresence("notification-listener");
2180             }
2181             if (hasDreamService) {
2182                 printComponentPresence("dream");
2183             }
2184             if (hasCameraActivity) {
2185                 printComponentPresence("camera");
2186             }
2187             if (hasCameraSecureActivity) {
2188                 printComponentPresence("camera-secure");
2189             }
2190 
2191             if (hasMainActivity) {
2192                 printf("main\n");
2193             }
2194             if (hasOtherActivities) {
2195                 printf("other-activities\n");
2196             }
2197              if (hasOtherReceivers) {
2198                 printf("other-receivers\n");
2199             }
2200             if (hasOtherServices) {
2201                 printf("other-services\n");
2202             }
2203 
2204             // For modern apps, if screen size buckets haven't been specified
2205             // but the new width ranges have, then infer the buckets from them.
2206             if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
2207                     && requiresSmallestWidthDp > 0) {
2208                 int compatWidth = compatibleWidthLimitDp;
2209                 if (compatWidth <= 0) {
2210                     compatWidth = requiresSmallestWidthDp;
2211                 }
2212                 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
2213                     smallScreen = -1;
2214                 } else {
2215                     smallScreen = 0;
2216                 }
2217                 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
2218                     normalScreen = -1;
2219                 } else {
2220                     normalScreen = 0;
2221                 }
2222                 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
2223                     largeScreen = -1;
2224                 } else {
2225                     largeScreen = 0;
2226                 }
2227                 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
2228                     xlargeScreen = -1;
2229                 } else {
2230                     xlargeScreen = 0;
2231                 }
2232             }
2233 
2234             // Determine default values for any unspecified screen sizes,
2235             // based on the target SDK of the package.  As of 4 (donut)
2236             // the screen size support was introduced, so all default to
2237             // enabled.
2238             if (smallScreen > 0) {
2239                 smallScreen = targetSdk >= 4 ? -1 : 0;
2240             }
2241             if (normalScreen > 0) {
2242                 normalScreen = -1;
2243             }
2244             if (largeScreen > 0) {
2245                 largeScreen = targetSdk >= 4 ? -1 : 0;
2246             }
2247             if (xlargeScreen > 0) {
2248                 // Introduced in Gingerbread.
2249                 xlargeScreen = targetSdk >= 9 ? -1 : 0;
2250             }
2251             if (anyDensity > 0) {
2252                 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
2253                         || compatibleWidthLimitDp > 0) ? -1 : 0;
2254             }
2255             printf("supports-screens:");
2256             if (smallScreen != 0) {
2257                 printf(" 'small'");
2258             }
2259             if (normalScreen != 0) {
2260                 printf(" 'normal'");
2261             }
2262             if (largeScreen != 0) {
2263                 printf(" 'large'");
2264             }
2265             if (xlargeScreen != 0) {
2266                 printf(" 'xlarge'");
2267             }
2268             printf("\n");
2269             printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
2270             if (requiresSmallestWidthDp > 0) {
2271                 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
2272             }
2273             if (compatibleWidthLimitDp > 0) {
2274                 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
2275             }
2276             if (largestWidthLimitDp > 0) {
2277                 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
2278             }
2279 
2280             printf("locales:");
2281             const size_t NL = locales.size();
2282             for (size_t i=0; i<NL; i++) {
2283                 const char* localeStr =  locales[i].string();
2284                 if (localeStr == NULL || strlen(localeStr) == 0) {
2285                     localeStr = "--_--";
2286                 }
2287                 printf(" '%s'", localeStr);
2288             }
2289             printf("\n");
2290 
2291             printf("densities:");
2292             const size_t ND = densities.size();
2293             for (size_t i=0; i<ND; i++) {
2294                 printf(" '%d'", densities[i]);
2295             }
2296             printf("\n");
2297 
2298             AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
2299             if (dir != NULL) {
2300                 if (dir->getFileCount() > 0) {
2301                     SortedVector<String8> architectures;
2302                     for (size_t i=0; i<dir->getFileCount(); i++) {
2303                         architectures.add(ResTable::normalizeForOutput(
2304                                 dir->getFileName(i).string()));
2305                     }
2306 
2307                     bool outputAltNativeCode = false;
2308                     // A multiArch package is one that contains 64-bit and
2309                     // 32-bit versions of native code and expects 3rd-party
2310                     // apps to load these native code libraries. Since most
2311                     // 64-bit systems also support 32-bit apps, the apps
2312                     // loading this multiArch package's code may be either
2313                     // 32-bit or 64-bit.
2314                     if (hasMultiArch) {
2315                         // If this is a multiArch package, report the 64-bit
2316                         // version only. Then as a separate entry, report the
2317                         // rest.
2318                         //
2319                         // If we report the 32-bit architecture, this APK will
2320                         // be installed on a 32-bit device, causing a large waste
2321                         // of bandwidth and disk space. This assumes that
2322                         // the developer of the multiArch package has also
2323                         // made a version that is 32-bit only.
2324                         String8 intel64("x86_64");
2325                         String8 arm64("arm64-v8a");
2326                         ssize_t index = architectures.indexOf(intel64);
2327                         if (index < 0) {
2328                             index = architectures.indexOf(arm64);
2329                         }
2330 
2331                         if (index >= 0) {
2332                             printf("native-code: '%s'\n", architectures[index].string());
2333                             architectures.removeAt(index);
2334                             outputAltNativeCode = true;
2335                         }
2336                     }
2337 
2338                     const size_t archCount = architectures.size();
2339                     if (archCount > 0) {
2340                         if (outputAltNativeCode) {
2341                             printf("alt-");
2342                         }
2343                         printf("native-code:");
2344                         for (size_t i = 0; i < archCount; i++) {
2345                             printf(" '%s'", architectures[i].string());
2346                         }
2347                         printf("\n");
2348                     }
2349                 }
2350                 delete dir;
2351             }
2352         } else if (strcmp("badger", option) == 0) {
2353             printf("%s", CONSOLE_DATA);
2354         } else if (strcmp("configurations", option) == 0) {
2355             Vector<ResTable_config> configs;
2356             res.getConfigurations(&configs);
2357             const size_t N = configs.size();
2358             for (size_t i=0; i<N; i++) {
2359                 printf("%s\n", configs[i].toString().string());
2360             }
2361         } else {
2362             fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2363             goto bail;
2364         }
2365     }
2366 
2367     result = NO_ERROR;
2368 
2369 bail:
2370     if (SourcePos::hasErrors()) {
2371         SourcePos::printErrors(stderr);
2372     }
2373 
2374     if (asset) {
2375         delete asset;
2376     }
2377     return (result != NO_ERROR);
2378 }
2379 
2380 
2381 /*
2382  * Handle the "add" command, which wants to add files to a new or
2383  * pre-existing archive.
2384  */
doAdd(Bundle * bundle)2385 int doAdd(Bundle* bundle)
2386 {
2387     ZipFile* zip = NULL;
2388     status_t result = UNKNOWN_ERROR;
2389     const char* zipFileName;
2390 
2391     if (bundle->getUpdate()) {
2392         /* avoid confusion */
2393         fprintf(stderr, "ERROR: can't use '-u' with add\n");
2394         goto bail;
2395     }
2396 
2397     if (bundle->getFileSpecCount() < 1) {
2398         fprintf(stderr, "ERROR: must specify zip file name\n");
2399         goto bail;
2400     }
2401     zipFileName = bundle->getFileSpecEntry(0);
2402 
2403     if (bundle->getFileSpecCount() < 2) {
2404         fprintf(stderr, "NOTE: nothing to do\n");
2405         goto bail;
2406     }
2407 
2408     zip = openReadWrite(zipFileName, true);
2409     if (zip == NULL) {
2410         fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2411         goto bail;
2412     }
2413 
2414     for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2415         const char* fileName = bundle->getFileSpecEntry(i);
2416 
2417         if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
2418             printf(" '%s'... (from gzip)\n", fileName);
2419             result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
2420         } else {
2421             if (bundle->getJunkPath()) {
2422                 String8 storageName = String8(fileName).getPathLeaf();
2423                 printf(" '%s' as '%s'...\n", fileName,
2424                         ResTable::normalizeForOutput(storageName.string()).string());
2425                 result = zip->add(fileName, storageName.string(),
2426                                   bundle->getCompressionMethod(), NULL);
2427             } else {
2428                 printf(" '%s'...\n", fileName);
2429                 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2430             }
2431         }
2432         if (result != NO_ERROR) {
2433             fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2434             if (result == NAME_NOT_FOUND) {
2435                 fprintf(stderr, ": file not found\n");
2436             } else if (result == ALREADY_EXISTS) {
2437                 fprintf(stderr, ": already exists in archive\n");
2438             } else {
2439                 fprintf(stderr, "\n");
2440             }
2441             goto bail;
2442         }
2443     }
2444 
2445     result = NO_ERROR;
2446 
2447 bail:
2448     delete zip;
2449     return (result != NO_ERROR);
2450 }
2451 
2452 
2453 /*
2454  * Delete files from an existing archive.
2455  */
doRemove(Bundle * bundle)2456 int doRemove(Bundle* bundle)
2457 {
2458     ZipFile* zip = NULL;
2459     status_t result = UNKNOWN_ERROR;
2460     const char* zipFileName;
2461 
2462     if (bundle->getFileSpecCount() < 1) {
2463         fprintf(stderr, "ERROR: must specify zip file name\n");
2464         goto bail;
2465     }
2466     zipFileName = bundle->getFileSpecEntry(0);
2467 
2468     if (bundle->getFileSpecCount() < 2) {
2469         fprintf(stderr, "NOTE: nothing to do\n");
2470         goto bail;
2471     }
2472 
2473     zip = openReadWrite(zipFileName, false);
2474     if (zip == NULL) {
2475         fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2476             zipFileName);
2477         goto bail;
2478     }
2479 
2480     for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2481         const char* fileName = bundle->getFileSpecEntry(i);
2482         ZipEntry* entry;
2483 
2484         entry = zip->getEntryByName(fileName);
2485         if (entry == NULL) {
2486             printf(" '%s' NOT FOUND\n", fileName);
2487             continue;
2488         }
2489 
2490         result = zip->remove(entry);
2491 
2492         if (result != NO_ERROR) {
2493             fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2494                 bundle->getFileSpecEntry(i), zipFileName);
2495             goto bail;
2496         }
2497     }
2498 
2499     /* update the archive */
2500     zip->flush();
2501 
2502 bail:
2503     delete zip;
2504     return (result != NO_ERROR);
2505 }
2506 
addResourcesToBuilder(const sp<AaptDir> & dir,const sp<ApkBuilder> & builder,bool ignoreConfig=false)2507 static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
2508     const size_t numDirs = dir->getDirs().size();
2509     for (size_t i = 0; i < numDirs; i++) {
2510         bool ignore = ignoreConfig;
2511         const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2512         const char* dirStr = subDir->getLeaf().string();
2513         if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2514             ignore = true;
2515         }
2516         status_t err = addResourcesToBuilder(subDir, builder, ignore);
2517         if (err != NO_ERROR) {
2518             return err;
2519         }
2520     }
2521 
2522     const size_t numFiles = dir->getFiles().size();
2523     for (size_t i = 0; i < numFiles; i++) {
2524         sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2525         const size_t numConfigs = gp->getFiles().size();
2526         for (size_t j = 0; j < numConfigs; j++) {
2527             status_t err = NO_ERROR;
2528             if (ignoreConfig) {
2529                 err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2530             } else {
2531                 err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2532             }
2533             if (err != NO_ERROR) {
2534                 fprintf(stderr, "Failed to add %s (%s) to builder.\n",
2535                         gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
2536                 return err;
2537             }
2538         }
2539     }
2540     return NO_ERROR;
2541 }
2542 
buildApkName(const String8 & original,const sp<ApkSplit> & split)2543 static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2544     if (split->isBase()) {
2545         return original;
2546     }
2547 
2548     String8 ext(original.getPathExtension());
2549     if (ext == String8(".apk")) {
2550         return String8::format("%s_%s%s",
2551                 original.getBasePath().string(),
2552                 split->getDirectorySafeName().string(),
2553                 ext.string());
2554     }
2555 
2556     return String8::format("%s_%s", original.string(),
2557             split->getDirectorySafeName().string());
2558 }
2559 
2560 /*
2561  * Package up an asset directory and associated application files.
2562  */
doPackage(Bundle * bundle)2563 int doPackage(Bundle* bundle)
2564 {
2565     const char* outputAPKFile;
2566     int retVal = 1;
2567     status_t err;
2568     sp<AaptAssets> assets;
2569     int N;
2570     FILE* fp;
2571     String8 dependencyFile;
2572     sp<ApkBuilder> builder;
2573 
2574     // -c en_XA or/and ar_XB means do pseudolocalization
2575     sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2576     err = configFilter->parse(bundle->getConfigurations());
2577     if (err != NO_ERROR) {
2578         goto bail;
2579     }
2580     if (configFilter->containsPseudo()) {
2581         bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2582     }
2583     if (configFilter->containsPseudoBidi()) {
2584         bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
2585     }
2586 
2587     N = bundle->getFileSpecCount();
2588     if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
2589             && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
2590         fprintf(stderr, "ERROR: no input files\n");
2591         goto bail;
2592     }
2593 
2594     outputAPKFile = bundle->getOutputAPKFile();
2595 
2596     // Make sure the filenames provided exist and are of the appropriate type.
2597     if (outputAPKFile) {
2598         FileType type;
2599         type = getFileType(outputAPKFile);
2600         if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2601             fprintf(stderr,
2602                 "ERROR: output file '%s' exists but is not regular file\n",
2603                 outputAPKFile);
2604             goto bail;
2605         }
2606     }
2607 
2608     // Load the assets.
2609     assets = new AaptAssets();
2610 
2611     // Set up the resource gathering in assets if we're going to generate
2612     // dependency files. Every time we encounter a resource while slurping
2613     // the tree, we'll add it to these stores so we have full resource paths
2614     // to write to a dependency file.
2615     if (bundle->getGenDependencies()) {
2616         sp<FilePathStore> resPathStore = new FilePathStore;
2617         assets->setFullResPaths(resPathStore);
2618         sp<FilePathStore> assetPathStore = new FilePathStore;
2619         assets->setFullAssetPaths(assetPathStore);
2620     }
2621 
2622     err = assets->slurpFromArgs(bundle);
2623     if (err < 0) {
2624         goto bail;
2625     }
2626 
2627     if (bundle->getVerbose()) {
2628         assets->print(String8());
2629     }
2630 
2631     // Create the ApkBuilder, which will collect the compiled files
2632     // to write to the final APK (or sets of APKs if we are building
2633     // a Split APK.
2634     builder = new ApkBuilder(configFilter);
2635 
2636     // If we are generating a Split APK, find out which configurations to split on.
2637     if (bundle->getSplitConfigurations().size() > 0) {
2638         const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2639         const size_t numSplits = splitStrs.size();
2640         for (size_t i = 0; i < numSplits; i++) {
2641             std::set<ConfigDescription> configs;
2642             if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2643                 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2644                 goto bail;
2645             }
2646 
2647             err = builder->createSplitForConfigs(configs);
2648             if (err != NO_ERROR) {
2649                 goto bail;
2650             }
2651         }
2652     }
2653 
2654     // If they asked for any fileAs that need to be compiled, do so.
2655     if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
2656         err = buildResources(bundle, assets, builder);
2657         if (err != 0) {
2658             goto bail;
2659         }
2660     }
2661 
2662     // At this point we've read everything and processed everything.  From here
2663     // on out it's just writing output files.
2664     if (SourcePos::hasErrors()) {
2665         goto bail;
2666     }
2667 
2668     // Update symbols with information about which ones are needed as Java symbols.
2669     assets->applyJavaSymbols();
2670     if (SourcePos::hasErrors()) {
2671         goto bail;
2672     }
2673 
2674     // If we've been asked to generate a dependency file, do that here
2675     if (bundle->getGenDependencies()) {
2676         // If this is the packaging step, generate the dependency file next to
2677         // the output apk (e.g. bin/resources.ap_.d)
2678         if (outputAPKFile) {
2679             dependencyFile = String8(outputAPKFile);
2680             // Add the .d extension to the dependency file.
2681             dependencyFile.append(".d");
2682         } else {
2683             // Else if this is the R.java dependency generation step,
2684             // generate the dependency file in the R.java package subdirectory
2685             // e.g. gen/com/foo/app/R.java.d
2686             dependencyFile = String8(bundle->getRClassDir());
2687             dependencyFile.appendPath("R.java.d");
2688         }
2689         // Make sure we have a clean dependency file to start with
2690         fp = fopen(dependencyFile, "w");
2691         fclose(fp);
2692     }
2693 
2694     // Write out R.java constants
2695     if (!assets->havePrivateSymbols()) {
2696         if (bundle->getCustomPackage() == NULL) {
2697             // Write the R.java file into the appropriate class directory
2698             // e.g. gen/com/foo/app/R.java
2699             err = writeResourceSymbols(bundle, assets, assets->getPackage(), true,
2700                     bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
2701         } else {
2702             const String8 customPkg(bundle->getCustomPackage());
2703             err = writeResourceSymbols(bundle, assets, customPkg, true,
2704                     bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
2705         }
2706         if (err < 0) {
2707             goto bail;
2708         }
2709         // If we have library files, we're going to write our R.java file into
2710         // the appropriate class directory for those libraries as well.
2711         // e.g. gen/com/foo/app/lib/R.java
2712         if (bundle->getExtraPackages() != NULL) {
2713             // Split on colon
2714             String8 libs(bundle->getExtraPackages());
2715             char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2716             while (packageString != NULL) {
2717                 // Write the R.java file out with the correct package name
2718                 err = writeResourceSymbols(bundle, assets, String8(packageString), true,
2719                         bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
2720                 if (err < 0) {
2721                     goto bail;
2722                 }
2723                 packageString = strtok(NULL, ":");
2724             }
2725             libs.unlockBuffer();
2726         }
2727     } else {
2728         err = writeResourceSymbols(bundle, assets, assets->getPackage(), false, false);
2729         if (err < 0) {
2730             goto bail;
2731         }
2732         err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true, false);
2733         if (err < 0) {
2734             goto bail;
2735         }
2736     }
2737 
2738     // Write out the ProGuard file
2739     err = writeProguardFile(bundle, assets);
2740     if (err < 0) {
2741         goto bail;
2742     }
2743 
2744     // Write out the Main Dex ProGuard file
2745     err = writeMainDexProguardFile(bundle, assets);
2746     if (err < 0) {
2747         goto bail;
2748     }
2749 
2750     // Write the apk
2751     if (outputAPKFile) {
2752         // Gather all resources and add them to the APK Builder. The builder will then
2753         // figure out which Split they belong in.
2754         err = addResourcesToBuilder(assets, builder);
2755         if (err != NO_ERROR) {
2756             goto bail;
2757         }
2758 
2759         const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2760         const size_t numSplits = splits.size();
2761         for (size_t i = 0; i < numSplits; i++) {
2762             const sp<ApkSplit>& split = splits[i];
2763             String8 outputPath = buildApkName(String8(outputAPKFile), split);
2764             err = writeAPK(bundle, outputPath, split);
2765             if (err != NO_ERROR) {
2766                 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2767                 goto bail;
2768             }
2769         }
2770     }
2771 
2772     // If we've been asked to generate a dependency file, we need to finish up here.
2773     // the writeResourceSymbols and writeAPK functions have already written the target
2774     // half of the dependency file, now we need to write the prerequisites. (files that
2775     // the R.java file or .ap_ file depend on)
2776     if (bundle->getGenDependencies()) {
2777         // Now that writeResourceSymbols or writeAPK has taken care of writing
2778         // the targets to our dependency file, we'll write the prereqs
2779         fp = fopen(dependencyFile, "a+");
2780         fprintf(fp, " : ");
2781         bool includeRaw = (outputAPKFile != NULL);
2782         err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2783         // Also manually add the AndroidManifeset since it's not under res/ or assets/
2784         // and therefore was not added to our pathstores during slurping
2785         fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2786         fclose(fp);
2787     }
2788 
2789     retVal = 0;
2790 bail:
2791     if (SourcePos::hasErrors()) {
2792         SourcePos::printErrors(stderr);
2793     }
2794     return retVal;
2795 }
2796 
2797 /*
2798  * Do PNG Crunching
2799  * PRECONDITIONS
2800  *  -S flag points to a source directory containing drawable* folders
2801  *  -C flag points to destination directory. The folder structure in the
2802  *     source directory will be mirrored to the destination (cache) directory
2803  *
2804  * POSTCONDITIONS
2805  *  Destination directory will be updated to match the PNG files in
2806  *  the source directory.
2807  */
doCrunch(Bundle * bundle)2808 int doCrunch(Bundle* bundle)
2809 {
2810     fprintf(stdout, "Crunching PNG Files in ");
2811     fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2812     fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2813 
2814     updatePreProcessedCache(bundle);
2815 
2816     return NO_ERROR;
2817 }
2818 
2819 /*
2820  * Do PNG Crunching on a single flag
2821  *  -i points to a single png file
2822  *  -o points to a single png output file
2823  */
doSingleCrunch(Bundle * bundle)2824 int doSingleCrunch(Bundle* bundle)
2825 {
2826     fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2827     fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2828 
2829     String8 input(bundle->getSingleCrunchInputFile());
2830     String8 output(bundle->getSingleCrunchOutputFile());
2831 
2832     if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2833         // we can't return the status_t as it gets truncate to the lower 8 bits.
2834         return 42;
2835     }
2836 
2837     return NO_ERROR;
2838 }
2839 
runInDaemonMode(Bundle * bundle)2840 int runInDaemonMode(Bundle* bundle) {
2841     std::cout << "Ready" << std::endl;
2842     for (std::string cmd; std::getline(std::cin, cmd);) {
2843         if (cmd == "quit") {
2844             return NO_ERROR;
2845         } else if (cmd == "s") {
2846             // Two argument crunch
2847             std::string inputFile, outputFile;
2848             std::getline(std::cin, inputFile);
2849             std::getline(std::cin, outputFile);
2850             bundle->setSingleCrunchInputFile(inputFile.c_str());
2851             bundle->setSingleCrunchOutputFile(outputFile.c_str());
2852             std::cout << "Crunching " << inputFile << std::endl;
2853             if (doSingleCrunch(bundle) != NO_ERROR) {
2854                 std::cout << "Error" << std::endl;
2855             }
2856             std::cout << "Done" << std::endl;
2857         } else {
2858             // in case of invalid command, just bail out.
2859             std::cerr << "Unknown command" << std::endl;
2860             return -1;
2861         }
2862     }
2863     return -1;
2864 }
2865 
2866 char CONSOLE_DATA[2925] = {
2867     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2868     32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2869     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2870     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2871     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2872     86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2873     62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2874     32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2875     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2876     81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2877     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2878     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2879     32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2880     59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2881     32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2882     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2883     59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2884     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2885     10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2886     32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2887     58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2888     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2889     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2890     47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2891     121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2892     32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2893     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2894     81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2895     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2896     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2897     32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2898     59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2899     32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2900     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2901     59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2902     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2903     32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2904     32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2905     70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2906     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2907     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2908     32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2909     81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2910     32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2911     32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2912     81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2913     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2914     32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2915     59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2916     60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2917     32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2918     61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2919     61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2920     46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2921     32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2922     59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2923     109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2924     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2925     67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2926     59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2927     61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2928     32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2929     59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2930     73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2931     59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2932     32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2933     59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2934     46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2935     97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2936     46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2937     119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2938     59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2939     32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2940     32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2941     119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2942     59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2943     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2944     32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2945     81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2946     87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2947     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2948     81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2949     45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2950     32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2951     32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2952     81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2953     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2954     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2955     59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2956     59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2957     32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2958     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2959     81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2960     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2961     10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2962     32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2963     81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2964     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2965     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2966     81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2967     32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2968     32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2969     32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2970     81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2971     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2972     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2973     58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2974     61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2975     32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2976     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2977     81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2978     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2979     32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2980     32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2981     81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2982     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2983     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2984     59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2985     59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2986     32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2987     32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2988     58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2989     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2990     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2991     61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2992     59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2993     32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2994     32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2995     32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2996     61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2997     32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2998     32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2999     59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
3000     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
3001     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
3002     59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
3003     59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3004     32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3005     32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
3006     32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
3007     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
3008     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3009     46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
3010     46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3011     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
3012     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
3013     59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
3014     59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3015     32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3016     32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
3017     32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
3018     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
3019     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
3020     59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
3021     59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3022     32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3023     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
3024     32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
3025     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3026     10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3027     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3028     32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3029     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
3030   };
3031