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