OSDN Git Service

am 8ed784e2: am edc8b857: Merge "Fix "@null" resource values in LayoutLib. [DO NOT...
[android-x86/frameworks-base.git] / tools / aapt / Command.cpp
1 //
2 // Copyright 2006 The Android Open Source Project
3 //
4 // Android Asset Packaging Tool main entry point.
5 //
6 #include "Main.h"
7 #include "Bundle.h"
8 #include "ResourceFilter.h"
9 #include "ResourceTable.h"
10 #include "Images.h"
11 #include "XMLNode.h"
12
13 #include <utils/Log.h>
14 #include <utils/threads.h>
15 #include <utils/List.h>
16 #include <utils/Errors.h>
17
18 #include <fcntl.h>
19 #include <errno.h>
20
21 using namespace android;
22
23 /*
24  * Show version info.  All the cool kids do it.
25  */
26 int doVersion(Bundle* bundle)
27 {
28     if (bundle->getFileSpecCount() != 0)
29         printf("(ignoring extra arguments)\n");
30     printf("Android Asset Packaging Tool, v0.2\n");
31
32     return 0;
33 }
34
35
36 /*
37  * Open the file read only.  The call fails if the file doesn't exist.
38  *
39  * Returns NULL on failure.
40  */
41 ZipFile* openReadOnly(const char* fileName)
42 {
43     ZipFile* zip;
44     status_t result;
45
46     zip = new ZipFile;
47     result = zip->open(fileName, ZipFile::kOpenReadOnly);
48     if (result != NO_ERROR) {
49         if (result == NAME_NOT_FOUND)
50             fprintf(stderr, "ERROR: '%s' not found\n", fileName);
51         else if (result == PERMISSION_DENIED)
52             fprintf(stderr, "ERROR: '%s' access denied\n", fileName);
53         else
54             fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n",
55                 fileName);
56         delete zip;
57         return NULL;
58     }
59
60     return zip;
61 }
62
63 /*
64  * Open the file read-write.  The file will be created if it doesn't
65  * already exist and "okayToCreate" is set.
66  *
67  * Returns NULL on failure.
68  */
69 ZipFile* openReadWrite(const char* fileName, bool okayToCreate)
70 {
71     ZipFile* zip = NULL;
72     status_t result;
73     int flags;
74
75     flags = ZipFile::kOpenReadWrite;
76     if (okayToCreate)
77         flags |= ZipFile::kOpenCreate;
78
79     zip = new ZipFile;
80     result = zip->open(fileName, flags);
81     if (result != NO_ERROR) {
82         delete zip;
83         zip = NULL;
84         goto bail;
85     }
86
87 bail:
88     return zip;
89 }
90
91
92 /*
93  * Return a short string describing the compression method.
94  */
95 const char* compressionName(int method)
96 {
97     if (method == ZipEntry::kCompressStored)
98         return "Stored";
99     else if (method == ZipEntry::kCompressDeflated)
100         return "Deflated";
101     else
102         return "Unknown";
103 }
104
105 /*
106  * Return the percent reduction in size (0% == no compression).
107  */
108 int calcPercent(long uncompressedLen, long compressedLen)
109 {
110     if (!uncompressedLen)
111         return 0;
112     else
113         return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5);
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  */
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     int count, i;
142
143     if (bundle->getVerbose()) {
144         printf("Archive:  %s\n", zipFileName);
145         printf(
146             " Length   Method    Size  Ratio   Offset      Date  Time  CRC-32    Name\n");
147         printf(
148             "--------  ------  ------- -----  -------      ----  ----  ------    ----\n");
149     }
150
151     totalUncLen = totalCompLen = 0;
152
153     count = zip->getNumEntries();
154     for (i = 0; i < count; i++) {
155         entry = zip->getEntryByIndex(i);
156         if (bundle->getVerbose()) {
157             char dateBuf[32];
158             time_t when;
159
160             when = entry->getModWhen();
161             strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M",
162                 localtime(&when));
163
164             printf("%8ld  %-7.7s %7ld %3d%%  %8zd  %s  %08lx  %s\n",
165                 (long) entry->getUncompressedLen(),
166                 compressionName(entry->getCompressionMethod()),
167                 (long) entry->getCompressedLen(),
168                 calcPercent(entry->getUncompressedLen(),
169                             entry->getCompressedLen()),
170                 (size_t) entry->getLFHOffset(),
171                 dateBuf,
172                 entry->getCRC32(),
173                 entry->getFileName());
174         } else {
175             printf("%s\n", entry->getFileName());
176         }
177
178         totalUncLen += entry->getUncompressedLen();
179         totalCompLen += entry->getCompressedLen();
180     }
181
182     if (bundle->getVerbose()) {
183         printf(
184         "--------          -------  ---                            -------\n");
185         printf("%8ld          %7ld  %2d%%                            %d files\n",
186             totalUncLen,
187             totalCompLen,
188             calcPercent(totalUncLen, totalCompLen),
189             zip->getNumEntries());
190     }
191
192     if (bundle->getAndroidList()) {
193         AssetManager assets;
194         if (!assets.addAssetPath(String8(zipFileName), NULL)) {
195             fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n");
196             goto bail;
197         }
198
199         const ResTable& res = assets.getResources(false);
200         if (&res == NULL) {
201             printf("\nNo resource table found.\n");
202         } else {
203 #ifndef HAVE_ANDROID_OS
204             printf("\nResource table:\n");
205             res.print(false);
206 #endif
207         }
208
209         Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
210                                                    Asset::ACCESS_BUFFER);
211         if (manifestAsset == NULL) {
212             printf("\nNo AndroidManifest.xml found.\n");
213         } else {
214             printf("\nAndroid manifest:\n");
215             ResXMLTree tree;
216             tree.setTo(manifestAsset->getBuffer(true),
217                        manifestAsset->getLength());
218             printXMLBlock(&tree);
219         }
220         delete manifestAsset;
221     }
222
223     result = 0;
224
225 bail:
226     delete zip;
227     return result;
228 }
229
230 static ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes)
231 {
232     size_t N = tree.getAttributeCount();
233     for (size_t i=0; i<N; i++) {
234         if (tree.getAttributeNameResID(i) == attrRes) {
235             return (ssize_t)i;
236         }
237     }
238     return -1;
239 }
240
241 String8 getAttribute(const ResXMLTree& tree, const char* ns,
242                             const char* attr, String8* outError)
243 {
244     ssize_t idx = tree.indexOfAttribute(ns, attr);
245     if (idx < 0) {
246         return String8();
247     }
248     Res_value value;
249     if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
250         if (value.dataType != Res_value::TYPE_STRING) {
251             if (outError != NULL) *outError = "attribute is not a string value";
252             return String8();
253         }
254     }
255     size_t len;
256     const uint16_t* str = tree.getAttributeStringValue(idx, &len);
257     return str ? String8(str, len) : String8();
258 }
259
260 static String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError)
261 {
262     ssize_t idx = indexOfAttribute(tree, attrRes);
263     if (idx < 0) {
264         return String8();
265     }
266     Res_value value;
267     if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
268         if (value.dataType != Res_value::TYPE_STRING) {
269             if (outError != NULL) *outError = "attribute is not a string value";
270             return String8();
271         }
272     }
273     size_t len;
274     const uint16_t* str = tree.getAttributeStringValue(idx, &len);
275     return str ? String8(str, len) : String8();
276 }
277
278 static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes,
279         String8* outError, int32_t defValue = -1)
280 {
281     ssize_t idx = indexOfAttribute(tree, attrRes);
282     if (idx < 0) {
283         return defValue;
284     }
285     Res_value value;
286     if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
287         if (value.dataType < Res_value::TYPE_FIRST_INT
288                 || value.dataType > Res_value::TYPE_LAST_INT) {
289             if (outError != NULL) *outError = "attribute is not an integer value";
290             return defValue;
291         }
292     }
293     return value.data;
294 }
295
296 static int32_t getResolvedIntegerAttribute(const ResTable* resTable, const ResXMLTree& tree,
297         uint32_t attrRes, String8* outError, int32_t defValue = -1)
298 {
299     ssize_t idx = indexOfAttribute(tree, attrRes);
300     if (idx < 0) {
301         return defValue;
302     }
303     Res_value value;
304     if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
305         if (value.dataType == Res_value::TYPE_REFERENCE) {
306             resTable->resolveReference(&value, 0);
307         }
308         if (value.dataType < Res_value::TYPE_FIRST_INT
309                 || value.dataType > Res_value::TYPE_LAST_INT) {
310             if (outError != NULL) *outError = "attribute is not an integer value";
311             return defValue;
312         }
313     }
314     return value.data;
315 }
316
317 static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree,
318         uint32_t attrRes, String8* outError)
319 {
320     ssize_t idx = indexOfAttribute(tree, attrRes);
321     if (idx < 0) {
322         return String8();
323     }
324     Res_value value;
325     if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
326         if (value.dataType == Res_value::TYPE_STRING) {
327             size_t len;
328             const uint16_t* str = tree.getAttributeStringValue(idx, &len);
329             return str ? String8(str, len) : String8();
330         }
331         resTable->resolveReference(&value, 0);
332         if (value.dataType != Res_value::TYPE_STRING) {
333             if (outError != NULL) *outError = "attribute is not a string value";
334             return String8();
335         }
336     }
337     size_t len;
338     const Res_value* value2 = &value;
339     const char16_t* str = const_cast<ResTable*>(resTable)->valueToString(value2, 0, NULL, &len);
340     return str ? String8(str, len) : String8();
341 }
342
343 // These are attribute resource constants for the platform, as found
344 // in android.R.attr
345 enum {
346     LABEL_ATTR = 0x01010001,
347     ICON_ATTR = 0x01010002,
348     NAME_ATTR = 0x01010003,
349     DEBUGGABLE_ATTR = 0x0101000f,
350     VERSION_CODE_ATTR = 0x0101021b,
351     VERSION_NAME_ATTR = 0x0101021c,
352     SCREEN_ORIENTATION_ATTR = 0x0101001e,
353     MIN_SDK_VERSION_ATTR = 0x0101020c,
354     MAX_SDK_VERSION_ATTR = 0x01010271,
355     REQ_TOUCH_SCREEN_ATTR = 0x01010227,
356     REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
357     REQ_HARD_KEYBOARD_ATTR = 0x01010229,
358     REQ_NAVIGATION_ATTR = 0x0101022a,
359     REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
360     TARGET_SDK_VERSION_ATTR = 0x01010270,
361     TEST_ONLY_ATTR = 0x01010272,
362     ANY_DENSITY_ATTR = 0x0101026c,
363     GL_ES_VERSION_ATTR = 0x01010281,
364     SMALL_SCREEN_ATTR = 0x01010284,
365     NORMAL_SCREEN_ATTR = 0x01010285,
366     LARGE_SCREEN_ATTR = 0x01010286,
367     XLARGE_SCREEN_ATTR = 0x010102bf,
368     REQUIRED_ATTR = 0x0101028e,
369     SCREEN_SIZE_ATTR = 0x010102ca,
370     SCREEN_DENSITY_ATTR = 0x010102cb,
371     REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
372     COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
373     LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
374     PUBLIC_KEY_ATTR = 0x010103a6,
375 };
376
377 const char *getComponentName(String8 &pkgName, String8 &componentName) {
378     ssize_t idx = componentName.find(".");
379     String8 retStr(pkgName);
380     if (idx == 0) {
381         retStr += componentName;
382     } else if (idx < 0) {
383         retStr += ".";
384         retStr += componentName;
385     } else {
386         return componentName.string();
387     }
388     return retStr.string();
389 }
390
391 static void printCompatibleScreens(ResXMLTree& tree) {
392     size_t len;
393     ResXMLTree::event_code_t code;
394     int depth = 0;
395     bool first = true;
396     printf("compatible-screens:");
397     while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
398         if (code == ResXMLTree::END_TAG) {
399             depth--;
400             if (depth < 0) {
401                 break;
402             }
403             continue;
404         }
405         if (code != ResXMLTree::START_TAG) {
406             continue;
407         }
408         depth++;
409         String8 tag(tree.getElementName(&len));
410         if (tag == "screen") {
411             int32_t screenSize = getIntegerAttribute(tree,
412                     SCREEN_SIZE_ATTR, NULL, -1);
413             int32_t screenDensity = getIntegerAttribute(tree,
414                     SCREEN_DENSITY_ATTR, NULL, -1);
415             if (screenSize > 0 && screenDensity > 0) {
416                 if (!first) {
417                     printf(",");
418                 }
419                 first = false;
420                 printf("'%d/%d'", screenSize, screenDensity);
421             }
422         }
423     }
424     printf("\n");
425 }
426
427 /*
428  * Handle the "dump" command, to extract select data from an archive.
429  */
430 extern char CONSOLE_DATA[2925]; // see EOF
431 int doDump(Bundle* bundle)
432 {
433     status_t result = UNKNOWN_ERROR;
434     Asset* asset = NULL;
435
436     if (bundle->getFileSpecCount() < 1) {
437         fprintf(stderr, "ERROR: no dump option specified\n");
438         return 1;
439     }
440
441     if (bundle->getFileSpecCount() < 2) {
442         fprintf(stderr, "ERROR: no dump file specified\n");
443         return 1;
444     }
445
446     const char* option = bundle->getFileSpecEntry(0);
447     const char* filename = bundle->getFileSpecEntry(1);
448
449     AssetManager assets;
450     void* assetsCookie;
451     if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
452         fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
453         return 1;
454     }
455
456     // Make a dummy config for retrieving resources...  we need to supply
457     // non-default values for some configs so that we can retrieve resources
458     // in the app that don't have a default.  The most important of these is
459     // the API version because key resources like icons will have an implicit
460     // version if they are using newer config types like density.
461     ResTable_config config;
462     config.language[0] = 'e';
463     config.language[1] = 'n';
464     config.country[0] = 'U';
465     config.country[1] = 'S';
466     config.orientation = ResTable_config::ORIENTATION_PORT;
467     config.density = ResTable_config::DENSITY_MEDIUM;
468     config.sdkVersion = 10000; // Very high.
469     config.screenWidthDp = 320;
470     config.screenHeightDp = 480;
471     config.smallestScreenWidthDp = 320;
472     assets.setConfiguration(config);
473
474     const ResTable& res = assets.getResources(false);
475     if (&res == NULL) {
476         fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
477         goto bail;
478     }
479
480     if (strcmp("resources", option) == 0) {
481 #ifndef HAVE_ANDROID_OS
482         res.print(bundle->getValues());
483 #endif
484
485     } else if (strcmp("strings", option) == 0) {
486         const ResStringPool* pool = res.getTableStringBlock(0);
487         printStringPool(pool);
488
489     } else if (strcmp("xmltree", option) == 0) {
490         if (bundle->getFileSpecCount() < 3) {
491             fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
492             goto bail;
493         }
494
495         for (int i=2; i<bundle->getFileSpecCount(); i++) {
496             const char* resname = bundle->getFileSpecEntry(i);
497             ResXMLTree tree;
498             asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
499             if (asset == NULL) {
500                 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
501                 goto bail;
502             }
503
504             if (tree.setTo(asset->getBuffer(true),
505                            asset->getLength()) != NO_ERROR) {
506                 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
507                 goto bail;
508             }
509             tree.restart();
510             printXMLBlock(&tree);
511             tree.uninit();
512             delete asset;
513             asset = NULL;
514         }
515
516     } else if (strcmp("xmlstrings", option) == 0) {
517         if (bundle->getFileSpecCount() < 3) {
518             fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
519             goto bail;
520         }
521
522         for (int i=2; i<bundle->getFileSpecCount(); i++) {
523             const char* resname = bundle->getFileSpecEntry(i);
524             ResXMLTree tree;
525             asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
526             if (asset == NULL) {
527                 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
528                 goto bail;
529             }
530
531             if (tree.setTo(asset->getBuffer(true),
532                            asset->getLength()) != NO_ERROR) {
533                 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
534                 goto bail;
535             }
536             printStringPool(&tree.getStrings());
537             delete asset;
538             asset = NULL;
539         }
540
541     } else {
542         ResXMLTree tree;
543         asset = assets.openNonAsset("AndroidManifest.xml",
544                                             Asset::ACCESS_BUFFER);
545         if (asset == NULL) {
546             fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
547             goto bail;
548         }
549
550         if (tree.setTo(asset->getBuffer(true),
551                        asset->getLength()) != NO_ERROR) {
552             fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
553             goto bail;
554         }
555         tree.restart();
556
557         if (strcmp("permissions", option) == 0) {
558             size_t len;
559             ResXMLTree::event_code_t code;
560             int depth = 0;
561             while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
562                 if (code == ResXMLTree::END_TAG) {
563                     depth--;
564                     continue;
565                 }
566                 if (code != ResXMLTree::START_TAG) {
567                     continue;
568                 }
569                 depth++;
570                 String8 tag(tree.getElementName(&len));
571                 //printf("Depth %d tag %s\n", depth, tag.string());
572                 if (depth == 1) {
573                     if (tag != "manifest") {
574                         fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
575                         goto bail;
576                     }
577                     String8 pkg = getAttribute(tree, NULL, "package", NULL);
578                     printf("package: %s\n", pkg.string());
579                 } else if (depth == 2 && tag == "permission") {
580                     String8 error;
581                     String8 name = getAttribute(tree, NAME_ATTR, &error);
582                     if (error != "") {
583                         fprintf(stderr, "ERROR: %s\n", error.string());
584                         goto bail;
585                     }
586                     printf("permission: %s\n", name.string());
587                 } else if (depth == 2 && tag == "uses-permission") {
588                     String8 error;
589                     String8 name = getAttribute(tree, NAME_ATTR, &error);
590                     if (error != "") {
591                         fprintf(stderr, "ERROR: %s\n", error.string());
592                         goto bail;
593                     }
594                     printf("uses-permission: %s\n", name.string());
595                 }
596             }
597         } else if (strcmp("badging", option) == 0) {
598             Vector<String8> locales;
599             res.getLocales(&locales);
600
601             Vector<ResTable_config> configs;
602             res.getConfigurations(&configs);
603             SortedVector<int> densities;
604             const size_t NC = configs.size();
605             for (size_t i=0; i<NC; i++) {
606                 int dens = configs[i].density;
607                 if (dens == 0) dens = 160;
608                 densities.add(dens);
609             }
610
611             size_t len;
612             ResXMLTree::event_code_t code;
613             int depth = 0;
614             String8 error;
615             bool withinActivity = false;
616             bool isMainActivity = false;
617             bool isLauncherActivity = false;
618             bool isSearchable = false;
619             bool withinApplication = false;
620             bool withinReceiver = false;
621             bool withinService = false;
622             bool withinIntentFilter = false;
623             bool hasMainActivity = false;
624             bool hasOtherActivities = false;
625             bool hasOtherReceivers = false;
626             bool hasOtherServices = false;
627             bool hasWallpaperService = false;
628             bool hasImeService = false;
629             bool hasWidgetReceivers = false;
630             bool hasIntentFilter = false;
631             bool actMainActivity = false;
632             bool actWidgetReceivers = false;
633             bool actImeService = false;
634             bool actWallpaperService = false;
635
636             // These two implement the implicit permissions that are granted
637             // to pre-1.6 applications.
638             bool hasWriteExternalStoragePermission = false;
639             bool hasReadPhoneStatePermission = false;
640
641             // If an app requests write storage, they will also get read storage.
642             bool hasReadExternalStoragePermission = false;
643
644             // Implement transition to read and write call log.
645             bool hasReadContactsPermission = false;
646             bool hasWriteContactsPermission = false;
647             bool hasReadCallLogPermission = false;
648             bool hasWriteCallLogPermission = false;
649
650             // This next group of variables is used to implement a group of
651             // backward-compatibility heuristics necessitated by the addition of
652             // some new uses-feature constants in 2.1 and 2.2. In most cases, the
653             // heuristic is "if an app requests a permission but doesn't explicitly
654             // request the corresponding <uses-feature>, presume it's there anyway".
655             bool specCameraFeature = false; // camera-related
656             bool specCameraAutofocusFeature = false;
657             bool reqCameraAutofocusFeature = false;
658             bool reqCameraFlashFeature = false;
659             bool hasCameraPermission = false;
660             bool specLocationFeature = false; // location-related
661             bool specNetworkLocFeature = false;
662             bool reqNetworkLocFeature = false;
663             bool specGpsFeature = false;
664             bool reqGpsFeature = false;
665             bool hasMockLocPermission = false;
666             bool hasCoarseLocPermission = false;
667             bool hasGpsPermission = false;
668             bool hasGeneralLocPermission = false;
669             bool specBluetoothFeature = false; // Bluetooth API-related
670             bool hasBluetoothPermission = false;
671             bool specMicrophoneFeature = false; // microphone-related
672             bool hasRecordAudioPermission = false;
673             bool specWiFiFeature = false;
674             bool hasWiFiPermission = false;
675             bool specTelephonyFeature = false; // telephony-related
676             bool reqTelephonySubFeature = false;
677             bool hasTelephonyPermission = false;
678             bool specTouchscreenFeature = false; // touchscreen-related
679             bool specMultitouchFeature = false;
680             bool reqDistinctMultitouchFeature = false;
681             bool specScreenPortraitFeature = false;
682             bool specScreenLandscapeFeature = false;
683             bool reqScreenPortraitFeature = false;
684             bool reqScreenLandscapeFeature = false;
685             // 2.2 also added some other features that apps can request, but that
686             // have no corresponding permission, so we cannot implement any
687             // back-compatibility heuristic for them. The below are thus unnecessary
688             // (but are retained here for documentary purposes.)
689             //bool specCompassFeature = false;
690             //bool specAccelerometerFeature = false;
691             //bool specProximityFeature = false;
692             //bool specAmbientLightFeature = false;
693             //bool specLiveWallpaperFeature = false;
694
695             int targetSdk = 0;
696             int smallScreen = 1;
697             int normalScreen = 1;
698             int largeScreen = 1;
699             int xlargeScreen = 1;
700             int anyDensity = 1;
701             int requiresSmallestWidthDp = 0;
702             int compatibleWidthLimitDp = 0;
703             int largestWidthLimitDp = 0;
704             String8 pkg;
705             String8 activityName;
706             String8 activityLabel;
707             String8 activityIcon;
708             String8 receiverName;
709             String8 serviceName;
710             while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
711                 if (code == ResXMLTree::END_TAG) {
712                     depth--;
713                     if (depth < 2) {
714                         withinApplication = false;
715                     } else if (depth < 3) {
716                         if (withinActivity && isMainActivity && isLauncherActivity) {
717                             const char *aName = getComponentName(pkg, activityName);
718                             printf("launchable-activity:");
719                             if (aName != NULL) {
720                                 printf(" name='%s' ", aName);
721                             }
722                             printf(" label='%s' icon='%s'\n",
723                                     activityLabel.string(),
724                                     activityIcon.string());
725                         }
726                         if (!hasIntentFilter) {
727                             hasOtherActivities |= withinActivity;
728                             hasOtherReceivers |= withinReceiver;
729                             hasOtherServices |= withinService;
730                         }
731                         withinActivity = false;
732                         withinService = false;
733                         withinReceiver = false;
734                         hasIntentFilter = false;
735                         isMainActivity = isLauncherActivity = false;
736                     } else if (depth < 4) {
737                         if (withinIntentFilter) {
738                             if (withinActivity) {
739                                 hasMainActivity |= actMainActivity;
740                                 hasOtherActivities |= !actMainActivity;
741                             } else if (withinReceiver) {
742                                 hasWidgetReceivers |= actWidgetReceivers;
743                                 hasOtherReceivers |= !actWidgetReceivers;
744                             } else if (withinService) {
745                                 hasImeService |= actImeService;
746                                 hasWallpaperService |= actWallpaperService;
747                                 hasOtherServices |= (!actImeService && !actWallpaperService);
748                             }
749                         }
750                         withinIntentFilter = false;
751                     }
752                     continue;
753                 }
754                 if (code != ResXMLTree::START_TAG) {
755                     continue;
756                 }
757                 depth++;
758                 String8 tag(tree.getElementName(&len));
759                 //printf("Depth %d,  %s\n", depth, tag.string());
760                 if (depth == 1) {
761                     if (tag != "manifest") {
762                         fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
763                         goto bail;
764                     }
765                     pkg = getAttribute(tree, NULL, "package", NULL);
766                     printf("package: name='%s' ", pkg.string());
767                     int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
768                     if (error != "") {
769                         fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
770                         goto bail;
771                     }
772                     if (versionCode > 0) {
773                         printf("versionCode='%d' ", versionCode);
774                     } else {
775                         printf("versionCode='' ");
776                     }
777                     String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
778                     if (error != "") {
779                         fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
780                         goto bail;
781                     }
782                     printf("versionName='%s'\n", versionName.string());
783                 } else if (depth == 2) {
784                     withinApplication = false;
785                     if (tag == "application") {
786                         withinApplication = true;
787
788                         String8 label;
789                         const size_t NL = locales.size();
790                         for (size_t i=0; i<NL; i++) {
791                             const char* localeStr =  locales[i].string();
792                             assets.setLocale(localeStr != NULL ? localeStr : "");
793                             String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
794                             if (llabel != "") {
795                                 if (localeStr == NULL || strlen(localeStr) == 0) {
796                                     label = llabel;
797                                     printf("application-label:'%s'\n", llabel.string());
798                                 } else {
799                                     if (label == "") {
800                                         label = llabel;
801                                     }
802                                     printf("application-label-%s:'%s'\n", localeStr,
803                                             llabel.string());
804                                 }
805                             }
806                         }
807
808                         ResTable_config tmpConfig = config;
809                         const size_t ND = densities.size();
810                         for (size_t i=0; i<ND; i++) {
811                             tmpConfig.density = densities[i];
812                             assets.setConfiguration(tmpConfig);
813                             String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
814                             if (icon != "") {
815                                 printf("application-icon-%d:'%s'\n", densities[i], icon.string());
816                             }
817                         }
818                         assets.setConfiguration(config);
819
820                         String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
821                         if (error != "") {
822                             fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
823                             goto bail;
824                         }
825                         int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
826                         if (error != "") {
827                             fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
828                             goto bail;
829                         }
830                         printf("application: label='%s' ", label.string());
831                         printf("icon='%s'\n", icon.string());
832                         if (testOnly != 0) {
833                             printf("testOnly='%d'\n", testOnly);
834                         }
835
836                         int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0);
837                         if (error != "") {
838                             fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string());
839                             goto bail;
840                         }
841                         if (debuggable != 0) {
842                             printf("application-debuggable\n");
843                         }
844                     } else if (tag == "uses-sdk") {
845                         int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
846                         if (error != "") {
847                             error = "";
848                             String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
849                             if (error != "") {
850                                 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
851                                         error.string());
852                                 goto bail;
853                             }
854                             if (name == "Donut") targetSdk = 4;
855                             printf("sdkVersion:'%s'\n", name.string());
856                         } else if (code != -1) {
857                             targetSdk = code;
858                             printf("sdkVersion:'%d'\n", code);
859                         }
860                         code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
861                         if (code != -1) {
862                             printf("maxSdkVersion:'%d'\n", code);
863                         }
864                         code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
865                         if (error != "") {
866                             error = "";
867                             String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
868                             if (error != "") {
869                                 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
870                                         error.string());
871                                 goto bail;
872                             }
873                             if (name == "Donut" && targetSdk < 4) targetSdk = 4;
874                             printf("targetSdkVersion:'%s'\n", name.string());
875                         } else if (code != -1) {
876                             if (targetSdk < code) {
877                                 targetSdk = code;
878                             }
879                             printf("targetSdkVersion:'%d'\n", code);
880                         }
881                     } else if (tag == "uses-configuration") {
882                         int32_t reqTouchScreen = getIntegerAttribute(tree,
883                                 REQ_TOUCH_SCREEN_ATTR, NULL, 0);
884                         int32_t reqKeyboardType = getIntegerAttribute(tree,
885                                 REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
886                         int32_t reqHardKeyboard = getIntegerAttribute(tree,
887                                 REQ_HARD_KEYBOARD_ATTR, NULL, 0);
888                         int32_t reqNavigation = getIntegerAttribute(tree,
889                                 REQ_NAVIGATION_ATTR, NULL, 0);
890                         int32_t reqFiveWayNav = getIntegerAttribute(tree,
891                                 REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
892                         printf("uses-configuration:");
893                         if (reqTouchScreen != 0) {
894                             printf(" reqTouchScreen='%d'", reqTouchScreen);
895                         }
896                         if (reqKeyboardType != 0) {
897                             printf(" reqKeyboardType='%d'", reqKeyboardType);
898                         }
899                         if (reqHardKeyboard != 0) {
900                             printf(" reqHardKeyboard='%d'", reqHardKeyboard);
901                         }
902                         if (reqNavigation != 0) {
903                             printf(" reqNavigation='%d'", reqNavigation);
904                         }
905                         if (reqFiveWayNav != 0) {
906                             printf(" reqFiveWayNav='%d'", reqFiveWayNav);
907                         }
908                         printf("\n");
909                     } else if (tag == "supports-screens") {
910                         smallScreen = getIntegerAttribute(tree,
911                                 SMALL_SCREEN_ATTR, NULL, 1);
912                         normalScreen = getIntegerAttribute(tree,
913                                 NORMAL_SCREEN_ATTR, NULL, 1);
914                         largeScreen = getIntegerAttribute(tree,
915                                 LARGE_SCREEN_ATTR, NULL, 1);
916                         xlargeScreen = getIntegerAttribute(tree,
917                                 XLARGE_SCREEN_ATTR, NULL, 1);
918                         anyDensity = getIntegerAttribute(tree,
919                                 ANY_DENSITY_ATTR, NULL, 1);
920                         requiresSmallestWidthDp = getIntegerAttribute(tree,
921                                 REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
922                         compatibleWidthLimitDp = getIntegerAttribute(tree,
923                                 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
924                         largestWidthLimitDp = getIntegerAttribute(tree,
925                                 LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
926                     } else if (tag == "uses-feature") {
927                         String8 name = getAttribute(tree, NAME_ATTR, &error);
928
929                         if (name != "" && error == "") {
930                             int req = getIntegerAttribute(tree,
931                                     REQUIRED_ATTR, NULL, 1);
932
933                             if (name == "android.hardware.camera") {
934                                 specCameraFeature = true;
935                             } else if (name == "android.hardware.camera.autofocus") {
936                                 // these have no corresponding permission to check for,
937                                 // but should imply the foundational camera permission
938                                 reqCameraAutofocusFeature = reqCameraAutofocusFeature || req;
939                                 specCameraAutofocusFeature = true;
940                             } else if (req && (name == "android.hardware.camera.flash")) {
941                                 // these have no corresponding permission to check for,
942                                 // but should imply the foundational camera permission
943                                 reqCameraFlashFeature = true;
944                             } else if (name == "android.hardware.location") {
945                                 specLocationFeature = true;
946                             } else if (name == "android.hardware.location.network") {
947                                 specNetworkLocFeature = true;
948                                 reqNetworkLocFeature = reqNetworkLocFeature || req;
949                             } else if (name == "android.hardware.location.gps") {
950                                 specGpsFeature = true;
951                                 reqGpsFeature = reqGpsFeature || req;
952                             } else if (name == "android.hardware.bluetooth") {
953                                 specBluetoothFeature = true;
954                             } else if (name == "android.hardware.touchscreen") {
955                                 specTouchscreenFeature = true;
956                             } else if (name == "android.hardware.touchscreen.multitouch") {
957                                 specMultitouchFeature = true;
958                             } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
959                                 reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req;
960                             } else if (name == "android.hardware.microphone") {
961                                 specMicrophoneFeature = true;
962                             } else if (name == "android.hardware.wifi") {
963                                 specWiFiFeature = true;
964                             } else if (name == "android.hardware.telephony") {
965                                 specTelephonyFeature = true;
966                             } else if (req && (name == "android.hardware.telephony.gsm" ||
967                                                name == "android.hardware.telephony.cdma")) {
968                                 // these have no corresponding permission to check for,
969                                 // but should imply the foundational telephony permission
970                                 reqTelephonySubFeature = true;
971                             } else if (name == "android.hardware.screen.portrait") {
972                                 specScreenPortraitFeature = true;
973                             } else if (name == "android.hardware.screen.landscape") {
974                                 specScreenLandscapeFeature = true;
975                             }
976                             printf("uses-feature%s:'%s'\n",
977                                     req ? "" : "-not-required", name.string());
978                         } else {
979                             int vers = getIntegerAttribute(tree,
980                                     GL_ES_VERSION_ATTR, &error);
981                             if (error == "") {
982                                 printf("uses-gl-es:'0x%x'\n", vers);
983                             }
984                         }
985                     } else if (tag == "uses-permission") {
986                         String8 name = getAttribute(tree, NAME_ATTR, &error);
987                         if (name != "" && error == "") {
988                             if (name == "android.permission.CAMERA") {
989                                 hasCameraPermission = true;
990                             } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
991                                 hasGpsPermission = true;
992                             } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
993                                 hasMockLocPermission = true;
994                             } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
995                                 hasCoarseLocPermission = true;
996                             } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
997                                        name == "android.permission.INSTALL_LOCATION_PROVIDER") {
998                                 hasGeneralLocPermission = true;
999                             } else if (name == "android.permission.BLUETOOTH" ||
1000                                        name == "android.permission.BLUETOOTH_ADMIN") {
1001                                 hasBluetoothPermission = true;
1002                             } else if (name == "android.permission.RECORD_AUDIO") {
1003                                 hasRecordAudioPermission = true;
1004                             } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1005                                        name == "android.permission.CHANGE_WIFI_STATE" ||
1006                                        name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
1007                                 hasWiFiPermission = true;
1008                             } else if (name == "android.permission.CALL_PHONE" ||
1009                                        name == "android.permission.CALL_PRIVILEGED" ||
1010                                        name == "android.permission.MODIFY_PHONE_STATE" ||
1011                                        name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1012                                        name == "android.permission.READ_SMS" ||
1013                                        name == "android.permission.RECEIVE_SMS" ||
1014                                        name == "android.permission.RECEIVE_MMS" ||
1015                                        name == "android.permission.RECEIVE_WAP_PUSH" ||
1016                                        name == "android.permission.SEND_SMS" ||
1017                                        name == "android.permission.WRITE_APN_SETTINGS" ||
1018                                        name == "android.permission.WRITE_SMS") {
1019                                 hasTelephonyPermission = true;
1020                             } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1021                                 hasWriteExternalStoragePermission = true;
1022                             } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1023                                 hasReadExternalStoragePermission = true;
1024                             } else if (name == "android.permission.READ_PHONE_STATE") {
1025                                 hasReadPhoneStatePermission = true;
1026                             } else if (name == "android.permission.READ_CONTACTS") {
1027                                 hasReadContactsPermission = true;
1028                             } else if (name == "android.permission.WRITE_CONTACTS") {
1029                                 hasWriteContactsPermission = true;
1030                             } else if (name == "android.permission.READ_CALL_LOG") {
1031                                 hasReadCallLogPermission = true;
1032                             } else if (name == "android.permission.WRITE_CALL_LOG") {
1033                                 hasWriteCallLogPermission = true;
1034                             }
1035                             printf("uses-permission:'%s'\n", name.string());
1036                         } else {
1037                             fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1038                                     error.string());
1039                             goto bail;
1040                         }
1041                     } else if (tag == "uses-package") {
1042                         String8 name = getAttribute(tree, NAME_ATTR, &error);
1043                         if (name != "" && error == "") {
1044                             printf("uses-package:'%s'\n", name.string());
1045                         } else {
1046                             fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1047                                     error.string());
1048                                 goto bail;
1049                         }
1050                     } else if (tag == "original-package") {
1051                         String8 name = getAttribute(tree, NAME_ATTR, &error);
1052                         if (name != "" && error == "") {
1053                             printf("original-package:'%s'\n", name.string());
1054                         } else {
1055                             fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1056                                     error.string());
1057                                 goto bail;
1058                         }
1059                     } else if (tag == "supports-gl-texture") {
1060                         String8 name = getAttribute(tree, NAME_ATTR, &error);
1061                         if (name != "" && error == "") {
1062                             printf("supports-gl-texture:'%s'\n", name.string());
1063                         } else {
1064                             fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1065                                     error.string());
1066                                 goto bail;
1067                         }
1068                     } else if (tag == "compatible-screens") {
1069                         printCompatibleScreens(tree);
1070                         depth--;
1071                     } else if (tag == "package-verifier") {
1072                         String8 name = getAttribute(tree, NAME_ATTR, &error);
1073                         if (name != "" && error == "") {
1074                             String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
1075                             if (publicKey != "" && error == "") {
1076                                 printf("package-verifier: name='%s' publicKey='%s'\n",
1077                                         name.string(), publicKey.string());
1078                             }
1079                         }
1080                     }
1081                 } else if (depth == 3 && withinApplication) {
1082                     withinActivity = false;
1083                     withinReceiver = false;
1084                     withinService = false;
1085                     hasIntentFilter = false;
1086                     if(tag == "activity") {
1087                         withinActivity = true;
1088                         activityName = getAttribute(tree, NAME_ATTR, &error);
1089                         if (error != "") {
1090                             fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1091                             goto bail;
1092                         }
1093
1094                         activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1095                         if (error != "") {
1096                             fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string());
1097                             goto bail;
1098                         }
1099
1100                         activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1101                         if (error != "") {
1102                             fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
1103                             goto bail;
1104                         }
1105
1106                         int32_t orien = getResolvedIntegerAttribute(&res, tree,
1107                                 SCREEN_ORIENTATION_ATTR, &error);
1108                         if (error == "") {
1109                             if (orien == 0 || orien == 6 || orien == 8) {
1110                                 // Requests landscape, sensorLandscape, or reverseLandscape.
1111                                 reqScreenLandscapeFeature = true;
1112                             } else if (orien == 1 || orien == 7 || orien == 9) {
1113                                 // Requests portrait, sensorPortrait, or reversePortrait.
1114                                 reqScreenPortraitFeature = true;
1115                             }
1116                         }
1117                     } else if (tag == "uses-library") {
1118                         String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1119                         if (error != "") {
1120                             fprintf(stderr, "ERROR getting 'android:name' attribute for uses-library: %s\n", error.string());
1121                             goto bail;
1122                         }
1123                         int req = getIntegerAttribute(tree,
1124                                 REQUIRED_ATTR, NULL, 1);
1125                         printf("uses-library%s:'%s'\n",
1126                                 req ? "" : "-not-required", libraryName.string());
1127                     } else if (tag == "receiver") {
1128                         withinReceiver = true;
1129                         receiverName = getAttribute(tree, NAME_ATTR, &error);
1130
1131                         if (error != "") {
1132                             fprintf(stderr, "ERROR getting 'android:name' attribute for receiver: %s\n", error.string());
1133                             goto bail;
1134                         }
1135                     } else if (tag == "service") {
1136                         withinService = true;
1137                         serviceName = getAttribute(tree, NAME_ATTR, &error);
1138
1139                         if (error != "") {
1140                             fprintf(stderr, "ERROR getting 'android:name' attribute for service: %s\n", error.string());
1141                             goto bail;
1142                         }
1143                     }
1144                 } else if ((depth == 4) && (tag == "intent-filter")) {
1145                     hasIntentFilter = true;
1146                     withinIntentFilter = true;
1147                     actMainActivity = actWidgetReceivers = actImeService = actWallpaperService = false;
1148                 } else if ((depth == 5) && withinIntentFilter){
1149                     String8 action;
1150                     if (tag == "action") {
1151                         action = getAttribute(tree, NAME_ATTR, &error);
1152                         if (error != "") {
1153                             fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1154                             goto bail;
1155                         }
1156                         if (withinActivity) {
1157                             if (action == "android.intent.action.MAIN") {
1158                                 isMainActivity = true;
1159                                 actMainActivity = true;
1160                             }
1161                         } else if (withinReceiver) {
1162                             if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1163                                 actWidgetReceivers = true;
1164                             }
1165                         } else if (withinService) {
1166                             if (action == "android.view.InputMethod") {
1167                                 actImeService = true;
1168                             } else if (action == "android.service.wallpaper.WallpaperService") {
1169                                 actWallpaperService = true;
1170                             }
1171                         }
1172                         if (action == "android.intent.action.SEARCH") {
1173                             isSearchable = true;
1174                         }
1175                     }
1176
1177                     if (tag == "category") {
1178                         String8 category = getAttribute(tree, NAME_ATTR, &error);
1179                         if (error != "") {
1180                             fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string());
1181                             goto bail;
1182                         }
1183                         if (withinActivity) {
1184                             if (category == "android.intent.category.LAUNCHER") {
1185                                 isLauncherActivity = true;
1186                             }
1187                         }
1188                     }
1189                 }
1190             }
1191
1192             // Pre-1.6 implicitly granted permission compatibility logic
1193             if (targetSdk < 4) {
1194                 if (!hasWriteExternalStoragePermission) {
1195                     printf("uses-permission:'android.permission.WRITE_EXTERNAL_STORAGE'\n");
1196                     printf("uses-implied-permission:'android.permission.WRITE_EXTERNAL_STORAGE'," \
1197                             "'targetSdkVersion < 4'\n");
1198                     hasWriteExternalStoragePermission = true;
1199                 }
1200                 if (!hasReadPhoneStatePermission) {
1201                     printf("uses-permission:'android.permission.READ_PHONE_STATE'\n");
1202                     printf("uses-implied-permission:'android.permission.READ_PHONE_STATE'," \
1203                             "'targetSdkVersion < 4'\n");
1204                 }
1205             }
1206
1207             // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1208             // force them to always take READ_EXTERNAL_STORAGE as well.  We always
1209             // do this (regardless of target API version) because we can't have
1210             // an app with write permission but not read permission.
1211             if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
1212                 printf("uses-permission:'android.permission.READ_EXTERNAL_STORAGE'\n");
1213                 printf("uses-implied-permission:'android.permission.READ_EXTERNAL_STORAGE'," \
1214                         "'requested WRITE_EXTERNAL_STORAGE'\n");
1215             }
1216
1217             // Pre-JellyBean call log permission compatibility.
1218             if (targetSdk < 16) {
1219                 if (!hasReadCallLogPermission && hasReadContactsPermission) {
1220                     printf("uses-permission:'android.permission.READ_CALL_LOG'\n");
1221                     printf("uses-implied-permission:'android.permission.READ_CALL_LOG'," \
1222                             "'targetSdkVersion < 16 and requested READ_CONTACTS'\n");
1223                 }
1224                 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
1225                     printf("uses-permission:'android.permission.WRITE_CALL_LOG'\n");
1226                     printf("uses-implied-permission:'android.permission.WRITE_CALL_LOG'," \
1227                             "'targetSdkVersion < 16 and requested WRITE_CONTACTS'\n");
1228                 }
1229             }
1230
1231             /* The following blocks handle printing "inferred" uses-features, based
1232              * on whether related features or permissions are used by the app.
1233              * Note that the various spec*Feature variables denote whether the
1234              * relevant tag was *present* in the AndroidManfest, not that it was
1235              * present and set to true.
1236              */
1237             // Camera-related back-compatibility logic
1238             if (!specCameraFeature) {
1239                 if (reqCameraFlashFeature) {
1240                     // if app requested a sub-feature (autofocus or flash) and didn't
1241                     // request the base camera feature, we infer that it meant to
1242                     printf("uses-feature:'android.hardware.camera'\n");
1243                     printf("uses-implied-feature:'android.hardware.camera'," \
1244                             "'requested android.hardware.camera.flash feature'\n");
1245                 } else if (reqCameraAutofocusFeature) {
1246                     // if app requested a sub-feature (autofocus or flash) and didn't
1247                     // request the base camera feature, we infer that it meant to
1248                     printf("uses-feature:'android.hardware.camera'\n");
1249                     printf("uses-implied-feature:'android.hardware.camera'," \
1250                             "'requested android.hardware.camera.autofocus feature'\n");
1251                 } else if (hasCameraPermission) {
1252                     // if app wants to use camera but didn't request the feature, we infer 
1253                     // that it meant to, and further that it wants autofocus
1254                     // (which was the 1.0 - 1.5 behavior)
1255                     printf("uses-feature:'android.hardware.camera'\n");
1256                     if (!specCameraAutofocusFeature) {
1257                         printf("uses-feature:'android.hardware.camera.autofocus'\n");
1258                         printf("uses-implied-feature:'android.hardware.camera.autofocus'," \
1259                                 "'requested android.permission.CAMERA permission'\n");
1260                     }
1261                 }
1262             }
1263
1264             // Location-related back-compatibility logic
1265             if (!specLocationFeature &&
1266                 (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission ||
1267                  hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) {
1268                 // if app either takes a location-related permission or requests one of the
1269                 // sub-features, we infer that it also meant to request the base location feature
1270                 printf("uses-feature:'android.hardware.location'\n");
1271                 printf("uses-implied-feature:'android.hardware.location'," \
1272                         "'requested a location access permission'\n");
1273             }
1274             if (!specGpsFeature && hasGpsPermission) {
1275                 // if app takes GPS (FINE location) perm but does not request the GPS
1276                 // feature, we infer that it meant to
1277                 printf("uses-feature:'android.hardware.location.gps'\n");
1278                 printf("uses-implied-feature:'android.hardware.location.gps'," \
1279                         "'requested android.permission.ACCESS_FINE_LOCATION permission'\n");
1280             }
1281             if (!specNetworkLocFeature && hasCoarseLocPermission) {
1282                 // if app takes Network location (COARSE location) perm but does not request the
1283                 // network location feature, we infer that it meant to
1284                 printf("uses-feature:'android.hardware.location.network'\n");
1285                 printf("uses-implied-feature:'android.hardware.location.network'," \
1286                         "'requested android.permission.ACCESS_COURSE_LOCATION permission'\n");
1287             }
1288
1289             // Bluetooth-related compatibility logic
1290             if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
1291                 // if app takes a Bluetooth permission but does not request the Bluetooth
1292                 // feature, we infer that it meant to
1293                 printf("uses-feature:'android.hardware.bluetooth'\n");
1294                 printf("uses-implied-feature:'android.hardware.bluetooth'," \
1295                         "'requested android.permission.BLUETOOTH or android.permission.BLUETOOTH_ADMIN " \
1296                         "permission and targetSdkVersion > 4'\n");
1297             }
1298
1299             // Microphone-related compatibility logic
1300             if (!specMicrophoneFeature && hasRecordAudioPermission) {
1301                 // if app takes the record-audio permission but does not request the microphone
1302                 // feature, we infer that it meant to
1303                 printf("uses-feature:'android.hardware.microphone'\n");
1304                 printf("uses-implied-feature:'android.hardware.microphone'," \
1305                         "'requested android.permission.RECORD_AUDIO permission'\n");
1306             }
1307
1308             // WiFi-related compatibility logic
1309             if (!specWiFiFeature && hasWiFiPermission) {
1310                 // if app takes one of the WiFi permissions but does not request the WiFi
1311                 // feature, we infer that it meant to
1312                 printf("uses-feature:'android.hardware.wifi'\n");
1313                 printf("uses-implied-feature:'android.hardware.wifi'," \
1314                         "'requested android.permission.ACCESS_WIFI_STATE, " \
1315                         "android.permission.CHANGE_WIFI_STATE, or " \
1316                         "android.permission.CHANGE_WIFI_MULTICAST_STATE permission'\n");
1317             }
1318
1319             // Telephony-related compatibility logic
1320             if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) {
1321                 // if app takes one of the telephony permissions or requests a sub-feature but
1322                 // does not request the base telephony feature, we infer that it meant to
1323                 printf("uses-feature:'android.hardware.telephony'\n");
1324                 printf("uses-implied-feature:'android.hardware.telephony'," \
1325                         "'requested a telephony-related permission or feature'\n");
1326             }
1327
1328             // Touchscreen-related back-compatibility logic
1329             if (!specTouchscreenFeature) { // not a typo!
1330                 // all apps are presumed to require a touchscreen, unless they explicitly say
1331                 // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
1332                 // Note that specTouchscreenFeature is true if the tag is present, regardless
1333                 // of whether its value is true or false, so this is safe
1334                 printf("uses-feature:'android.hardware.touchscreen'\n");
1335                 printf("uses-implied-feature:'android.hardware.touchscreen'," \
1336                         "'assumed you require a touch screen unless explicitly made optional'\n");
1337             }
1338             if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
1339                 // if app takes one of the telephony permissions or requests a sub-feature but
1340                 // does not request the base telephony feature, we infer that it meant to
1341                 printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
1342                 printf("uses-implied-feature:'android.hardware.touchscreen.multitouch'," \
1343                         "'requested android.hardware.touchscreen.multitouch.distinct feature'\n");
1344             }
1345
1346             // Landscape/portrait-related compatibility logic
1347             if (!specScreenLandscapeFeature && !specScreenPortraitFeature) {
1348                 // If the app has specified any activities in its manifest
1349                 // that request a specific orientation, then assume that
1350                 // orientation is required.
1351                 if (reqScreenLandscapeFeature) {
1352                     printf("uses-feature:'android.hardware.screen.landscape'\n");
1353                     printf("uses-implied-feature:'android.hardware.screen.landscape'," \
1354                             "'one or more activities have specified a landscape orientation'\n");
1355                 }
1356                 if (reqScreenPortraitFeature) {
1357                     printf("uses-feature:'android.hardware.screen.portrait'\n");
1358                     printf("uses-implied-feature:'android.hardware.screen.portrait'," \
1359                             "'one or more activities have specified a portrait orientation'\n");
1360                 }
1361             }
1362
1363             if (hasMainActivity) {
1364                 printf("main\n");
1365             }
1366             if (hasWidgetReceivers) {
1367                 printf("app-widget\n");
1368             }
1369             if (hasImeService) {
1370                 printf("ime\n");
1371             }
1372             if (hasWallpaperService) {
1373                 printf("wallpaper\n");
1374             }
1375             if (hasOtherActivities) {
1376                 printf("other-activities\n");
1377             }
1378             if (isSearchable) {
1379                 printf("search\n");
1380             }
1381             if (hasOtherReceivers) {
1382                 printf("other-receivers\n");
1383             }
1384             if (hasOtherServices) {
1385                 printf("other-services\n");
1386             }
1387
1388             // For modern apps, if screen size buckets haven't been specified
1389             // but the new width ranges have, then infer the buckets from them.
1390             if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1391                     && requiresSmallestWidthDp > 0) {
1392                 int compatWidth = compatibleWidthLimitDp;
1393                 if (compatWidth <= 0) compatWidth = requiresSmallestWidthDp;
1394                 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1395                     smallScreen = -1;
1396                 } else {
1397                     smallScreen = 0;
1398                 }
1399                 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1400                     normalScreen = -1;
1401                 } else {
1402                     normalScreen = 0;
1403                 }
1404                 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1405                     largeScreen = -1;
1406                 } else {
1407                     largeScreen = 0;
1408                 }
1409                 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1410                     xlargeScreen = -1;
1411                 } else {
1412                     xlargeScreen = 0;
1413                 }
1414             }
1415
1416             // Determine default values for any unspecified screen sizes,
1417             // based on the target SDK of the package.  As of 4 (donut)
1418             // the screen size support was introduced, so all default to
1419             // enabled.
1420             if (smallScreen > 0) {
1421                 smallScreen = targetSdk >= 4 ? -1 : 0;
1422             }
1423             if (normalScreen > 0) {
1424                 normalScreen = -1;
1425             }
1426             if (largeScreen > 0) {
1427                 largeScreen = targetSdk >= 4 ? -1 : 0;
1428             }
1429             if (xlargeScreen > 0) {
1430                 // Introduced in Gingerbread.
1431                 xlargeScreen = targetSdk >= 9 ? -1 : 0;
1432             }
1433             if (anyDensity > 0) {
1434                 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1435                         || compatibleWidthLimitDp > 0) ? -1 : 0;
1436             }
1437             printf("supports-screens:");
1438             if (smallScreen != 0) printf(" 'small'");
1439             if (normalScreen != 0) printf(" 'normal'");
1440             if (largeScreen != 0) printf(" 'large'");
1441             if (xlargeScreen != 0) printf(" 'xlarge'");
1442             printf("\n");
1443             printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
1444             if (requiresSmallestWidthDp > 0) {
1445                 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1446             }
1447             if (compatibleWidthLimitDp > 0) {
1448                 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1449             }
1450             if (largestWidthLimitDp > 0) {
1451                 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1452             }
1453
1454             printf("locales:");
1455             const size_t NL = locales.size();
1456             for (size_t i=0; i<NL; i++) {
1457                 const char* localeStr =  locales[i].string();
1458                 if (localeStr == NULL || strlen(localeStr) == 0) {
1459                     localeStr = "--_--";
1460                 }
1461                 printf(" '%s'", localeStr);
1462             }
1463             printf("\n");
1464
1465             printf("densities:");
1466             const size_t ND = densities.size();
1467             for (size_t i=0; i<ND; i++) {
1468                 printf(" '%d'", densities[i]);
1469             }
1470             printf("\n");
1471
1472             AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1473             if (dir != NULL) {
1474                 if (dir->getFileCount() > 0) {
1475                     printf("native-code:");
1476                     for (size_t i=0; i<dir->getFileCount(); i++) {
1477                         printf(" '%s'", dir->getFileName(i).string());
1478                     }
1479                     printf("\n");
1480                 }
1481                 delete dir;
1482             }
1483         } else if (strcmp("badger", option) == 0) {
1484             printf("%s", CONSOLE_DATA);
1485         } else if (strcmp("configurations", option) == 0) {
1486             Vector<ResTable_config> configs;
1487             res.getConfigurations(&configs);
1488             const size_t N = configs.size();
1489             for (size_t i=0; i<N; i++) {
1490                 printf("%s\n", configs[i].toString().string());
1491             }
1492         } else {
1493             fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
1494             goto bail;
1495         }
1496     }
1497
1498     result = NO_ERROR;
1499
1500 bail:
1501     if (asset) {
1502         delete asset;
1503     }
1504     return (result != NO_ERROR);
1505 }
1506
1507
1508 /*
1509  * Handle the "add" command, which wants to add files to a new or
1510  * pre-existing archive.
1511  */
1512 int doAdd(Bundle* bundle)
1513 {
1514     ZipFile* zip = NULL;
1515     status_t result = UNKNOWN_ERROR;
1516     const char* zipFileName;
1517
1518     if (bundle->getUpdate()) {
1519         /* avoid confusion */
1520         fprintf(stderr, "ERROR: can't use '-u' with add\n");
1521         goto bail;
1522     }
1523
1524     if (bundle->getFileSpecCount() < 1) {
1525         fprintf(stderr, "ERROR: must specify zip file name\n");
1526         goto bail;
1527     }
1528     zipFileName = bundle->getFileSpecEntry(0);
1529
1530     if (bundle->getFileSpecCount() < 2) {
1531         fprintf(stderr, "NOTE: nothing to do\n");
1532         goto bail;
1533     }
1534
1535     zip = openReadWrite(zipFileName, true);
1536     if (zip == NULL) {
1537         fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
1538         goto bail;
1539     }
1540
1541     for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1542         const char* fileName = bundle->getFileSpecEntry(i);
1543
1544         if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
1545             printf(" '%s'... (from gzip)\n", fileName);
1546             result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
1547         } else {
1548             if (bundle->getJunkPath()) {
1549                 String8 storageName = String8(fileName).getPathLeaf();
1550                 printf(" '%s' as '%s'...\n", fileName, storageName.string());
1551                 result = zip->add(fileName, storageName.string(),
1552                                   bundle->getCompressionMethod(), NULL);
1553             } else {
1554                 printf(" '%s'...\n", fileName);
1555                 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
1556             }
1557         }
1558         if (result != NO_ERROR) {
1559             fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
1560             if (result == NAME_NOT_FOUND)
1561                 fprintf(stderr, ": file not found\n");
1562             else if (result == ALREADY_EXISTS)
1563                 fprintf(stderr, ": already exists in archive\n");
1564             else
1565                 fprintf(stderr, "\n");
1566             goto bail;
1567         }
1568     }
1569
1570     result = NO_ERROR;
1571
1572 bail:
1573     delete zip;
1574     return (result != NO_ERROR);
1575 }
1576
1577
1578 /*
1579  * Delete files from an existing archive.
1580  */
1581 int doRemove(Bundle* bundle)
1582 {
1583     ZipFile* zip = NULL;
1584     status_t result = UNKNOWN_ERROR;
1585     const char* zipFileName;
1586
1587     if (bundle->getFileSpecCount() < 1) {
1588         fprintf(stderr, "ERROR: must specify zip file name\n");
1589         goto bail;
1590     }
1591     zipFileName = bundle->getFileSpecEntry(0);
1592
1593     if (bundle->getFileSpecCount() < 2) {
1594         fprintf(stderr, "NOTE: nothing to do\n");
1595         goto bail;
1596     }
1597
1598     zip = openReadWrite(zipFileName, false);
1599     if (zip == NULL) {
1600         fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
1601             zipFileName);
1602         goto bail;
1603     }
1604
1605     for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1606         const char* fileName = bundle->getFileSpecEntry(i);
1607         ZipEntry* entry;
1608
1609         entry = zip->getEntryByName(fileName);
1610         if (entry == NULL) {
1611             printf(" '%s' NOT FOUND\n", fileName);
1612             continue;
1613         }
1614
1615         result = zip->remove(entry);
1616
1617         if (result != NO_ERROR) {
1618             fprintf(stderr, "Unable to delete '%s' from '%s'\n",
1619                 bundle->getFileSpecEntry(i), zipFileName);
1620             goto bail;
1621         }
1622     }
1623
1624     /* update the archive */
1625     zip->flush();
1626
1627 bail:
1628     delete zip;
1629     return (result != NO_ERROR);
1630 }
1631
1632
1633 /*
1634  * Package up an asset directory and associated application files.
1635  */
1636 int doPackage(Bundle* bundle)
1637 {
1638     const char* outputAPKFile;
1639     int retVal = 1;
1640     status_t err;
1641     sp<AaptAssets> assets;
1642     int N;
1643     FILE* fp;
1644     String8 dependencyFile;
1645
1646     // -c zz_ZZ means do pseudolocalization
1647     ResourceFilter filter;
1648     err = filter.parse(bundle->getConfigurations());
1649     if (err != NO_ERROR) {
1650         goto bail;
1651     }
1652     if (filter.containsPseudo()) {
1653         bundle->setPseudolocalize(true);
1654     }
1655
1656     N = bundle->getFileSpecCount();
1657     if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
1658             && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDir() == NULL) {
1659         fprintf(stderr, "ERROR: no input files\n");
1660         goto bail;
1661     }
1662
1663     outputAPKFile = bundle->getOutputAPKFile();
1664
1665     // Make sure the filenames provided exist and are of the appropriate type.
1666     if (outputAPKFile) {
1667         FileType type;
1668         type = getFileType(outputAPKFile);
1669         if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
1670             fprintf(stderr,
1671                 "ERROR: output file '%s' exists but is not regular file\n",
1672                 outputAPKFile);
1673             goto bail;
1674         }
1675     }
1676
1677     // Load the assets.
1678     assets = new AaptAssets();
1679
1680     // Set up the resource gathering in assets if we're going to generate
1681     // dependency files. Every time we encounter a resource while slurping
1682     // the tree, we'll add it to these stores so we have full resource paths
1683     // to write to a dependency file.
1684     if (bundle->getGenDependencies()) {
1685         sp<FilePathStore> resPathStore = new FilePathStore;
1686         assets->setFullResPaths(resPathStore);
1687         sp<FilePathStore> assetPathStore = new FilePathStore;
1688         assets->setFullAssetPaths(assetPathStore);
1689     }
1690
1691     err = assets->slurpFromArgs(bundle);
1692     if (err < 0) {
1693         goto bail;
1694     }
1695
1696     if (bundle->getVerbose()) {
1697         assets->print(String8());
1698     }
1699
1700     // If they asked for any fileAs that need to be compiled, do so.
1701     if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
1702         err = buildResources(bundle, assets);
1703         if (err != 0) {
1704             goto bail;
1705         }
1706     }
1707
1708     // At this point we've read everything and processed everything.  From here
1709     // on out it's just writing output files.
1710     if (SourcePos::hasErrors()) {
1711         goto bail;
1712     }
1713
1714     // Update symbols with information about which ones are needed as Java symbols.
1715     assets->applyJavaSymbols();
1716     if (SourcePos::hasErrors()) {
1717         goto bail;
1718     }
1719
1720     // If we've been asked to generate a dependency file, do that here
1721     if (bundle->getGenDependencies()) {
1722         // If this is the packaging step, generate the dependency file next to
1723         // the output apk (e.g. bin/resources.ap_.d)
1724         if (outputAPKFile) {
1725             dependencyFile = String8(outputAPKFile);
1726             // Add the .d extension to the dependency file.
1727             dependencyFile.append(".d");
1728         } else {
1729             // Else if this is the R.java dependency generation step,
1730             // generate the dependency file in the R.java package subdirectory
1731             // e.g. gen/com/foo/app/R.java.d
1732             dependencyFile = String8(bundle->getRClassDir());
1733             dependencyFile.appendPath("R.java.d");
1734         }
1735         // Make sure we have a clean dependency file to start with
1736         fp = fopen(dependencyFile, "w");
1737         fclose(fp);
1738     }
1739
1740     // Write out R.java constants
1741     if (!assets->havePrivateSymbols()) {
1742         if (bundle->getCustomPackage() == NULL) {
1743             // Write the R.java file into the appropriate class directory
1744             // e.g. gen/com/foo/app/R.java
1745             err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
1746         } else {
1747             const String8 customPkg(bundle->getCustomPackage());
1748             err = writeResourceSymbols(bundle, assets, customPkg, true);
1749         }
1750         if (err < 0) {
1751             goto bail;
1752         }
1753         // If we have library files, we're going to write our R.java file into
1754         // the appropriate class directory for those libraries as well.
1755         // e.g. gen/com/foo/app/lib/R.java
1756         if (bundle->getExtraPackages() != NULL) {
1757             // Split on colon
1758             String8 libs(bundle->getExtraPackages());
1759             char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
1760             while (packageString != NULL) {
1761                 // Write the R.java file out with the correct package name
1762                 err = writeResourceSymbols(bundle, assets, String8(packageString), true);
1763                 if (err < 0) {
1764                     goto bail;
1765                 }
1766                 packageString = strtok(NULL, ":");
1767             }
1768             libs.unlockBuffer();
1769         }
1770     } else {
1771         err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
1772         if (err < 0) {
1773             goto bail;
1774         }
1775         err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
1776         if (err < 0) {
1777             goto bail;
1778         }
1779     }
1780
1781     // Write out the ProGuard file
1782     err = writeProguardFile(bundle, assets);
1783     if (err < 0) {
1784         goto bail;
1785     }
1786
1787     // Write the apk
1788     if (outputAPKFile) {
1789         err = writeAPK(bundle, assets, String8(outputAPKFile));
1790         if (err != NO_ERROR) {
1791             fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
1792             goto bail;
1793         }
1794     }
1795
1796     // If we've been asked to generate a dependency file, we need to finish up here.
1797     // the writeResourceSymbols and writeAPK functions have already written the target
1798     // half of the dependency file, now we need to write the prerequisites. (files that
1799     // the R.java file or .ap_ file depend on)
1800     if (bundle->getGenDependencies()) {
1801         // Now that writeResourceSymbols or writeAPK has taken care of writing
1802         // the targets to our dependency file, we'll write the prereqs
1803         fp = fopen(dependencyFile, "a+");
1804         fprintf(fp, " : ");
1805         bool includeRaw = (outputAPKFile != NULL);
1806         err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
1807         // Also manually add the AndroidManifeset since it's not under res/ or assets/
1808         // and therefore was not added to our pathstores during slurping
1809         fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
1810         fclose(fp);
1811     }
1812
1813     retVal = 0;
1814 bail:
1815     if (SourcePos::hasErrors()) {
1816         SourcePos::printErrors(stderr);
1817     }
1818     return retVal;
1819 }
1820
1821 /*
1822  * Do PNG Crunching
1823  * PRECONDITIONS
1824  *  -S flag points to a source directory containing drawable* folders
1825  *  -C flag points to destination directory. The folder structure in the
1826  *     source directory will be mirrored to the destination (cache) directory
1827  *
1828  * POSTCONDITIONS
1829  *  Destination directory will be updated to match the PNG files in
1830  *  the source directory. 
1831  */
1832 int doCrunch(Bundle* bundle)
1833 {
1834     fprintf(stdout, "Crunching PNG Files in ");
1835     fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
1836     fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
1837
1838     updatePreProcessedCache(bundle);
1839
1840     return NO_ERROR;
1841 }
1842
1843 char CONSOLE_DATA[2925] = {
1844     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1845     32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1846     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1847     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1848     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
1849     86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
1850     62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1851     32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1852     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
1853     81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
1854     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1855     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1856     32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
1857     59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1858     32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1859     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
1860     59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
1861     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1862     10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1863     32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
1864     58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
1865     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
1866     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
1867     47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
1868     121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1869     32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1870     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
1871     81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
1872     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
1873     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1874     32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
1875     59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1876     32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1877     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
1878     59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
1879     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1880     32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1881     32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
1882     70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
1883     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
1884     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1885     32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
1886     81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1887     32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1888     32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
1889     81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
1890     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
1891     32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
1892     59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
1893     60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1894     32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
1895     61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
1896     61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
1897     46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1898     32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
1899     59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
1900     109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
1901     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
1902     67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
1903     59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
1904     61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
1905     32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
1906     59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
1907     73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
1908     59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
1909     32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
1910     59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
1911     46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
1912     97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
1913     46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
1914     119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
1915     59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
1916     32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1917     32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
1918     119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
1919     59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
1920     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1921     32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
1922     81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
1923     87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1924     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
1925     81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
1926     45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
1927     32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1928     32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
1929     81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
1930     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1931     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
1932     59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
1933     59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1934     32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1935     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
1936     81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
1937     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1938     10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1939     32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
1940     81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1941     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
1942     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
1943     81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
1944     32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1945     32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1946     32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
1947     81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
1948     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
1949     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1950     58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
1951     61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1952     32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1953     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
1954     81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
1955     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1956     32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1957     32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
1958     81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1959     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
1960     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
1961     59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
1962     59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1963     32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1964     32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
1965     58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
1966     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
1967     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
1968     61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
1969     59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
1970     32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
1971     32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
1972     32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
1973     61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1974     32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1975     32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
1976     59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
1977     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
1978     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
1979     59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
1980     59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1981     32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1982     32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
1983     32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
1984     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
1985     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1986     46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
1987     46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1988     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1989     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
1990     59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
1991     59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1992     32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1993     32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
1994     32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
1995     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1996     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
1997     59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
1998     59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1999     32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2000     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2001     32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2002     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2003     10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2004     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2005     32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2006     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2007   };