• 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 "ResourceTable.h"
9 #include "XMLNode.h"
10 
11 #include <utils/Log.h>
12 #include <utils/threads.h>
13 #include <utils/List.h>
14 #include <utils/Errors.h>
15 
16 #include <fcntl.h>
17 #include <errno.h>
18 
19 using namespace android;
20 
21 /*
22  * Show version info.  All the cool kids do it.
23  */
doVersion(Bundle * bundle)24 int doVersion(Bundle* bundle)
25 {
26     if (bundle->getFileSpecCount() != 0)
27         printf("(ignoring extra arguments)\n");
28     printf("Android Asset Packaging Tool, v0.2\n");
29 
30     return 0;
31 }
32 
33 
34 /*
35  * Open the file read only.  The call fails if the file doesn't exist.
36  *
37  * Returns NULL on failure.
38  */
openReadOnly(const char * fileName)39 ZipFile* openReadOnly(const char* fileName)
40 {
41     ZipFile* zip;
42     status_t result;
43 
44     zip = new ZipFile;
45     result = zip->open(fileName, ZipFile::kOpenReadOnly);
46     if (result != NO_ERROR) {
47         if (result == NAME_NOT_FOUND)
48             fprintf(stderr, "ERROR: '%s' not found\n", fileName);
49         else if (result == PERMISSION_DENIED)
50             fprintf(stderr, "ERROR: '%s' access denied\n", fileName);
51         else
52             fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n",
53                 fileName);
54         delete zip;
55         return NULL;
56     }
57 
58     return zip;
59 }
60 
61 /*
62  * Open the file read-write.  The file will be created if it doesn't
63  * already exist and "okayToCreate" is set.
64  *
65  * Returns NULL on failure.
66  */
openReadWrite(const char * fileName,bool okayToCreate)67 ZipFile* openReadWrite(const char* fileName, bool okayToCreate)
68 {
69     ZipFile* zip = NULL;
70     status_t result;
71     int flags;
72 
73     flags = ZipFile::kOpenReadWrite;
74     if (okayToCreate)
75         flags |= ZipFile::kOpenCreate;
76 
77     zip = new ZipFile;
78     result = zip->open(fileName, flags);
79     if (result != NO_ERROR) {
80         delete zip;
81         zip = NULL;
82         goto bail;
83     }
84 
85 bail:
86     return zip;
87 }
88 
89 
90 /*
91  * Return a short string describing the compression method.
92  */
compressionName(int method)93 const char* compressionName(int method)
94 {
95     if (method == ZipEntry::kCompressStored)
96         return "Stored";
97     else if (method == ZipEntry::kCompressDeflated)
98         return "Deflated";
99     else
100         return "Unknown";
101 }
102 
103 /*
104  * Return the percent reduction in size (0% == no compression).
105  */
calcPercent(long uncompressedLen,long compressedLen)106 int calcPercent(long uncompressedLen, long compressedLen)
107 {
108     if (!uncompressedLen)
109         return 0;
110     else
111         return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5);
112 }
113 
114 /*
115  * Handle the "list" command, which can be a simple file dump or
116  * a verbose listing.
117  *
118  * The verbose listing closely matches the output of the Info-ZIP "unzip"
119  * command.
120  */
doList(Bundle * bundle)121 int doList(Bundle* bundle)
122 {
123     int result = 1;
124     ZipFile* zip = NULL;
125     const ZipEntry* entry;
126     long totalUncLen, totalCompLen;
127     const char* zipFileName;
128 
129     if (bundle->getFileSpecCount() != 1) {
130         fprintf(stderr, "ERROR: specify zip file name (only)\n");
131         goto bail;
132     }
133     zipFileName = bundle->getFileSpecEntry(0);
134 
135     zip = openReadOnly(zipFileName);
136     if (zip == NULL)
137         goto bail;
138 
139     int count, i;
140 
141     if (bundle->getVerbose()) {
142         printf("Archive:  %s\n", zipFileName);
143         printf(
144             " Length   Method    Size  Ratio   Date   Time   CRC-32    Name\n");
145         printf(
146             "--------  ------  ------- -----   ----   ----   ------    ----\n");
147     }
148 
149     totalUncLen = totalCompLen = 0;
150 
151     count = zip->getNumEntries();
152     for (i = 0; i < count; i++) {
153         entry = zip->getEntryByIndex(i);
154         if (bundle->getVerbose()) {
155             char dateBuf[32];
156             time_t when;
157 
158             when = entry->getModWhen();
159             strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M",
160                 localtime(&when));
161 
162             printf("%8ld  %-7.7s %7ld %3d%%  %s  %08lx  %s\n",
163                 (long) entry->getUncompressedLen(),
164                 compressionName(entry->getCompressionMethod()),
165                 (long) entry->getCompressedLen(),
166                 calcPercent(entry->getUncompressedLen(),
167                             entry->getCompressedLen()),
168                 dateBuf,
169                 entry->getCRC32(),
170                 entry->getFileName());
171         } else {
172             printf("%s\n", entry->getFileName());
173         }
174 
175         totalUncLen += entry->getUncompressedLen();
176         totalCompLen += entry->getCompressedLen();
177     }
178 
179     if (bundle->getVerbose()) {
180         printf(
181         "--------          -------  ---                            -------\n");
182         printf("%8ld          %7ld  %2d%%                            %d files\n",
183             totalUncLen,
184             totalCompLen,
185             calcPercent(totalUncLen, totalCompLen),
186             zip->getNumEntries());
187     }
188 
189     if (bundle->getAndroidList()) {
190         AssetManager assets;
191         if (!assets.addAssetPath(String8(zipFileName), NULL)) {
192             fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n");
193             goto bail;
194         }
195 
196         const ResTable& res = assets.getResources(false);
197         if (&res == NULL) {
198             printf("\nNo resource table found.\n");
199         } else {
200             printf("\nResource table:\n");
201             res.print(false);
202         }
203 
204         Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
205                                                    Asset::ACCESS_BUFFER);
206         if (manifestAsset == NULL) {
207             printf("\nNo AndroidManifest.xml found.\n");
208         } else {
209             printf("\nAndroid manifest:\n");
210             ResXMLTree tree;
211             tree.setTo(manifestAsset->getBuffer(true),
212                        manifestAsset->getLength());
213             printXMLBlock(&tree);
214         }
215         delete manifestAsset;
216     }
217 
218     result = 0;
219 
220 bail:
221     delete zip;
222     return result;
223 }
224 
indexOfAttribute(const ResXMLTree & tree,uint32_t attrRes)225 static ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes)
226 {
227     size_t N = tree.getAttributeCount();
228     for (size_t i=0; i<N; i++) {
229         if (tree.getAttributeNameResID(i) == attrRes) {
230             return (ssize_t)i;
231         }
232     }
233     return -1;
234 }
235 
getAttribute(const ResXMLTree & tree,const char * ns,const char * attr,String8 * outError)236 String8 getAttribute(const ResXMLTree& tree, const char* ns,
237                             const char* attr, String8* outError)
238 {
239     ssize_t idx = tree.indexOfAttribute(ns, attr);
240     if (idx < 0) {
241         return String8();
242     }
243     Res_value value;
244     if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
245         if (value.dataType != Res_value::TYPE_STRING) {
246             if (outError != NULL) *outError = "attribute is not a string value";
247             return String8();
248         }
249     }
250     size_t len;
251     const uint16_t* str = tree.getAttributeStringValue(idx, &len);
252     return str ? String8(str, len) : String8();
253 }
254 
getAttribute(const ResXMLTree & tree,uint32_t attrRes,String8 * outError)255 static String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError)
256 {
257     ssize_t idx = indexOfAttribute(tree, attrRes);
258     if (idx < 0) {
259         return String8();
260     }
261     Res_value value;
262     if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
263         if (value.dataType != Res_value::TYPE_STRING) {
264             if (outError != NULL) *outError = "attribute is not a string value";
265             return String8();
266         }
267     }
268     size_t len;
269     const uint16_t* str = tree.getAttributeStringValue(idx, &len);
270     return str ? String8(str, len) : String8();
271 }
272 
getIntegerAttribute(const ResXMLTree & tree,uint32_t attrRes,String8 * outError,int32_t defValue=-1)273 static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes,
274         String8* outError, int32_t defValue = -1)
275 {
276     ssize_t idx = indexOfAttribute(tree, attrRes);
277     if (idx < 0) {
278         return defValue;
279     }
280     Res_value value;
281     if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
282         if (value.dataType < Res_value::TYPE_FIRST_INT
283                 || value.dataType > Res_value::TYPE_LAST_INT) {
284             if (outError != NULL) *outError = "attribute is not an integer value";
285             return defValue;
286         }
287     }
288     return value.data;
289 }
290 
getResolvedAttribute(const ResTable * resTable,const ResXMLTree & tree,uint32_t attrRes,String8 * outError)291 static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree,
292         uint32_t attrRes, String8* outError)
293 {
294     ssize_t idx = indexOfAttribute(tree, attrRes);
295     if (idx < 0) {
296         return String8();
297     }
298     Res_value value;
299     if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
300         if (value.dataType == Res_value::TYPE_STRING) {
301             size_t len;
302             const uint16_t* str = tree.getAttributeStringValue(idx, &len);
303             return str ? String8(str, len) : String8();
304         }
305         resTable->resolveReference(&value, 0);
306         if (value.dataType != Res_value::TYPE_STRING) {
307             if (outError != NULL) *outError = "attribute is not a string value";
308             return String8();
309         }
310     }
311     size_t len;
312     const Res_value* value2 = &value;
313     const char16_t* str = const_cast<ResTable*>(resTable)->valueToString(value2, 0, NULL, &len);
314     return str ? String8(str, len) : String8();
315 }
316 
317 // These are attribute resource constants for the platform, as found
318 // in android.R.attr
319 enum {
320     NAME_ATTR = 0x01010003,
321     VERSION_CODE_ATTR = 0x0101021b,
322     VERSION_NAME_ATTR = 0x0101021c,
323     LABEL_ATTR = 0x01010001,
324     ICON_ATTR = 0x01010002,
325     MIN_SDK_VERSION_ATTR = 0x0101020c,
326     MAX_SDK_VERSION_ATTR = 0x01010271,
327     REQ_TOUCH_SCREEN_ATTR = 0x01010227,
328     REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
329     REQ_HARD_KEYBOARD_ATTR = 0x01010229,
330     REQ_NAVIGATION_ATTR = 0x0101022a,
331     REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
332     TARGET_SDK_VERSION_ATTR = 0x01010270,
333     TEST_ONLY_ATTR = 0x01010272,
334     DENSITY_ATTR = 0x0101026c,
335     GL_ES_VERSION_ATTR = 0x01010281,
336     SMALL_SCREEN_ATTR = 0x01010284,
337     NORMAL_SCREEN_ATTR = 0x01010285,
338     LARGE_SCREEN_ATTR = 0x01010286,
339     REQUIRED_ATTR = 0x0101028e,
340 };
341 
getComponentName(String8 & pkgName,String8 & componentName)342 const char *getComponentName(String8 &pkgName, String8 &componentName) {
343     ssize_t idx = componentName.find(".");
344     String8 retStr(pkgName);
345     if (idx == 0) {
346         retStr += componentName;
347     } else if (idx < 0) {
348         retStr += ".";
349         retStr += componentName;
350     } else {
351         return componentName.string();
352     }
353     return retStr.string();
354 }
355 
356 /*
357  * Handle the "dump" command, to extract select data from an archive.
358  */
doDump(Bundle * bundle)359 int doDump(Bundle* bundle)
360 {
361     status_t result = UNKNOWN_ERROR;
362     Asset* asset = NULL;
363 
364     if (bundle->getFileSpecCount() < 1) {
365         fprintf(stderr, "ERROR: no dump option specified\n");
366         return 1;
367     }
368 
369     if (bundle->getFileSpecCount() < 2) {
370         fprintf(stderr, "ERROR: no dump file specified\n");
371         return 1;
372     }
373 
374     const char* option = bundle->getFileSpecEntry(0);
375     const char* filename = bundle->getFileSpecEntry(1);
376 
377     AssetManager assets;
378     void* assetsCookie;
379     if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
380         fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
381         return 1;
382     }
383 
384     const ResTable& res = assets.getResources(false);
385     if (&res == NULL) {
386         fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
387         goto bail;
388     }
389 
390     if (strcmp("resources", option) == 0) {
391         res.print(bundle->getValues());
392 
393     } else if (strcmp("xmltree", option) == 0) {
394         if (bundle->getFileSpecCount() < 3) {
395             fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
396             goto bail;
397         }
398 
399         for (int i=2; i<bundle->getFileSpecCount(); i++) {
400             const char* resname = bundle->getFileSpecEntry(i);
401             ResXMLTree tree;
402             asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
403             if (asset == NULL) {
404                 fprintf(stderr, "ERROR: dump failed because resource %p found\n", resname);
405                 goto bail;
406             }
407 
408             if (tree.setTo(asset->getBuffer(true),
409                            asset->getLength()) != NO_ERROR) {
410                 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
411                 goto bail;
412             }
413             tree.restart();
414             printXMLBlock(&tree);
415             delete asset;
416             asset = NULL;
417         }
418 
419     } else if (strcmp("xmlstrings", option) == 0) {
420         if (bundle->getFileSpecCount() < 3) {
421             fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
422             goto bail;
423         }
424 
425         for (int i=2; i<bundle->getFileSpecCount(); i++) {
426             const char* resname = bundle->getFileSpecEntry(i);
427             ResXMLTree tree;
428             asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
429             if (asset == NULL) {
430                 fprintf(stderr, "ERROR: dump failed because resource %p found\n", resname);
431                 goto bail;
432             }
433 
434             if (tree.setTo(asset->getBuffer(true),
435                            asset->getLength()) != NO_ERROR) {
436                 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
437                 goto bail;
438             }
439             printStringPool(&tree.getStrings());
440             delete asset;
441             asset = NULL;
442         }
443 
444     } else {
445         ResXMLTree tree;
446         asset = assets.openNonAsset("AndroidManifest.xml",
447                                             Asset::ACCESS_BUFFER);
448         if (asset == NULL) {
449             fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
450             goto bail;
451         }
452 
453         if (tree.setTo(asset->getBuffer(true),
454                        asset->getLength()) != NO_ERROR) {
455             fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
456             goto bail;
457         }
458         tree.restart();
459 
460         if (strcmp("permissions", option) == 0) {
461             size_t len;
462             ResXMLTree::event_code_t code;
463             int depth = 0;
464             while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
465                 if (code == ResXMLTree::END_TAG) {
466                     depth--;
467                     continue;
468                 }
469                 if (code != ResXMLTree::START_TAG) {
470                     continue;
471                 }
472                 depth++;
473                 String8 tag(tree.getElementName(&len));
474                 //printf("Depth %d tag %s\n", depth, tag.string());
475                 if (depth == 1) {
476                     if (tag != "manifest") {
477                         fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
478                         goto bail;
479                     }
480                     String8 pkg = getAttribute(tree, NULL, "package", NULL);
481                     printf("package: %s\n", pkg.string());
482                 } else if (depth == 2 && tag == "permission") {
483                     String8 error;
484                     String8 name = getAttribute(tree, NAME_ATTR, &error);
485                     if (error != "") {
486                         fprintf(stderr, "ERROR: %s\n", error.string());
487                         goto bail;
488                     }
489                     printf("permission: %s\n", name.string());
490                 } else if (depth == 2 && tag == "uses-permission") {
491                     String8 error;
492                     String8 name = getAttribute(tree, NAME_ATTR, &error);
493                     if (error != "") {
494                         fprintf(stderr, "ERROR: %s\n", error.string());
495                         goto bail;
496                     }
497                     printf("uses-permission: %s\n", name.string());
498                 }
499             }
500         } else if (strcmp("badging", option) == 0) {
501             size_t len;
502             ResXMLTree::event_code_t code;
503             int depth = 0;
504             String8 error;
505             bool withinActivity = false;
506             bool isMainActivity = false;
507             bool isLauncherActivity = false;
508             bool isSearchable = false;
509             bool withinApplication = false;
510             bool withinReceiver = false;
511             bool withinService = false;
512             bool withinIntentFilter = false;
513             bool hasMainActivity = false;
514             bool hasOtherActivities = false;
515             bool hasOtherReceivers = false;
516             bool hasOtherServices = false;
517             bool hasWallpaperService = false;
518             bool hasImeService = false;
519             bool hasWidgetReceivers = false;
520             bool hasIntentFilter = false;
521             bool actMainActivity = false;
522             bool actWidgetReceivers = false;
523             bool actImeService = false;
524             bool actWallpaperService = false;
525             bool specCameraFeature = false;
526             bool hasCameraPermission = false;
527             int targetSdk = 0;
528             int smallScreen = 1;
529             int normalScreen = 1;
530             int largeScreen = 1;
531             String8 pkg;
532             String8 activityName;
533             String8 activityLabel;
534             String8 activityIcon;
535             String8 receiverName;
536             String8 serviceName;
537             while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
538                 if (code == ResXMLTree::END_TAG) {
539                     depth--;
540                     if (depth < 2) {
541                         withinApplication = false;
542                     } else if (depth < 3) {
543                         if (withinActivity && isMainActivity && isLauncherActivity) {
544                             const char *aName = getComponentName(pkg, activityName);
545                             if (aName != NULL) {
546                                 printf("launchable activity name='%s'", aName);
547                             }
548                             printf("label='%s' icon='%s'\n",
549                                     activityLabel.string(),
550                                     activityIcon.string());
551                         }
552                         if (!hasIntentFilter) {
553                             hasOtherActivities |= withinActivity;
554                             hasOtherReceivers |= withinReceiver;
555                             hasOtherServices |= withinService;
556                         }
557                         withinActivity = false;
558                         withinService = false;
559                         withinReceiver = false;
560                         hasIntentFilter = false;
561                         isMainActivity = isLauncherActivity = false;
562                     } else if (depth < 4) {
563                         if (withinIntentFilter) {
564                             if (withinActivity) {
565                                 hasMainActivity |= actMainActivity;
566                                 hasOtherActivities |= !actMainActivity;
567                             } else if (withinReceiver) {
568                                 hasWidgetReceivers |= actWidgetReceivers;
569                                 hasOtherReceivers |= !actWidgetReceivers;
570                             } else if (withinService) {
571                                 hasImeService |= actImeService;
572                                 hasWallpaperService |= actWallpaperService;
573                                 hasOtherServices |= (!actImeService && !actWallpaperService);
574                             }
575                         }
576                         withinIntentFilter = false;
577                     }
578                     continue;
579                 }
580                 if (code != ResXMLTree::START_TAG) {
581                     continue;
582                 }
583                 depth++;
584                 String8 tag(tree.getElementName(&len));
585                 //printf("Depth %d,  %s\n", depth, tag.string());
586                 if (depth == 1) {
587                     if (tag != "manifest") {
588                         fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
589                         goto bail;
590                     }
591                     pkg = getAttribute(tree, NULL, "package", NULL);
592                     printf("package: name='%s' ", pkg.string());
593                     int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
594                     if (error != "") {
595                         fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
596                         goto bail;
597                     }
598                     if (versionCode > 0) {
599                         printf("versionCode='%d' ", versionCode);
600                     } else {
601                         printf("versionCode='' ");
602                     }
603                     String8 versionName = getAttribute(tree, VERSION_NAME_ATTR, &error);
604                     if (error != "") {
605                         fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
606                         goto bail;
607                     }
608                     printf("versionName='%s'\n", versionName.string());
609                 } else if (depth == 2) {
610                     withinApplication = false;
611                     if (tag == "application") {
612                         withinApplication = true;
613                         String8 label = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
614                          if (error != "") {
615                              fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string());
616                              goto bail;
617                         }
618                         printf("application: label='%s' ", label.string());
619                         String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
620                         if (error != "") {
621                             fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
622                             goto bail;
623                         }
624                         printf("icon='%s'\n", icon.string());
625                         int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
626                         if (error != "") {
627                             fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
628                             goto bail;
629                         }
630                         if (testOnly != 0) {
631                             printf("testOnly='%d'\n", testOnly);
632                         }
633                     } else if (tag == "uses-sdk") {
634                         int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
635                         if (error != "") {
636                             error = "";
637                             String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
638                             if (error != "") {
639                                 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
640                                         error.string());
641                                 goto bail;
642                             }
643                             if (name == "Donut") targetSdk = 4;
644                             printf("sdkVersion:'%s'\n", name.string());
645                         } else if (code != -1) {
646                             targetSdk = code;
647                             printf("sdkVersion:'%d'\n", code);
648                         }
649                         code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
650                         if (code != -1) {
651                             printf("maxSdkVersion:'%d'\n", code);
652                         }
653                         code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
654                         if (error != "") {
655                             error = "";
656                             String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
657                             if (error != "") {
658                                 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
659                                         error.string());
660                                 goto bail;
661                             }
662                             if (name == "Donut" && targetSdk < 4) targetSdk = 4;
663                             printf("targetSdkVersion:'%s'\n", name.string());
664                         } else if (code != -1) {
665                             if (targetSdk < code) {
666                                 targetSdk = code;
667                             }
668                             printf("targetSdkVersion:'%d'\n", code);
669                         }
670                     } else if (tag == "uses-configuration") {
671                         int32_t reqTouchScreen = getIntegerAttribute(tree,
672                                 REQ_TOUCH_SCREEN_ATTR, NULL, 0);
673                         int32_t reqKeyboardType = getIntegerAttribute(tree,
674                                 REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
675                         int32_t reqHardKeyboard = getIntegerAttribute(tree,
676                                 REQ_HARD_KEYBOARD_ATTR, NULL, 0);
677                         int32_t reqNavigation = getIntegerAttribute(tree,
678                                 REQ_NAVIGATION_ATTR, NULL, 0);
679                         int32_t reqFiveWayNav = getIntegerAttribute(tree,
680                                 REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
681                         printf("uses-configuation:");
682                         if (reqTouchScreen != 0) {
683                             printf(" reqTouchScreen='%d'", reqTouchScreen);
684                         }
685                         if (reqKeyboardType != 0) {
686                             printf(" reqKeyboardType='%d'", reqKeyboardType);
687                         }
688                         if (reqHardKeyboard != 0) {
689                             printf(" reqHardKeyboard='%d'", reqHardKeyboard);
690                         }
691                         if (reqNavigation != 0) {
692                             printf(" reqNavigation='%d'", reqNavigation);
693                         }
694                         if (reqFiveWayNav != 0) {
695                             printf(" reqFiveWayNav='%d'", reqFiveWayNav);
696                         }
697                         printf("\n");
698                     } else if (tag == "supports-density") {
699                         int32_t dens = getIntegerAttribute(tree, DENSITY_ATTR, &error);
700                         if (error != "") {
701                             fprintf(stderr, "ERROR getting 'android:density' attribute: %s\n",
702                                     error.string());
703                             goto bail;
704                         }
705                         printf("supports-density:'%d'\n", dens);
706                     } else if (tag == "supports-screens") {
707                         smallScreen = getIntegerAttribute(tree,
708                                 SMALL_SCREEN_ATTR, NULL, 1);
709                         normalScreen = getIntegerAttribute(tree,
710                                 NORMAL_SCREEN_ATTR, NULL, 1);
711                         largeScreen = getIntegerAttribute(tree,
712                                 LARGE_SCREEN_ATTR, NULL, 1);
713                     } else if (tag == "uses-feature") {
714                         String8 name = getAttribute(tree, NAME_ATTR, &error);
715 
716                         if (name != "" && error == "") {
717                             int req = getIntegerAttribute(tree,
718                                     REQUIRED_ATTR, NULL, 1);
719                             if (name == "android.hardware.camera") {
720                                 specCameraFeature = true;
721                             }
722                             printf("uses-feature%s:'%s'\n",
723                                     req ? "" : "-not-required", name.string());
724                         } else {
725                             int vers = getIntegerAttribute(tree,
726                                     GL_ES_VERSION_ATTR, &error);
727                             if (error == "") {
728                                 printf("uses-gl-es:'0x%x'\n", vers);
729                             }
730                         }
731                     } else if (tag == "uses-permission") {
732                         String8 name = getAttribute(tree, NAME_ATTR, &error);
733                         if (name != "" && error == "") {
734                             if (name == "android.permission.CAMERA") {
735                                 hasCameraPermission = true;
736                             }
737                             printf("uses-permission:'%s'\n", name.string());
738                         } else {
739                             fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
740                                     error.string());
741                             goto bail;
742                         }
743                     }
744                 } else if (depth == 3 && withinApplication) {
745                     withinActivity = false;
746                     withinReceiver = false;
747                     withinService = false;
748                     hasIntentFilter = false;
749                     if(tag == "activity") {
750                         withinActivity = true;
751                         activityName = getAttribute(tree, NAME_ATTR, &error);
752                         if (error != "") {
753                             fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
754                             goto bail;
755                         }
756 
757                         activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
758                         if (error != "") {
759                             fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string());
760                             goto bail;
761                         }
762 
763                         activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
764                         if (error != "") {
765                             fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
766                             goto bail;
767                         }
768                     } else if (tag == "uses-library") {
769                         String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
770                         if (error != "") {
771                             fprintf(stderr, "ERROR getting 'android:name' attribute for uses-library: %s\n", error.string());
772                             goto bail;
773                         }
774                         int req = getIntegerAttribute(tree,
775                                 REQUIRED_ATTR, NULL, 1);
776                         printf("uses-library%s:'%s'\n",
777                                 req ? "" : "-not-required", libraryName.string());
778                     } else if (tag == "receiver") {
779                         withinReceiver = true;
780                         receiverName = getAttribute(tree, NAME_ATTR, &error);
781 
782                         if (error != "") {
783                             fprintf(stderr, "ERROR getting 'android:name' attribute for receiver: %s\n", error.string());
784                             goto bail;
785                         }
786                     } else if (tag == "service") {
787                         withinService = true;
788                         serviceName = getAttribute(tree, NAME_ATTR, &error);
789 
790                         if (error != "") {
791                             fprintf(stderr, "ERROR getting 'android:name' attribute for service: %s\n", error.string());
792                             goto bail;
793                         }
794                     }
795                 } else if ((depth == 4) && (tag == "intent-filter")) {
796                     hasIntentFilter = true;
797                     withinIntentFilter = true;
798                     actMainActivity = actWidgetReceivers = actImeService = actWallpaperService = false;
799                 } else if ((depth == 5) && withinIntentFilter){
800                     String8 action;
801                     if (tag == "action") {
802                         action = getAttribute(tree, NAME_ATTR, &error);
803                         if (error != "") {
804                             fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
805                             goto bail;
806                         }
807                         if (withinActivity) {
808                             if (action == "android.intent.action.MAIN") {
809                                 isMainActivity = true;
810                                 actMainActivity = true;
811                             }
812                         } else if (withinReceiver) {
813                             if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
814                                 actWidgetReceivers = true;
815                             }
816                         } else if (withinService) {
817                             if (action == "android.view.InputMethod") {
818                                 actImeService = true;
819                             } else if (action == "android.service.wallpaper.WallpaperService") {
820                                 actWallpaperService = true;
821                             }
822                         }
823                         if (action == "android.intent.action.SEARCH") {
824                             isSearchable = true;
825                         }
826                     }
827 
828                     if (tag == "category") {
829                         String8 category = getAttribute(tree, NAME_ATTR, &error);
830                         if (error != "") {
831                             fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string());
832                             goto bail;
833                         }
834                         if (withinActivity) {
835                             if (category == "android.intent.category.LAUNCHER") {
836                                 isLauncherActivity = true;
837                             }
838                         }
839                     }
840                 }
841             }
842 
843             if (!specCameraFeature && hasCameraPermission) {
844                 // For applications that have not explicitly stated their
845                 // camera feature requirements, but have requested the camera
846                 // permission, we are going to give them compatibility treatment
847                 // of requiring the equivalent to original android devices.
848                 printf("uses-feature:'android.hardware.camera'\n");
849                 printf("uses-feature:'android.hardware.camera.autofocus'\n");
850             }
851 
852             if (hasMainActivity) {
853                 printf("main\n");
854             }
855             if (hasWidgetReceivers) {
856                 printf("app-widget\n");
857             }
858             if (hasImeService) {
859                 printf("ime\n");
860             }
861             if (hasWallpaperService) {
862                 printf("wallpaper\n");
863             }
864             if (hasOtherActivities) {
865                 printf("other-activities\n");
866             }
867             if (isSearchable) {
868                 printf("search\n");
869             }
870             if (hasOtherReceivers) {
871                 printf("other-receivers\n");
872             }
873             if (hasOtherServices) {
874                 printf("other-services\n");
875             }
876 
877             // Determine default values for any unspecified screen sizes,
878             // based on the target SDK of the package.  As of 4 (donut)
879             // the screen size support was introduced, so all default to
880             // enabled.
881             if (smallScreen > 0) {
882                 smallScreen = targetSdk >= 4 ? -1 : 0;
883             }
884             if (normalScreen > 0) {
885                 normalScreen = -1;
886             }
887             if (largeScreen > 0) {
888                 largeScreen = targetSdk >= 4 ? -1 : 0;
889             }
890             printf("supports-screens:");
891             if (smallScreen != 0) printf(" 'small'");
892             if (normalScreen != 0) printf(" 'normal'");
893             if (largeScreen != 0) printf(" 'large'");
894             printf("\n");
895 
896             printf("locales:");
897             Vector<String8> locales;
898             res.getLocales(&locales);
899             const size_t NL = locales.size();
900             for (size_t i=0; i<NL; i++) {
901                 const char* localeStr =  locales[i].string();
902                 if (localeStr == NULL || strlen(localeStr) == 0) {
903                     localeStr = "--_--";
904                 }
905                 printf(" '%s'", localeStr);
906             }
907             printf("\n");
908 
909             Vector<ResTable_config> configs;
910             res.getConfigurations(&configs);
911             SortedVector<int> densities;
912             const size_t NC = configs.size();
913             for (size_t i=0; i<NC; i++) {
914                 int dens = configs[i].density;
915                 if (dens == 0) dens = 160;
916                 densities.add(dens);
917             }
918 
919             printf("densities:");
920             const size_t ND = densities.size();
921             for (size_t i=0; i<ND; i++) {
922                 printf(" '%d'", densities[i]);
923             }
924             printf("\n");
925 
926             AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
927             if (dir != NULL) {
928                 if (dir->getFileCount() > 0) {
929                     printf("native-code:");
930                     for (size_t i=0; i<dir->getFileCount(); i++) {
931                         printf(" '%s'", dir->getFileName(i).string());
932                     }
933                     printf("\n");
934                 }
935                 delete dir;
936             }
937         } else if (strcmp("configurations", option) == 0) {
938             Vector<ResTable_config> configs;
939             res.getConfigurations(&configs);
940             const size_t N = configs.size();
941             for (size_t i=0; i<N; i++) {
942                 printf("%s\n", configs[i].toString().string());
943             }
944         } else {
945             fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
946             goto bail;
947         }
948     }
949 
950     result = NO_ERROR;
951 
952 bail:
953     if (asset) {
954         delete asset;
955     }
956     return (result != NO_ERROR);
957 }
958 
959 
960 /*
961  * Handle the "add" command, which wants to add files to a new or
962  * pre-existing archive.
963  */
doAdd(Bundle * bundle)964 int doAdd(Bundle* bundle)
965 {
966     ZipFile* zip = NULL;
967     status_t result = UNKNOWN_ERROR;
968     const char* zipFileName;
969 
970     if (bundle->getUpdate()) {
971         /* avoid confusion */
972         fprintf(stderr, "ERROR: can't use '-u' with add\n");
973         goto bail;
974     }
975 
976     if (bundle->getFileSpecCount() < 1) {
977         fprintf(stderr, "ERROR: must specify zip file name\n");
978         goto bail;
979     }
980     zipFileName = bundle->getFileSpecEntry(0);
981 
982     if (bundle->getFileSpecCount() < 2) {
983         fprintf(stderr, "NOTE: nothing to do\n");
984         goto bail;
985     }
986 
987     zip = openReadWrite(zipFileName, true);
988     if (zip == NULL) {
989         fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
990         goto bail;
991     }
992 
993     for (int i = 1; i < bundle->getFileSpecCount(); i++) {
994         const char* fileName = bundle->getFileSpecEntry(i);
995 
996         if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
997             printf(" '%s'... (from gzip)\n", fileName);
998             result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
999         } else {
1000             if (bundle->getJunkPath()) {
1001                 String8 storageName = String8(fileName).getPathLeaf();
1002                 printf(" '%s' as '%s'...\n", fileName, storageName.string());
1003                 result = zip->add(fileName, storageName.string(),
1004                                   bundle->getCompressionMethod(), NULL);
1005             } else {
1006                 printf(" '%s'...\n", fileName);
1007                 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
1008             }
1009         }
1010         if (result != NO_ERROR) {
1011             fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
1012             if (result == NAME_NOT_FOUND)
1013                 fprintf(stderr, ": file not found\n");
1014             else if (result == ALREADY_EXISTS)
1015                 fprintf(stderr, ": already exists in archive\n");
1016             else
1017                 fprintf(stderr, "\n");
1018             goto bail;
1019         }
1020     }
1021 
1022     result = NO_ERROR;
1023 
1024 bail:
1025     delete zip;
1026     return (result != NO_ERROR);
1027 }
1028 
1029 
1030 /*
1031  * Delete files from an existing archive.
1032  */
doRemove(Bundle * bundle)1033 int doRemove(Bundle* bundle)
1034 {
1035     ZipFile* zip = NULL;
1036     status_t result = UNKNOWN_ERROR;
1037     const char* zipFileName;
1038 
1039     if (bundle->getFileSpecCount() < 1) {
1040         fprintf(stderr, "ERROR: must specify zip file name\n");
1041         goto bail;
1042     }
1043     zipFileName = bundle->getFileSpecEntry(0);
1044 
1045     if (bundle->getFileSpecCount() < 2) {
1046         fprintf(stderr, "NOTE: nothing to do\n");
1047         goto bail;
1048     }
1049 
1050     zip = openReadWrite(zipFileName, false);
1051     if (zip == NULL) {
1052         fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
1053             zipFileName);
1054         goto bail;
1055     }
1056 
1057     for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1058         const char* fileName = bundle->getFileSpecEntry(i);
1059         ZipEntry* entry;
1060 
1061         entry = zip->getEntryByName(fileName);
1062         if (entry == NULL) {
1063             printf(" '%s' NOT FOUND\n", fileName);
1064             continue;
1065         }
1066 
1067         result = zip->remove(entry);
1068 
1069         if (result != NO_ERROR) {
1070             fprintf(stderr, "Unable to delete '%s' from '%s'\n",
1071                 bundle->getFileSpecEntry(i), zipFileName);
1072             goto bail;
1073         }
1074     }
1075 
1076     /* update the archive */
1077     zip->flush();
1078 
1079 bail:
1080     delete zip;
1081     return (result != NO_ERROR);
1082 }
1083 
1084 
1085 /*
1086  * Package up an asset directory and associated application files.
1087  */
doPackage(Bundle * bundle)1088 int doPackage(Bundle* bundle)
1089 {
1090     const char* outputAPKFile;
1091     int retVal = 1;
1092     status_t err;
1093     sp<AaptAssets> assets;
1094     int N;
1095 
1096     // -c zz_ZZ means do pseudolocalization
1097     ResourceFilter filter;
1098     err = filter.parse(bundle->getConfigurations());
1099     if (err != NO_ERROR) {
1100         goto bail;
1101     }
1102     if (filter.containsPseudo()) {
1103         bundle->setPseudolocalize(true);
1104     }
1105 
1106     N = bundle->getFileSpecCount();
1107     if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
1108             && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDir() == NULL) {
1109         fprintf(stderr, "ERROR: no input files\n");
1110         goto bail;
1111     }
1112 
1113     outputAPKFile = bundle->getOutputAPKFile();
1114 
1115     // Make sure the filenames provided exist and are of the appropriate type.
1116     if (outputAPKFile) {
1117         FileType type;
1118         type = getFileType(outputAPKFile);
1119         if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
1120             fprintf(stderr,
1121                 "ERROR: output file '%s' exists but is not regular file\n",
1122                 outputAPKFile);
1123             goto bail;
1124         }
1125     }
1126 
1127     // Load the assets.
1128     assets = new AaptAssets();
1129     err = assets->slurpFromArgs(bundle);
1130     if (err < 0) {
1131         goto bail;
1132     }
1133 
1134     if (bundle->getVerbose()) {
1135         assets->print();
1136     }
1137 
1138     // If they asked for any files that need to be compiled, do so.
1139     if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
1140         err = buildResources(bundle, assets);
1141         if (err != 0) {
1142             goto bail;
1143         }
1144     }
1145 
1146     // At this point we've read everything and processed everything.  From here
1147     // on out it's just writing output files.
1148     if (SourcePos::hasErrors()) {
1149         goto bail;
1150     }
1151 
1152     // Write out R.java constants
1153     if (assets->getPackage() == assets->getSymbolsPrivatePackage()) {
1154         err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
1155         if (err < 0) {
1156             goto bail;
1157         }
1158     } else {
1159         err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
1160         if (err < 0) {
1161             goto bail;
1162         }
1163         err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
1164         if (err < 0) {
1165             goto bail;
1166         }
1167     }
1168 
1169     // Write out the ProGuard file
1170     err = writeProguardFile(bundle, assets);
1171     if (err < 0) {
1172         goto bail;
1173     }
1174 
1175     // Write the apk
1176     if (outputAPKFile) {
1177         err = writeAPK(bundle, assets, String8(outputAPKFile));
1178         if (err != NO_ERROR) {
1179             fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
1180             goto bail;
1181         }
1182     }
1183 
1184     retVal = 0;
1185 bail:
1186     if (SourcePos::hasErrors()) {
1187         SourcePos::printErrors(stderr);
1188     }
1189     return retVal;
1190 }
1191