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