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