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