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