2 // Copyright 2006 The Android Open Source Project 
   4 // Android Asset Packaging Tool main entry point. 
   8 #include "ResourceFilter.h" 
   9 #include "ResourceTable.h" 
  12 #include <utils/Log.h> 
  13 #include <utils/threads.h> 
  14 #include <utils/List.h> 
  15 #include <utils/Errors.h> 
  20 using namespace android
; 
  23  * Show version info.  All the cool kids do it. 
  25 int doVersion(Bundle
* bundle
) 
  27     if (bundle
->getFileSpecCount() != 0) 
  28         printf("(ignoring extra arguments)\n"); 
  29     printf("Android Asset Packaging Tool, v0.2\n"); 
  36  * Open the file read only.  The call fails if the file doesn't exist. 
  38  * Returns NULL on failure. 
  40 ZipFile
* openReadOnly(const char* fileName
) 
  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
); 
  53             fprintf(stderr
, "ERROR: failed opening '%s' as Zip file\n", 
  63  * Open the file read-write.  The file will be created if it doesn't 
  64  * already exist and "okayToCreate" is set. 
  66  * Returns NULL on failure. 
  68 ZipFile
* openReadWrite(const char* fileName
, bool okayToCreate
) 
  74     flags 
= ZipFile::kOpenReadWrite
; 
  76         flags 
|= ZipFile::kOpenCreate
; 
  79     result 
= zip
->open(fileName
, flags
); 
  80     if (result 
!= NO_ERROR
) { 
  92  * Return a short string describing the compression method. 
  94 const char* compressionName(int method
) 
  96     if (method 
== ZipEntry::kCompressStored
) 
  98     else if (method 
== ZipEntry::kCompressDeflated
) 
 105  * Return the percent reduction in size (0% == no compression). 
 107 int calcPercent(long uncompressedLen
, long compressedLen
) 
 109     if (!uncompressedLen
) 
 112         return (int) (100.0 - (compressedLen 
* 100.0) / uncompressedLen 
+ 0.5); 
 116  * Handle the "list" command, which can be a simple file dump or 
 119  * The verbose listing closely matches the output of the Info-ZIP "unzip" 
 122 int doList(Bundle
* bundle
) 
 126     const ZipEntry
* entry
; 
 127     long totalUncLen
, totalCompLen
; 
 128     const char* zipFileName
; 
 130     if (bundle
->getFileSpecCount() != 1) { 
 131         fprintf(stderr
, "ERROR: specify zip file name (only)\n"); 
 134     zipFileName 
= bundle
->getFileSpecEntry(0); 
 136     zip 
= openReadOnly(zipFileName
); 
 142     if (bundle
->getVerbose()) { 
 143         printf("Archive:  %s\n", zipFileName
); 
 145             " Length   Method    Size  Ratio   Offset      Date  Time  CRC-32    Name\n"); 
 147             "--------  ------  ------- -----  -------      ----  ----  ------    ----\n"); 
 150     totalUncLen 
= totalCompLen 
= 0; 
 152     count 
= zip
->getNumEntries(); 
 153     for (i 
= 0; i 
< count
; i
++) { 
 154         entry 
= zip
->getEntryByIndex(i
); 
 155         if (bundle
->getVerbose()) { 
 159             when 
= entry
->getModWhen(); 
 160             strftime(dateBuf
, sizeof(dateBuf
), "%m-%d-%y %H:%M", 
 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(), 
 172                 entry
->getFileName()); 
 174             printf("%s\n", entry
->getFileName()); 
 177         totalUncLen 
+= entry
->getUncompressedLen(); 
 178         totalCompLen 
+= entry
->getCompressedLen(); 
 181     if (bundle
->getVerbose()) { 
 183         "--------          -------  ---                            -------\n"); 
 184         printf("%8ld          %7ld  %2d%%                            %d files\n", 
 187             calcPercent(totalUncLen
, totalCompLen
), 
 188             zip
->getNumEntries()); 
 191     if (bundle
->getAndroidList()) { 
 193         if (!assets
.addAssetPath(String8(zipFileName
), NULL
)) { 
 194             fprintf(stderr
, "ERROR: list -a failed because assets could not be loaded\n"); 
 198         const ResTable
& res 
= assets
.getResources(false); 
 200             printf("\nNo resource table found.\n"); 
 202 #ifndef HAVE_ANDROID_OS 
 203             printf("\nResource table:\n"); 
 208         Asset
* manifestAsset 
= assets
.openNonAsset("AndroidManifest.xml", 
 209                                                    Asset::ACCESS_BUFFER
); 
 210         if (manifestAsset 
== NULL
) { 
 211             printf("\nNo AndroidManifest.xml found.\n"); 
 213             printf("\nAndroid manifest:\n"); 
 215             tree
.setTo(manifestAsset
->getBuffer(true), 
 216                        manifestAsset
->getLength()); 
 217             printXMLBlock(&tree
); 
 219         delete manifestAsset
; 
 229 static ssize_t 
indexOfAttribute(const ResXMLTree
& tree
, uint32_t attrRes
) 
 231     size_t N 
= tree
.getAttributeCount(); 
 232     for (size_t i
=0; i
<N
; i
++) { 
 233         if (tree
.getAttributeNameResID(i
) == attrRes
) { 
 240 String8 
getAttribute(const ResXMLTree
& tree
, const char* ns
, 
 241                             const char* attr
, String8
* outError
) 
 243     ssize_t idx 
= tree
.indexOfAttribute(ns
, attr
); 
 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"; 
 255     const uint16_t* str 
= tree
.getAttributeStringValue(idx
, &len
); 
 256     return str 
? String8(str
, len
) : String8(); 
 259 static String8 
getAttribute(const ResXMLTree
& tree
, uint32_t attrRes
, String8
* outError
) 
 261     ssize_t idx 
= indexOfAttribute(tree
, attrRes
); 
 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"; 
 273     const uint16_t* str 
= tree
.getAttributeStringValue(idx
, &len
); 
 274     return str 
? String8(str
, len
) : String8(); 
 277 static int32_t getIntegerAttribute(const ResXMLTree
& tree
, uint32_t attrRes
, 
 278         String8
* outError
, int32_t defValue 
= -1) 
 280     ssize_t idx 
= indexOfAttribute(tree
, attrRes
); 
 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"; 
 295 static int32_t getResolvedIntegerAttribute(const ResTable
* resTable
, const ResXMLTree
& tree
, 
 296         uint32_t attrRes
, String8
* outError
, int32_t defValue 
= -1) 
 298     ssize_t idx 
= indexOfAttribute(tree
, attrRes
); 
 303     if (tree
.getAttributeValue(idx
, &value
) != NO_ERROR
) { 
 304         if (value
.dataType 
== Res_value::TYPE_REFERENCE
) { 
 305             resTable
->resolveReference(&value
, 0); 
 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"; 
 316 static String8 
getResolvedAttribute(const ResTable
* resTable
, const ResXMLTree
& tree
, 
 317         uint32_t attrRes
, String8
* outError
) 
 319     ssize_t idx 
= indexOfAttribute(tree
, attrRes
); 
 324     if (tree
.getAttributeValue(idx
, &value
) != NO_ERROR
) { 
 325         if (value
.dataType 
== Res_value::TYPE_STRING
) { 
 327             const uint16_t* str 
= tree
.getAttributeStringValue(idx
, &len
); 
 328             return str 
? String8(str
, len
) : String8(); 
 330         resTable
->resolveReference(&value
, 0); 
 331         if (value
.dataType 
!= Res_value::TYPE_STRING
) { 
 332             if (outError 
!= NULL
) *outError 
= "attribute is not a string value"; 
 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(); 
 342 // These are attribute resource constants for the platform, as found 
 345     LABEL_ATTR 
= 0x01010001, 
 346     ICON_ATTR 
= 0x01010002, 
 347     NAME_ATTR 
= 0x01010003, 
 348     VERSION_CODE_ATTR 
= 0x0101021b, 
 349     VERSION_NAME_ATTR 
= 0x0101021c, 
 350     SCREEN_ORIENTATION_ATTR 
= 0x0101001e, 
 351     MIN_SDK_VERSION_ATTR 
= 0x0101020c, 
 352     MAX_SDK_VERSION_ATTR 
= 0x01010271, 
 353     REQ_TOUCH_SCREEN_ATTR 
= 0x01010227, 
 354     REQ_KEYBOARD_TYPE_ATTR 
= 0x01010228, 
 355     REQ_HARD_KEYBOARD_ATTR 
= 0x01010229, 
 356     REQ_NAVIGATION_ATTR 
= 0x0101022a, 
 357     REQ_FIVE_WAY_NAV_ATTR 
= 0x01010232, 
 358     TARGET_SDK_VERSION_ATTR 
= 0x01010270, 
 359     TEST_ONLY_ATTR 
= 0x01010272, 
 360     ANY_DENSITY_ATTR 
= 0x0101026c, 
 361     GL_ES_VERSION_ATTR 
= 0x01010281, 
 362     SMALL_SCREEN_ATTR 
= 0x01010284, 
 363     NORMAL_SCREEN_ATTR 
= 0x01010285, 
 364     LARGE_SCREEN_ATTR 
= 0x01010286, 
 365     XLARGE_SCREEN_ATTR 
= 0x010102bf, 
 366     REQUIRED_ATTR 
= 0x0101028e, 
 367     SCREEN_SIZE_ATTR 
= 0x010102ca, 
 368     SCREEN_DENSITY_ATTR 
= 0x010102cb, 
 369     REQUIRES_SMALLEST_WIDTH_DP_ATTR 
= 0x01010364, 
 370     COMPATIBLE_WIDTH_LIMIT_DP_ATTR 
= 0x01010365, 
 371     LARGEST_WIDTH_LIMIT_DP_ATTR 
= 0x01010366, 
 372     PUBLIC_KEY_ATTR 
= 0x010103a6, 
 375 const char *getComponentName(String8 
&pkgName
, String8 
&componentName
) { 
 376     ssize_t idx 
= componentName
.find("."); 
 377     String8 
retStr(pkgName
); 
 379         retStr 
+= componentName
; 
 380     } else if (idx 
< 0) { 
 382         retStr 
+= componentName
; 
 384         return componentName
.string(); 
 386     return retStr
.string(); 
 389 static void printCompatibleScreens(ResXMLTree
& tree
) { 
 391     ResXMLTree::event_code_t code
; 
 394     printf("compatible-screens:"); 
 395     while ((code
=tree
.next()) != ResXMLTree::END_DOCUMENT 
&& code 
!= ResXMLTree::BAD_DOCUMENT
) { 
 396         if (code 
== ResXMLTree::END_TAG
) { 
 403         if (code 
!= ResXMLTree::START_TAG
) { 
 407         String8 
tag(tree
.getElementName(&len
)); 
 408         if (tag 
== "screen") { 
 409             int32_t screenSize 
= getIntegerAttribute(tree
, 
 410                     SCREEN_SIZE_ATTR
, NULL
, -1); 
 411             int32_t screenDensity 
= getIntegerAttribute(tree
, 
 412                     SCREEN_DENSITY_ATTR
, NULL
, -1); 
 413             if (screenSize 
> 0 && screenDensity 
> 0) { 
 418                 printf("'%d/%d'", screenSize
, screenDensity
); 
 426  * Handle the "dump" command, to extract select data from an archive. 
 428 int doDump(Bundle
* bundle
) 
 430     status_t result 
= UNKNOWN_ERROR
; 
 433     if (bundle
->getFileSpecCount() < 1) { 
 434         fprintf(stderr
, "ERROR: no dump option specified\n"); 
 438     if (bundle
->getFileSpecCount() < 2) { 
 439         fprintf(stderr
, "ERROR: no dump file specified\n"); 
 443     const char* option 
= bundle
->getFileSpecEntry(0); 
 444     const char* filename 
= bundle
->getFileSpecEntry(1); 
 448     if (!assets
.addAssetPath(String8(filename
), &assetsCookie
)) { 
 449         fprintf(stderr
, "ERROR: dump failed because assets could not be loaded\n"); 
 453     // Make a dummy config for retrieving resources...  we need to supply 
 454     // non-default values for some configs so that we can retrieve resources 
 455     // in the app that don't have a default.  The most important of these is 
 456     // the API version because key resources like icons will have an implicit 
 457     // version if they are using newer config types like density. 
 458     ResTable_config config
; 
 459     config
.language
[0] = 'e'; 
 460     config
.language
[1] = 'n'; 
 461     config
.country
[0] = 'U'; 
 462     config
.country
[1] = 'S'; 
 463     config
.orientation 
= ResTable_config::ORIENTATION_PORT
; 
 464     config
.density 
= ResTable_config::DENSITY_MEDIUM
; 
 465     config
.sdkVersion 
= 10000; // Very high. 
 466     config
.screenWidthDp 
= 320; 
 467     config
.screenHeightDp 
= 480; 
 468     config
.smallestScreenWidthDp 
= 320; 
 469     assets
.setConfiguration(config
); 
 471     const ResTable
& res 
= assets
.getResources(false); 
 473         fprintf(stderr
, "ERROR: dump failed because no resource table was found\n"); 
 477     if (strcmp("resources", option
) == 0) { 
 478 #ifndef HAVE_ANDROID_OS 
 479         res
.print(bundle
->getValues()); 
 481     } else if (strcmp("xmltree", option
) == 0) { 
 482         if (bundle
->getFileSpecCount() < 3) { 
 483             fprintf(stderr
, "ERROR: no dump xmltree resource file specified\n"); 
 487         for (int i
=2; i
<bundle
->getFileSpecCount(); i
++) { 
 488             const char* resname 
= bundle
->getFileSpecEntry(i
); 
 490             asset 
= assets
.openNonAsset(resname
, Asset::ACCESS_BUFFER
); 
 492                 fprintf(stderr
, "ERROR: dump failed because resource %s found\n", resname
); 
 496             if (tree
.setTo(asset
->getBuffer(true), 
 497                            asset
->getLength()) != NO_ERROR
) { 
 498                 fprintf(stderr
, "ERROR: Resource %s is corrupt\n", resname
); 
 502             printXMLBlock(&tree
); 
 508     } else if (strcmp("xmlstrings", option
) == 0) { 
 509         if (bundle
->getFileSpecCount() < 3) { 
 510             fprintf(stderr
, "ERROR: no dump xmltree resource file specified\n"); 
 514         for (int i
=2; i
<bundle
->getFileSpecCount(); i
++) { 
 515             const char* resname 
= bundle
->getFileSpecEntry(i
); 
 517             asset 
= assets
.openNonAsset(resname
, Asset::ACCESS_BUFFER
); 
 519                 fprintf(stderr
, "ERROR: dump failed because resource %s found\n", resname
); 
 523             if (tree
.setTo(asset
->getBuffer(true), 
 524                            asset
->getLength()) != NO_ERROR
) { 
 525                 fprintf(stderr
, "ERROR: Resource %s is corrupt\n", resname
); 
 528             printStringPool(&tree
.getStrings()); 
 535         asset 
= assets
.openNonAsset("AndroidManifest.xml", 
 536                                             Asset::ACCESS_BUFFER
); 
 538             fprintf(stderr
, "ERROR: dump failed because no AndroidManifest.xml found\n"); 
 542         if (tree
.setTo(asset
->getBuffer(true), 
 543                        asset
->getLength()) != NO_ERROR
) { 
 544             fprintf(stderr
, "ERROR: AndroidManifest.xml is corrupt\n"); 
 549         if (strcmp("permissions", option
) == 0) { 
 551             ResXMLTree::event_code_t code
; 
 553             while ((code
=tree
.next()) != ResXMLTree::END_DOCUMENT 
&& code 
!= ResXMLTree::BAD_DOCUMENT
) { 
 554                 if (code 
== ResXMLTree::END_TAG
) { 
 558                 if (code 
!= ResXMLTree::START_TAG
) { 
 562                 String8 
tag(tree
.getElementName(&len
)); 
 563                 //printf("Depth %d tag %s\n", depth, tag.string()); 
 565                     if (tag 
!= "manifest") { 
 566                         fprintf(stderr
, "ERROR: manifest does not start with <manifest> tag\n"); 
 569                     String8 pkg 
= getAttribute(tree
, NULL
, "package", NULL
); 
 570                     printf("package: %s\n", pkg
.string()); 
 571                 } else if (depth 
== 2 && tag 
== "permission") { 
 573                     String8 name 
= getAttribute(tree
, NAME_ATTR
, &error
); 
 575                         fprintf(stderr
, "ERROR: %s\n", error
.string()); 
 578                     printf("permission: %s\n", name
.string()); 
 579                 } else if (depth 
== 2 && tag 
== "uses-permission") { 
 581                     String8 name 
= getAttribute(tree
, NAME_ATTR
, &error
); 
 583                         fprintf(stderr
, "ERROR: %s\n", error
.string()); 
 586                     printf("uses-permission: %s\n", name
.string()); 
 589         } else if (strcmp("badging", option
) == 0) { 
 590             Vector
<String8
> locales
; 
 591             res
.getLocales(&locales
); 
 593             Vector
<ResTable_config
> configs
; 
 594             res
.getConfigurations(&configs
); 
 595             SortedVector
<int> densities
; 
 596             const size_t NC 
= configs
.size(); 
 597             for (size_t i
=0; i
<NC
; i
++) { 
 598                 int dens 
= configs
[i
].density
; 
 599                 if (dens 
== 0) dens 
= 160; 
 604             ResXMLTree::event_code_t code
; 
 607             bool withinActivity 
= false; 
 608             bool isMainActivity 
= false; 
 609             bool isLauncherActivity 
= false; 
 610             bool isSearchable 
= false; 
 611             bool withinApplication 
= false; 
 612             bool withinReceiver 
= false; 
 613             bool withinService 
= false; 
 614             bool withinIntentFilter 
= false; 
 615             bool hasMainActivity 
= false; 
 616             bool hasOtherActivities 
= false; 
 617             bool hasOtherReceivers 
= false; 
 618             bool hasOtherServices 
= false; 
 619             bool hasWallpaperService 
= false; 
 620             bool hasImeService 
= false; 
 621             bool hasWidgetReceivers 
= false; 
 622             bool hasIntentFilter 
= false; 
 623             bool actMainActivity 
= false; 
 624             bool actWidgetReceivers 
= false; 
 625             bool actImeService 
= false; 
 626             bool actWallpaperService 
= false; 
 628             // These two implement the implicit permissions that are granted 
 629             // to pre-1.6 applications. 
 630             bool hasWriteExternalStoragePermission 
= false; 
 631             bool hasReadPhoneStatePermission 
= false; 
 633             // This next group of variables is used to implement a group of 
 634             // backward-compatibility heuristics necessitated by the addition of 
 635             // some new uses-feature constants in 2.1 and 2.2. In most cases, the 
 636             // heuristic is "if an app requests a permission but doesn't explicitly 
 637             // request the corresponding <uses-feature>, presume it's there anyway". 
 638             bool specCameraFeature 
= false; // camera-related 
 639             bool specCameraAutofocusFeature 
= false; 
 640             bool reqCameraAutofocusFeature 
= false; 
 641             bool reqCameraFlashFeature 
= false; 
 642             bool hasCameraPermission 
= false; 
 643             bool specLocationFeature 
= false; // location-related 
 644             bool specNetworkLocFeature 
= false; 
 645             bool reqNetworkLocFeature 
= false; 
 646             bool specGpsFeature 
= false; 
 647             bool reqGpsFeature 
= false; 
 648             bool hasMockLocPermission 
= false; 
 649             bool hasCoarseLocPermission 
= false; 
 650             bool hasGpsPermission 
= false; 
 651             bool hasGeneralLocPermission 
= false; 
 652             bool specBluetoothFeature 
= false; // Bluetooth API-related 
 653             bool hasBluetoothPermission 
= false; 
 654             bool specMicrophoneFeature 
= false; // microphone-related 
 655             bool hasRecordAudioPermission 
= false; 
 656             bool specWiFiFeature 
= false; 
 657             bool hasWiFiPermission 
= false; 
 658             bool specTelephonyFeature 
= false; // telephony-related 
 659             bool reqTelephonySubFeature 
= false; 
 660             bool hasTelephonyPermission 
= false; 
 661             bool specTouchscreenFeature 
= false; // touchscreen-related 
 662             bool specMultitouchFeature 
= false; 
 663             bool reqDistinctMultitouchFeature 
= false; 
 664             bool specScreenPortraitFeature 
= false; 
 665             bool specScreenLandscapeFeature 
= false; 
 666             bool reqScreenPortraitFeature 
= false; 
 667             bool reqScreenLandscapeFeature 
= false; 
 668             // 2.2 also added some other features that apps can request, but that 
 669             // have no corresponding permission, so we cannot implement any 
 670             // back-compatibility heuristic for them. The below are thus unnecessary 
 671             // (but are retained here for documentary purposes.) 
 672             //bool specCompassFeature = false; 
 673             //bool specAccelerometerFeature = false; 
 674             //bool specProximityFeature = false; 
 675             //bool specAmbientLightFeature = false; 
 676             //bool specLiveWallpaperFeature = false; 
 680             int normalScreen 
= 1; 
 682             int xlargeScreen 
= 1; 
 684             int requiresSmallestWidthDp 
= 0; 
 685             int compatibleWidthLimitDp 
= 0; 
 686             int largestWidthLimitDp 
= 0; 
 688             String8 activityName
; 
 689             String8 activityLabel
; 
 690             String8 activityIcon
; 
 691             String8 receiverName
; 
 693             while ((code
=tree
.next()) != ResXMLTree::END_DOCUMENT 
&& code 
!= ResXMLTree::BAD_DOCUMENT
) { 
 694                 if (code 
== ResXMLTree::END_TAG
) { 
 697                         withinApplication 
= false; 
 698                     } else if (depth 
< 3) { 
 699                         if (withinActivity 
&& isMainActivity 
&& isLauncherActivity
) { 
 700                             const char *aName 
= getComponentName(pkg
, activityName
); 
 701                             printf("launchable-activity:"); 
 703                                 printf(" name='%s' ", aName
); 
 705                             printf(" label='%s' icon='%s'\n", 
 706                                     activityLabel
.string(), 
 707                                     activityIcon
.string()); 
 709                         if (!hasIntentFilter
) { 
 710                             hasOtherActivities 
|= withinActivity
; 
 711                             hasOtherReceivers 
|= withinReceiver
; 
 712                             hasOtherServices 
|= withinService
; 
 714                         withinActivity 
= false; 
 715                         withinService 
= false; 
 716                         withinReceiver 
= false; 
 717                         hasIntentFilter 
= false; 
 718                         isMainActivity 
= isLauncherActivity 
= false; 
 719                     } else if (depth 
< 4) { 
 720                         if (withinIntentFilter
) { 
 721                             if (withinActivity
) { 
 722                                 hasMainActivity 
|= actMainActivity
; 
 723                                 hasOtherActivities 
|= !actMainActivity
; 
 724                             } else if (withinReceiver
) { 
 725                                 hasWidgetReceivers 
|= actWidgetReceivers
; 
 726                                 hasOtherReceivers 
|= !actWidgetReceivers
; 
 727                             } else if (withinService
) { 
 728                                 hasImeService 
|= actImeService
; 
 729                                 hasWallpaperService 
|= actWallpaperService
; 
 730                                 hasOtherServices 
|= (!actImeService 
&& !actWallpaperService
); 
 733                         withinIntentFilter 
= false; 
 737                 if (code 
!= ResXMLTree::START_TAG
) { 
 741                 String8 
tag(tree
.getElementName(&len
)); 
 742                 //printf("Depth %d,  %s\n", depth, tag.string()); 
 744                     if (tag 
!= "manifest") { 
 745                         fprintf(stderr
, "ERROR: manifest does not start with <manifest> tag\n"); 
 748                     pkg 
= getAttribute(tree
, NULL
, "package", NULL
); 
 749                     printf("package: name='%s' ", pkg
.string()); 
 750                     int32_t versionCode 
= getIntegerAttribute(tree
, VERSION_CODE_ATTR
, &error
); 
 752                         fprintf(stderr
, "ERROR getting 'android:versionCode' attribute: %s\n", error
.string()); 
 755                     if (versionCode 
> 0) { 
 756                         printf("versionCode='%d' ", versionCode
); 
 758                         printf("versionCode='' "); 
 760                     String8 versionName 
= getResolvedAttribute(&res
, tree
, VERSION_NAME_ATTR
, &error
); 
 762                         fprintf(stderr
, "ERROR getting 'android:versionName' attribute: %s\n", error
.string()); 
 765                     printf("versionName='%s'\n", versionName
.string()); 
 766                 } else if (depth 
== 2) { 
 767                     withinApplication 
= false; 
 768                     if (tag 
== "application") { 
 769                         withinApplication 
= true; 
 772                         const size_t NL 
= locales
.size(); 
 773                         for (size_t i
=0; i
<NL
; i
++) { 
 774                             const char* localeStr 
=  locales
[i
].string(); 
 775                             assets
.setLocale(localeStr 
!= NULL 
? localeStr 
: ""); 
 776                             String8 llabel 
= getResolvedAttribute(&res
, tree
, LABEL_ATTR
, &error
); 
 778                                 if (localeStr 
== NULL 
|| strlen(localeStr
) == 0) { 
 780                                     printf("application-label:'%s'\n", llabel
.string()); 
 785                                     printf("application-label-%s:'%s'\n", localeStr
, 
 791                         ResTable_config tmpConfig 
= config
; 
 792                         const size_t ND 
= densities
.size(); 
 793                         for (size_t i
=0; i
<ND
; i
++) { 
 794                             tmpConfig
.density 
= densities
[i
]; 
 795                             assets
.setConfiguration(tmpConfig
); 
 796                             String8 icon 
= getResolvedAttribute(&res
, tree
, ICON_ATTR
, &error
); 
 798                                 printf("application-icon-%d:'%s'\n", densities
[i
], icon
.string()); 
 801                         assets
.setConfiguration(config
); 
 803                         String8 icon 
= getResolvedAttribute(&res
, tree
, ICON_ATTR
, &error
); 
 805                             fprintf(stderr
, "ERROR getting 'android:icon' attribute: %s\n", error
.string()); 
 808                         int32_t testOnly 
= getIntegerAttribute(tree
, TEST_ONLY_ATTR
, &error
, 0); 
 810                             fprintf(stderr
, "ERROR getting 'android:testOnly' attribute: %s\n", error
.string()); 
 813                         printf("application: label='%s' ", label
.string()); 
 814                         printf("icon='%s'\n", icon
.string()); 
 816                             printf("testOnly='%d'\n", testOnly
); 
 818                     } else if (tag 
== "uses-sdk") { 
 819                         int32_t code 
= getIntegerAttribute(tree
, MIN_SDK_VERSION_ATTR
, &error
); 
 822                             String8 name 
= getResolvedAttribute(&res
, tree
, MIN_SDK_VERSION_ATTR
, &error
); 
 824                                 fprintf(stderr
, "ERROR getting 'android:minSdkVersion' attribute: %s\n", 
 828                             if (name 
== "Donut") targetSdk 
= 4; 
 829                             printf("sdkVersion:'%s'\n", name
.string()); 
 830                         } else if (code 
!= -1) { 
 832                             printf("sdkVersion:'%d'\n", code
); 
 834                         code 
= getIntegerAttribute(tree
, MAX_SDK_VERSION_ATTR
, NULL
, -1); 
 836                             printf("maxSdkVersion:'%d'\n", code
); 
 838                         code 
= getIntegerAttribute(tree
, TARGET_SDK_VERSION_ATTR
, &error
); 
 841                             String8 name 
= getResolvedAttribute(&res
, tree
, TARGET_SDK_VERSION_ATTR
, &error
); 
 843                                 fprintf(stderr
, "ERROR getting 'android:targetSdkVersion' attribute: %s\n", 
 847                             if (name 
== "Donut" && targetSdk 
< 4) targetSdk 
= 4; 
 848                             printf("targetSdkVersion:'%s'\n", name
.string()); 
 849                         } else if (code 
!= -1) { 
 850                             if (targetSdk 
< code
) { 
 853                             printf("targetSdkVersion:'%d'\n", code
); 
 855                     } else if (tag 
== "uses-configuration") { 
 856                         int32_t reqTouchScreen 
= getIntegerAttribute(tree
, 
 857                                 REQ_TOUCH_SCREEN_ATTR
, NULL
, 0); 
 858                         int32_t reqKeyboardType 
= getIntegerAttribute(tree
, 
 859                                 REQ_KEYBOARD_TYPE_ATTR
, NULL
, 0); 
 860                         int32_t reqHardKeyboard 
= getIntegerAttribute(tree
, 
 861                                 REQ_HARD_KEYBOARD_ATTR
, NULL
, 0); 
 862                         int32_t reqNavigation 
= getIntegerAttribute(tree
, 
 863                                 REQ_NAVIGATION_ATTR
, NULL
, 0); 
 864                         int32_t reqFiveWayNav 
= getIntegerAttribute(tree
, 
 865                                 REQ_FIVE_WAY_NAV_ATTR
, NULL
, 0); 
 866                         printf("uses-configuration:"); 
 867                         if (reqTouchScreen 
!= 0) { 
 868                             printf(" reqTouchScreen='%d'", reqTouchScreen
); 
 870                         if (reqKeyboardType 
!= 0) { 
 871                             printf(" reqKeyboardType='%d'", reqKeyboardType
); 
 873                         if (reqHardKeyboard 
!= 0) { 
 874                             printf(" reqHardKeyboard='%d'", reqHardKeyboard
); 
 876                         if (reqNavigation 
!= 0) { 
 877                             printf(" reqNavigation='%d'", reqNavigation
); 
 879                         if (reqFiveWayNav 
!= 0) { 
 880                             printf(" reqFiveWayNav='%d'", reqFiveWayNav
); 
 883                     } else if (tag 
== "supports-screens") { 
 884                         smallScreen 
= getIntegerAttribute(tree
, 
 885                                 SMALL_SCREEN_ATTR
, NULL
, 1); 
 886                         normalScreen 
= getIntegerAttribute(tree
, 
 887                                 NORMAL_SCREEN_ATTR
, NULL
, 1); 
 888                         largeScreen 
= getIntegerAttribute(tree
, 
 889                                 LARGE_SCREEN_ATTR
, NULL
, 1); 
 890                         xlargeScreen 
= getIntegerAttribute(tree
, 
 891                                 XLARGE_SCREEN_ATTR
, NULL
, 1); 
 892                         anyDensity 
= getIntegerAttribute(tree
, 
 893                                 ANY_DENSITY_ATTR
, NULL
, 1); 
 894                         requiresSmallestWidthDp 
= getIntegerAttribute(tree
, 
 895                                 REQUIRES_SMALLEST_WIDTH_DP_ATTR
, NULL
, 0); 
 896                         compatibleWidthLimitDp 
= getIntegerAttribute(tree
, 
 897                                 COMPATIBLE_WIDTH_LIMIT_DP_ATTR
, NULL
, 0); 
 898                         largestWidthLimitDp 
= getIntegerAttribute(tree
, 
 899                                 LARGEST_WIDTH_LIMIT_DP_ATTR
, NULL
, 0); 
 900                     } else if (tag 
== "uses-feature") { 
 901                         String8 name 
= getAttribute(tree
, NAME_ATTR
, &error
); 
 903                         if (name 
!= "" && error 
== "") { 
 904                             int req 
= getIntegerAttribute(tree
, 
 905                                     REQUIRED_ATTR
, NULL
, 1); 
 907                             if (name 
== "android.hardware.camera") { 
 908                                 specCameraFeature 
= true; 
 909                             } else if (name 
== "android.hardware.camera.autofocus") { 
 910                                 // these have no corresponding permission to check for, 
 911                                 // but should imply the foundational camera permission 
 912                                 reqCameraAutofocusFeature 
= reqCameraAutofocusFeature 
|| req
; 
 913                                 specCameraAutofocusFeature 
= true; 
 914                             } else if (req 
&& (name 
== "android.hardware.camera.flash")) { 
 915                                 // these have no corresponding permission to check for, 
 916                                 // but should imply the foundational camera permission 
 917                                 reqCameraFlashFeature 
= true; 
 918                             } else if (name 
== "android.hardware.location") { 
 919                                 specLocationFeature 
= true; 
 920                             } else if (name 
== "android.hardware.location.network") { 
 921                                 specNetworkLocFeature 
= true; 
 922                                 reqNetworkLocFeature 
= reqNetworkLocFeature 
|| req
; 
 923                             } else if (name 
== "android.hardware.location.gps") { 
 924                                 specGpsFeature 
= true; 
 925                                 reqGpsFeature 
= reqGpsFeature 
|| req
; 
 926                             } else if (name 
== "android.hardware.bluetooth") { 
 927                                 specBluetoothFeature 
= true; 
 928                             } else if (name 
== "android.hardware.touchscreen") { 
 929                                 specTouchscreenFeature 
= true; 
 930                             } else if (name 
== "android.hardware.touchscreen.multitouch") { 
 931                                 specMultitouchFeature 
= true; 
 932                             } else if (name 
== "android.hardware.touchscreen.multitouch.distinct") { 
 933                                 reqDistinctMultitouchFeature 
= reqDistinctMultitouchFeature 
|| req
; 
 934                             } else if (name 
== "android.hardware.microphone") { 
 935                                 specMicrophoneFeature 
= true; 
 936                             } else if (name 
== "android.hardware.wifi") { 
 937                                 specWiFiFeature 
= true; 
 938                             } else if (name 
== "android.hardware.telephony") { 
 939                                 specTelephonyFeature 
= true; 
 940                             } else if (req 
&& (name 
== "android.hardware.telephony.gsm" || 
 941                                                name 
== "android.hardware.telephony.cdma")) { 
 942                                 // these have no corresponding permission to check for, 
 943                                 // but should imply the foundational telephony permission 
 944                                 reqTelephonySubFeature 
= true; 
 945                             } else if (name 
== "android.hardware.screen.portrait") { 
 946                                 specScreenPortraitFeature 
= true; 
 947                             } else if (name 
== "android.hardware.screen.landscape") { 
 948                                 specScreenLandscapeFeature 
= true; 
 950                             printf("uses-feature%s:'%s'\n", 
 951                                     req 
? "" : "-not-required", name
.string()); 
 953                             int vers 
= getIntegerAttribute(tree
, 
 954                                     GL_ES_VERSION_ATTR
, &error
); 
 956                                 printf("uses-gl-es:'0x%x'\n", vers
); 
 959                     } else if (tag 
== "uses-permission") { 
 960                         String8 name 
= getAttribute(tree
, NAME_ATTR
, &error
); 
 961                         if (name 
!= "" && error 
== "") { 
 962                             if (name 
== "android.permission.CAMERA") { 
 963                                 hasCameraPermission 
= true; 
 964                             } else if (name 
== "android.permission.ACCESS_FINE_LOCATION") { 
 965                                 hasGpsPermission 
= true; 
 966                             } else if (name 
== "android.permission.ACCESS_MOCK_LOCATION") { 
 967                                 hasMockLocPermission 
= true; 
 968                             } else if (name 
== "android.permission.ACCESS_COARSE_LOCATION") { 
 969                                 hasCoarseLocPermission 
= true; 
 970                             } else if (name 
== "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" || 
 971                                        name 
== "android.permission.INSTALL_LOCATION_PROVIDER") { 
 972                                 hasGeneralLocPermission 
= true; 
 973                             } else if (name 
== "android.permission.BLUETOOTH" || 
 974                                        name 
== "android.permission.BLUETOOTH_ADMIN") { 
 975                                 hasBluetoothPermission 
= true; 
 976                             } else if (name 
== "android.permission.RECORD_AUDIO") { 
 977                                 hasRecordAudioPermission 
= true; 
 978                             } else if (name 
== "android.permission.ACCESS_WIFI_STATE" || 
 979                                        name 
== "android.permission.CHANGE_WIFI_STATE" || 
 980                                        name 
== "android.permission.CHANGE_WIFI_MULTICAST_STATE") { 
 981                                 hasWiFiPermission 
= true; 
 982                             } else if (name 
== "android.permission.CALL_PHONE" || 
 983                                        name 
== "android.permission.CALL_PRIVILEGED" || 
 984                                        name 
== "android.permission.MODIFY_PHONE_STATE" || 
 985                                        name 
== "android.permission.PROCESS_OUTGOING_CALLS" || 
 986                                        name 
== "android.permission.READ_SMS" || 
 987                                        name 
== "android.permission.RECEIVE_SMS" || 
 988                                        name 
== "android.permission.RECEIVE_MMS" || 
 989                                        name 
== "android.permission.RECEIVE_WAP_PUSH" || 
 990                                        name 
== "android.permission.SEND_SMS" || 
 991                                        name 
== "android.permission.WRITE_APN_SETTINGS" || 
 992                                        name 
== "android.permission.WRITE_SMS") { 
 993                                 hasTelephonyPermission 
= true; 
 994                             } else if (name 
== "android.permission.WRITE_EXTERNAL_STORAGE") { 
 995                                 hasWriteExternalStoragePermission 
= true; 
 996                             } else if (name 
== "android.permission.READ_PHONE_STATE") { 
 997                                 hasReadPhoneStatePermission 
= true; 
 999                             printf("uses-permission:'%s'\n", name
.string()); 
1001                             fprintf(stderr
, "ERROR getting 'android:name' attribute: %s\n", 
1005                     } else if (tag 
== "uses-package") { 
1006                         String8 name 
= getAttribute(tree
, NAME_ATTR
, &error
); 
1007                         if (name 
!= "" && error 
== "") { 
1008                             printf("uses-package:'%s'\n", name
.string()); 
1010                             fprintf(stderr
, "ERROR getting 'android:name' attribute: %s\n", 
1014                     } else if (tag 
== "original-package") { 
1015                         String8 name 
= getAttribute(tree
, NAME_ATTR
, &error
); 
1016                         if (name 
!= "" && error 
== "") { 
1017                             printf("original-package:'%s'\n", name
.string()); 
1019                             fprintf(stderr
, "ERROR getting 'android:name' attribute: %s\n", 
1023                     } else if (tag 
== "supports-gl-texture") { 
1024                         String8 name 
= getAttribute(tree
, NAME_ATTR
, &error
); 
1025                         if (name 
!= "" && error 
== "") { 
1026                             printf("supports-gl-texture:'%s'\n", name
.string()); 
1028                             fprintf(stderr
, "ERROR getting 'android:name' attribute: %s\n", 
1032                     } else if (tag 
== "compatible-screens") { 
1033                         printCompatibleScreens(tree
); 
1035                     } else if (tag 
== "package-verifier") { 
1036                         String8 name 
= getAttribute(tree
, NAME_ATTR
, &error
); 
1037                         if (name 
!= "" && error 
== "") { 
1038                             String8 publicKey 
= getAttribute(tree
, PUBLIC_KEY_ATTR
, &error
); 
1039                             if (publicKey 
!= "" && error 
== "") { 
1040                                 printf("package-verifier: name='%s' publicKey='%s'\n", 
1041                                         name
.string(), publicKey
.string()); 
1045                 } else if (depth 
== 3 && withinApplication
) { 
1046                     withinActivity 
= false; 
1047                     withinReceiver 
= false; 
1048                     withinService 
= false; 
1049                     hasIntentFilter 
= false; 
1050                     if(tag 
== "activity") { 
1051                         withinActivity 
= true; 
1052                         activityName 
= getAttribute(tree
, NAME_ATTR
, &error
); 
1054                             fprintf(stderr
, "ERROR getting 'android:name' attribute: %s\n", error
.string()); 
1058                         activityLabel 
= getResolvedAttribute(&res
, tree
, LABEL_ATTR
, &error
); 
1060                             fprintf(stderr
, "ERROR getting 'android:label' attribute: %s\n", error
.string()); 
1064                         activityIcon 
= getResolvedAttribute(&res
, tree
, ICON_ATTR
, &error
); 
1066                             fprintf(stderr
, "ERROR getting 'android:icon' attribute: %s\n", error
.string()); 
1070                         int32_t orien 
= getResolvedIntegerAttribute(&res
, tree
, 
1071                                 SCREEN_ORIENTATION_ATTR
, &error
); 
1073                             if (orien 
== 0 || orien 
== 6 || orien 
== 8) { 
1074                                 // Requests landscape, sensorLandscape, or reverseLandscape. 
1075                                 reqScreenLandscapeFeature 
= true; 
1076                             } else if (orien 
== 1 || orien 
== 7 || orien 
== 9) { 
1077                                 // Requests portrait, sensorPortrait, or reversePortrait. 
1078                                 reqScreenPortraitFeature 
= true; 
1081                     } else if (tag 
== "uses-library") { 
1082                         String8 libraryName 
= getAttribute(tree
, NAME_ATTR
, &error
); 
1084                             fprintf(stderr
, "ERROR getting 'android:name' attribute for uses-library: %s\n", error
.string()); 
1087                         int req 
= getIntegerAttribute(tree
, 
1088                                 REQUIRED_ATTR
, NULL
, 1); 
1089                         printf("uses-library%s:'%s'\n", 
1090                                 req 
? "" : "-not-required", libraryName
.string()); 
1091                     } else if (tag 
== "receiver") { 
1092                         withinReceiver 
= true; 
1093                         receiverName 
= getAttribute(tree
, NAME_ATTR
, &error
); 
1096                             fprintf(stderr
, "ERROR getting 'android:name' attribute for receiver: %s\n", error
.string()); 
1099                     } else if (tag 
== "service") { 
1100                         withinService 
= true; 
1101                         serviceName 
= getAttribute(tree
, NAME_ATTR
, &error
); 
1104                             fprintf(stderr
, "ERROR getting 'android:name' attribute for service: %s\n", error
.string()); 
1108                 } else if ((depth 
== 4) && (tag 
== "intent-filter")) { 
1109                     hasIntentFilter 
= true; 
1110                     withinIntentFilter 
= true; 
1111                     actMainActivity 
= actWidgetReceivers 
= actImeService 
= actWallpaperService 
= false; 
1112                 } else if ((depth 
== 5) && withinIntentFilter
){ 
1114                     if (tag 
== "action") { 
1115                         action 
= getAttribute(tree
, NAME_ATTR
, &error
); 
1117                             fprintf(stderr
, "ERROR getting 'android:name' attribute: %s\n", error
.string()); 
1120                         if (withinActivity
) { 
1121                             if (action 
== "android.intent.action.MAIN") { 
1122                                 isMainActivity 
= true; 
1123                                 actMainActivity 
= true; 
1125                         } else if (withinReceiver
) { 
1126                             if (action 
== "android.appwidget.action.APPWIDGET_UPDATE") { 
1127                                 actWidgetReceivers 
= true; 
1129                         } else if (withinService
) { 
1130                             if (action 
== "android.view.InputMethod") { 
1131                                 actImeService 
= true; 
1132                             } else if (action 
== "android.service.wallpaper.WallpaperService") { 
1133                                 actWallpaperService 
= true; 
1136                         if (action 
== "android.intent.action.SEARCH") { 
1137                             isSearchable 
= true; 
1141                     if (tag 
== "category") { 
1142                         String8 category 
= getAttribute(tree
, NAME_ATTR
, &error
); 
1144                             fprintf(stderr
, "ERROR getting 'name' attribute: %s\n", error
.string()); 
1147                         if (withinActivity
) { 
1148                             if (category 
== "android.intent.category.LAUNCHER") { 
1149                                 isLauncherActivity 
= true; 
1156             // Pre-1.6 implicitly granted permission compatibility logic 
1157             if (targetSdk 
< 4) { 
1158                 if (!hasWriteExternalStoragePermission
) { 
1159                     printf("uses-permission:'android.permission.WRITE_EXTERNAL_STORAGE'\n"); 
1161                 if (!hasReadPhoneStatePermission
) { 
1162                     printf("uses-permission:'android.permission.READ_PHONE_STATE'\n"); 
1166             /* The following blocks handle printing "inferred" uses-features, based 
1167              * on whether related features or permissions are used by the app. 
1168              * Note that the various spec*Feature variables denote whether the 
1169              * relevant tag was *present* in the AndroidManfest, not that it was 
1170              * present and set to true. 
1172             // Camera-related back-compatibility logic 
1173             if (!specCameraFeature
) { 
1174                 if (reqCameraFlashFeature 
|| reqCameraAutofocusFeature
) { 
1175                     // if app requested a sub-feature (autofocus or flash) and didn't 
1176                     // request the base camera feature, we infer that it meant to 
1177                     printf("uses-feature:'android.hardware.camera'\n"); 
1178                 } else if (hasCameraPermission
) { 
1179                     // if app wants to use camera but didn't request the feature, we infer  
1180                     // that it meant to, and further that it wants autofocus 
1181                     // (which was the 1.0 - 1.5 behavior) 
1182                     printf("uses-feature:'android.hardware.camera'\n"); 
1183                     if (!specCameraAutofocusFeature
) { 
1184                         printf("uses-feature:'android.hardware.camera.autofocus'\n"); 
1189             // Location-related back-compatibility logic 
1190             if (!specLocationFeature 
&& 
1191                 (hasMockLocPermission 
|| hasCoarseLocPermission 
|| hasGpsPermission 
|| 
1192                  hasGeneralLocPermission 
|| reqNetworkLocFeature 
|| reqGpsFeature
)) { 
1193                 // if app either takes a location-related permission or requests one of the 
1194                 // sub-features, we infer that it also meant to request the base location feature 
1195                 printf("uses-feature:'android.hardware.location'\n"); 
1197             if (!specGpsFeature 
&& hasGpsPermission
) { 
1198                 // if app takes GPS (FINE location) perm but does not request the GPS 
1199                 // feature, we infer that it meant to 
1200                 printf("uses-feature:'android.hardware.location.gps'\n"); 
1202             if (!specNetworkLocFeature 
&& hasCoarseLocPermission
) { 
1203                 // if app takes Network location (COARSE location) perm but does not request the 
1204                 // network location feature, we infer that it meant to 
1205                 printf("uses-feature:'android.hardware.location.network'\n"); 
1208             // Bluetooth-related compatibility logic 
1209             if (!specBluetoothFeature 
&& hasBluetoothPermission 
&& (targetSdk 
> 4)) { 
1210                 // if app takes a Bluetooth permission but does not request the Bluetooth 
1211                 // feature, we infer that it meant to 
1212                 printf("uses-feature:'android.hardware.bluetooth'\n"); 
1215             // Microphone-related compatibility logic 
1216             if (!specMicrophoneFeature 
&& hasRecordAudioPermission
) { 
1217                 // if app takes the record-audio permission but does not request the microphone 
1218                 // feature, we infer that it meant to 
1219                 printf("uses-feature:'android.hardware.microphone'\n"); 
1222             // WiFi-related compatibility logic 
1223             if (!specWiFiFeature 
&& hasWiFiPermission
) { 
1224                 // if app takes one of the WiFi permissions but does not request the WiFi 
1225                 // feature, we infer that it meant to 
1226                 printf("uses-feature:'android.hardware.wifi'\n"); 
1229             // Telephony-related compatibility logic 
1230             if (!specTelephonyFeature 
&& (hasTelephonyPermission 
|| reqTelephonySubFeature
)) { 
1231                 // if app takes one of the telephony permissions or requests a sub-feature but 
1232                 // does not request the base telephony feature, we infer that it meant to 
1233                 printf("uses-feature:'android.hardware.telephony'\n"); 
1236             // Touchscreen-related back-compatibility logic 
1237             if (!specTouchscreenFeature
) { // not a typo! 
1238                 // all apps are presumed to require a touchscreen, unless they explicitly say 
1239                 // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/> 
1240                 // Note that specTouchscreenFeature is true if the tag is present, regardless 
1241                 // of whether its value is true or false, so this is safe 
1242                 printf("uses-feature:'android.hardware.touchscreen'\n"); 
1244             if (!specMultitouchFeature 
&& reqDistinctMultitouchFeature
) { 
1245                 // if app takes one of the telephony permissions or requests a sub-feature but 
1246                 // does not request the base telephony feature, we infer that it meant to 
1247                 printf("uses-feature:'android.hardware.touchscreen.multitouch'\n"); 
1250             // Landscape/portrait-related compatibility logic 
1251             if (!specScreenLandscapeFeature 
&& !specScreenPortraitFeature
) { 
1252                 // If the app has specified any activities in its manifest 
1253                 // that request a specific orientation, then assume that 
1254                 // orientation is required. 
1255                 if (reqScreenLandscapeFeature
) { 
1256                     printf("uses-feature:'android.hardware.screen.landscape'\n"); 
1258                 if (reqScreenPortraitFeature
) { 
1259                     printf("uses-feature:'android.hardware.screen.portrait'\n"); 
1263             if (hasMainActivity
) { 
1266             if (hasWidgetReceivers
) { 
1267                 printf("app-widget\n"); 
1269             if (hasImeService
) { 
1272             if (hasWallpaperService
) { 
1273                 printf("wallpaper\n"); 
1275             if (hasOtherActivities
) { 
1276                 printf("other-activities\n"); 
1281             if (hasOtherReceivers
) { 
1282                 printf("other-receivers\n"); 
1284             if (hasOtherServices
) { 
1285                 printf("other-services\n"); 
1288             // For modern apps, if screen size buckets haven't been specified 
1289             // but the new width ranges have, then infer the buckets from them. 
1290             if (smallScreen 
> 0 && normalScreen 
> 0 && largeScreen 
> 0 && xlargeScreen 
> 0 
1291                     && requiresSmallestWidthDp 
> 0) { 
1292                 int compatWidth 
= compatibleWidthLimitDp
; 
1293                 if (compatWidth 
<= 0) compatWidth 
= requiresSmallestWidthDp
; 
1294                 if (requiresSmallestWidthDp 
<= 240 && compatWidth 
>= 240) { 
1299                 if (requiresSmallestWidthDp 
<= 320 && compatWidth 
>= 320) { 
1304                 if (requiresSmallestWidthDp 
<= 480 && compatWidth 
>= 480) { 
1309                 if (requiresSmallestWidthDp 
<= 720 && compatWidth 
>= 720) { 
1316             // Determine default values for any unspecified screen sizes, 
1317             // based on the target SDK of the package.  As of 4 (donut) 
1318             // the screen size support was introduced, so all default to 
1320             if (smallScreen 
> 0) { 
1321                 smallScreen 
= targetSdk 
>= 4 ? -1 : 0; 
1323             if (normalScreen 
> 0) { 
1326             if (largeScreen 
> 0) { 
1327                 largeScreen 
= targetSdk 
>= 4 ? -1 : 0; 
1329             if (xlargeScreen 
> 0) { 
1330                 // Introduced in Gingerbread. 
1331                 xlargeScreen 
= targetSdk 
>= 9 ? -1 : 0; 
1333             if (anyDensity 
> 0) { 
1334                 anyDensity 
= (targetSdk 
>= 4 || requiresSmallestWidthDp 
> 0 
1335                         || compatibleWidthLimitDp 
> 0) ? -1 : 0; 
1337             printf("supports-screens:"); 
1338             if (smallScreen 
!= 0) printf(" 'small'"); 
1339             if (normalScreen 
!= 0) printf(" 'normal'"); 
1340             if (largeScreen 
!= 0) printf(" 'large'"); 
1341             if (xlargeScreen 
!= 0) printf(" 'xlarge'"); 
1343             printf("supports-any-density: '%s'\n", anyDensity 
? "true" : "false"); 
1344             if (requiresSmallestWidthDp 
> 0) { 
1345                 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp
); 
1347             if (compatibleWidthLimitDp 
> 0) { 
1348                 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp
); 
1350             if (largestWidthLimitDp 
> 0) { 
1351                 printf("largest-width-limit:'%d'\n", largestWidthLimitDp
); 
1355             const size_t NL 
= locales
.size(); 
1356             for (size_t i
=0; i
<NL
; i
++) { 
1357                 const char* localeStr 
=  locales
[i
].string(); 
1358                 if (localeStr 
== NULL 
|| strlen(localeStr
) == 0) { 
1359                     localeStr 
= "--_--"; 
1361                 printf(" '%s'", localeStr
); 
1365             printf("densities:"); 
1366             const size_t ND 
= densities
.size(); 
1367             for (size_t i
=0; i
<ND
; i
++) { 
1368                 printf(" '%d'", densities
[i
]); 
1372             AssetDir
* dir 
= assets
.openNonAssetDir(assetsCookie
, "lib"); 
1374                 if (dir
->getFileCount() > 0) { 
1375                     printf("native-code:"); 
1376                     for (size_t i
=0; i
<dir
->getFileCount(); i
++) { 
1377                         printf(" '%s'", dir
->getFileName(i
).string()); 
1383         } else if (strcmp("configurations", option
) == 0) { 
1384             Vector
<ResTable_config
> configs
; 
1385             res
.getConfigurations(&configs
); 
1386             const size_t N 
= configs
.size(); 
1387             for (size_t i
=0; i
<N
; i
++) { 
1388                 printf("%s\n", configs
[i
].toString().string()); 
1391             fprintf(stderr
, "ERROR: unknown dump option '%s'\n", option
); 
1402     return (result 
!= NO_ERROR
); 
1407  * Handle the "add" command, which wants to add files to a new or 
1408  * pre-existing archive. 
1410 int doAdd(Bundle
* bundle
) 
1412     ZipFile
* zip 
= NULL
; 
1413     status_t result 
= UNKNOWN_ERROR
; 
1414     const char* zipFileName
; 
1416     if (bundle
->getUpdate()) { 
1417         /* avoid confusion */ 
1418         fprintf(stderr
, "ERROR: can't use '-u' with add\n"); 
1422     if (bundle
->getFileSpecCount() < 1) { 
1423         fprintf(stderr
, "ERROR: must specify zip file name\n"); 
1426     zipFileName 
= bundle
->getFileSpecEntry(0); 
1428     if (bundle
->getFileSpecCount() < 2) { 
1429         fprintf(stderr
, "NOTE: nothing to do\n"); 
1433     zip 
= openReadWrite(zipFileName
, true); 
1435         fprintf(stderr
, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName
); 
1439     for (int i 
= 1; i 
< bundle
->getFileSpecCount(); i
++) { 
1440         const char* fileName 
= bundle
->getFileSpecEntry(i
); 
1442         if (strcasecmp(String8(fileName
).getPathExtension().string(), ".gz") == 0) { 
1443             printf(" '%s'... (from gzip)\n", fileName
); 
1444             result 
= zip
->addGzip(fileName
, String8(fileName
).getBasePath().string(), NULL
); 
1446             if (bundle
->getJunkPath()) { 
1447                 String8 storageName 
= String8(fileName
).getPathLeaf(); 
1448                 printf(" '%s' as '%s'...\n", fileName
, storageName
.string()); 
1449                 result 
= zip
->add(fileName
, storageName
.string(), 
1450                                   bundle
->getCompressionMethod(), NULL
); 
1452                 printf(" '%s'...\n", fileName
); 
1453                 result 
= zip
->add(fileName
, bundle
->getCompressionMethod(), NULL
); 
1456         if (result 
!= NO_ERROR
) { 
1457             fprintf(stderr
, "Unable to add '%s' to '%s'", bundle
->getFileSpecEntry(i
), zipFileName
); 
1458             if (result 
== NAME_NOT_FOUND
) 
1459                 fprintf(stderr
, ": file not found\n"); 
1460             else if (result 
== ALREADY_EXISTS
) 
1461                 fprintf(stderr
, ": already exists in archive\n"); 
1463                 fprintf(stderr
, "\n"); 
1472     return (result 
!= NO_ERROR
); 
1477  * Delete files from an existing archive. 
1479 int doRemove(Bundle
* bundle
) 
1481     ZipFile
* zip 
= NULL
; 
1482     status_t result 
= UNKNOWN_ERROR
; 
1483     const char* zipFileName
; 
1485     if (bundle
->getFileSpecCount() < 1) { 
1486         fprintf(stderr
, "ERROR: must specify zip file name\n"); 
1489     zipFileName 
= bundle
->getFileSpecEntry(0); 
1491     if (bundle
->getFileSpecCount() < 2) { 
1492         fprintf(stderr
, "NOTE: nothing to do\n"); 
1496     zip 
= openReadWrite(zipFileName
, false); 
1498         fprintf(stderr
, "ERROR: failed opening Zip archive '%s'\n", 
1503     for (int i 
= 1; i 
< bundle
->getFileSpecCount(); i
++) { 
1504         const char* fileName 
= bundle
->getFileSpecEntry(i
); 
1507         entry 
= zip
->getEntryByName(fileName
); 
1508         if (entry 
== NULL
) { 
1509             printf(" '%s' NOT FOUND\n", fileName
); 
1513         result 
= zip
->remove(entry
); 
1515         if (result 
!= NO_ERROR
) { 
1516             fprintf(stderr
, "Unable to delete '%s' from '%s'\n", 
1517                 bundle
->getFileSpecEntry(i
), zipFileName
); 
1522     /* update the archive */ 
1527     return (result 
!= NO_ERROR
); 
1532  * Package up an asset directory and associated application files. 
1534 int doPackage(Bundle
* bundle
) 
1536     const char* outputAPKFile
; 
1539     sp
<AaptAssets
> assets
; 
1542     String8 dependencyFile
; 
1544     // -c zz_ZZ means do pseudolocalization 
1545     ResourceFilter filter
; 
1546     err 
= filter
.parse(bundle
->getConfigurations()); 
1547     if (err 
!= NO_ERROR
) { 
1550     if (filter
.containsPseudo()) { 
1551         bundle
->setPseudolocalize(true); 
1554     N 
= bundle
->getFileSpecCount(); 
1555     if (N 
< 1 && bundle
->getResourceSourceDirs().size() == 0 && bundle
->getJarFiles().size() == 0 
1556             && bundle
->getAndroidManifestFile() == NULL 
&& bundle
->getAssetSourceDir() == NULL
) { 
1557         fprintf(stderr
, "ERROR: no input files\n"); 
1561     outputAPKFile 
= bundle
->getOutputAPKFile(); 
1563     // Make sure the filenames provided exist and are of the appropriate type. 
1564     if (outputAPKFile
) { 
1566         type 
= getFileType(outputAPKFile
); 
1567         if (type 
!= kFileTypeNonexistent 
&& type 
!= kFileTypeRegular
) { 
1569                 "ERROR: output file '%s' exists but is not regular file\n", 
1576     assets 
= new AaptAssets(); 
1578     // Set up the resource gathering in assets if we're going to generate 
1579     // dependency files. Every time we encounter a resource while slurping 
1580     // the tree, we'll add it to these stores so we have full resource paths 
1581     // to write to a dependency file. 
1582     if (bundle
->getGenDependencies()) { 
1583         sp
<FilePathStore
> resPathStore 
= new FilePathStore
; 
1584         assets
->setFullResPaths(resPathStore
); 
1585         sp
<FilePathStore
> assetPathStore 
= new FilePathStore
; 
1586         assets
->setFullAssetPaths(assetPathStore
); 
1589     err 
= assets
->slurpFromArgs(bundle
); 
1594     if (bundle
->getVerbose()) { 
1595         assets
->print(String8()); 
1598     // If they asked for any fileAs that need to be compiled, do so. 
1599     if (bundle
->getResourceSourceDirs().size() || bundle
->getAndroidManifestFile()) { 
1600         err 
= buildResources(bundle
, assets
); 
1606     // At this point we've read everything and processed everything.  From here 
1607     // on out it's just writing output files. 
1608     if (SourcePos::hasErrors()) { 
1612     // If we've been asked to generate a dependency file, do that here 
1613     if (bundle
->getGenDependencies()) { 
1614         // If this is the packaging step, generate the dependency file next to 
1615         // the output apk (e.g. bin/resources.ap_.d) 
1616         if (outputAPKFile
) { 
1617             dependencyFile 
= String8(outputAPKFile
); 
1618             // Add the .d extension to the dependency file. 
1619             dependencyFile
.append(".d"); 
1621             // Else if this is the R.java dependency generation step, 
1622             // generate the dependency file in the R.java package subdirectory 
1623             // e.g. gen/com/foo/app/R.java.d 
1624             dependencyFile 
= String8(bundle
->getRClassDir()); 
1625             dependencyFile
.appendPath("R.java.d"); 
1627         // Make sure we have a clean dependency file to start with 
1628         fp 
= fopen(dependencyFile
, "w"); 
1632     // Write out R.java constants 
1633     if (assets
->getPackage() == assets
->getSymbolsPrivatePackage()) { 
1634         if (bundle
->getCustomPackage() == NULL
) { 
1635             // Write the R.java file into the appropriate class directory 
1636             // e.g. gen/com/foo/app/R.java 
1637             err 
= writeResourceSymbols(bundle
, assets
, assets
->getPackage(), true); 
1638             // If we have library files, we're going to write our R.java file into 
1639             // the appropriate class directory for those libraries as well. 
1640             // e.g. gen/com/foo/app/lib/R.java 
1641             if (bundle
->getExtraPackages() != NULL
) { 
1643                 String8 
libs(bundle
->getExtraPackages()); 
1644                 char* packageString 
= strtok(libs
.lockBuffer(libs
.length()), ":"); 
1645                 while (packageString 
!= NULL
) { 
1646                     // Write the R.java file out with the correct package name 
1647                     err 
= writeResourceSymbols(bundle
, assets
, String8(packageString
), true); 
1648                     packageString 
= strtok(NULL
, ":"); 
1650                 libs
.unlockBuffer(); 
1653             const String8 
customPkg(bundle
->getCustomPackage()); 
1654             err 
= writeResourceSymbols(bundle
, assets
, customPkg
, true); 
1660         err 
= writeResourceSymbols(bundle
, assets
, assets
->getPackage(), false); 
1664         err 
= writeResourceSymbols(bundle
, assets
, assets
->getSymbolsPrivatePackage(), true); 
1670     // Write out the ProGuard file 
1671     err 
= writeProguardFile(bundle
, assets
); 
1677     if (outputAPKFile
) { 
1678         err 
= writeAPK(bundle
, assets
, String8(outputAPKFile
)); 
1679         if (err 
!= NO_ERROR
) { 
1680             fprintf(stderr
, "ERROR: packaging of '%s' failed\n", outputAPKFile
); 
1685     // If we've been asked to generate a dependency file, we need to finish up here. 
1686     // the writeResourceSymbols and writeAPK functions have already written the target 
1687     // half of the dependency file, now we need to write the prerequisites. (files that 
1688     // the R.java file or .ap_ file depend on) 
1689     if (bundle
->getGenDependencies()) { 
1690         // Now that writeResourceSymbols or writeAPK has taken care of writing 
1691         // the targets to our dependency file, we'll write the prereqs 
1692         fp 
= fopen(dependencyFile
, "a+"); 
1694         bool includeRaw 
= (outputAPKFile 
!= NULL
); 
1695         err 
= writeDependencyPreReqs(bundle
, assets
, fp
, includeRaw
); 
1696         // Also manually add the AndroidManifeset since it's not under res/ or assets/ 
1697         // and therefore was not added to our pathstores during slurping 
1698         fprintf(fp
, "%s \\\n", bundle
->getAndroidManifestFile()); 
1704     if (SourcePos::hasErrors()) { 
1705         SourcePos::printErrors(stderr
); 
1713  *  -S flag points to a source directory containing drawable* folders 
1714  *  -C flag points to destination directory. The folder structure in the 
1715  *     source directory will be mirrored to the destination (cache) directory 
1718  *  Destination directory will be updated to match the PNG files in 
1719  *  the source directory.  
1721 int doCrunch(Bundle
* bundle
) 
1723     fprintf(stdout
, "Crunching PNG Files in "); 
1724     fprintf(stdout
, "source dir: %s\n", bundle
->getResourceSourceDirs()[0]); 
1725     fprintf(stdout
, "To destination dir: %s\n", bundle
->getCrunchedOutputDir()); 
1727     updatePreProcessedCache(bundle
);