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     DEBUGGABLE_ATTR = 0x0101000f,
    350     VERSION_CODE_ATTR = 0x0101021b,
    351     VERSION_NAME_ATTR = 0x0101021c,
    352     SCREEN_ORIENTATION_ATTR = 0x0101001e,
    353     MIN_SDK_VERSION_ATTR = 0x0101020c,
    354     MAX_SDK_VERSION_ATTR = 0x01010271,
    355     REQ_TOUCH_SCREEN_ATTR = 0x01010227,
    356     REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
    357     REQ_HARD_KEYBOARD_ATTR = 0x01010229,
    358     REQ_NAVIGATION_ATTR = 0x0101022a,
    359     REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
    360     TARGET_SDK_VERSION_ATTR = 0x01010270,
    361     TEST_ONLY_ATTR = 0x01010272,
    362     ANY_DENSITY_ATTR = 0x0101026c,
    363     GL_ES_VERSION_ATTR = 0x01010281,
    364     SMALL_SCREEN_ATTR = 0x01010284,
    365     NORMAL_SCREEN_ATTR = 0x01010285,
    366     LARGE_SCREEN_ATTR = 0x01010286,
    367     XLARGE_SCREEN_ATTR = 0x010102bf,
    368     REQUIRED_ATTR = 0x0101028e,
    369     SCREEN_SIZE_ATTR = 0x010102ca,
    370     SCREEN_DENSITY_ATTR = 0x010102cb,
    371     REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
    372     COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
    373     LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
    374     PUBLIC_KEY_ATTR = 0x010103a6,
    375 };
    376 
    377 const char *getComponentName(String8 &pkgName, String8 &componentName) {
    378     ssize_t idx = componentName.find(".");
    379     String8 retStr(pkgName);
    380     if (idx == 0) {
    381         retStr += componentName;
    382     } else if (idx < 0) {
    383         retStr += ".";
    384         retStr += componentName;
    385     } else {
    386         return componentName.string();
    387     }
    388     return retStr.string();
    389 }
    390 
    391 static void printCompatibleScreens(ResXMLTree& tree) {
    392     size_t len;
    393     ResXMLTree::event_code_t code;
    394     int depth = 0;
    395     bool first = true;
    396     printf("compatible-screens:");
    397     while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
    398         if (code == ResXMLTree::END_TAG) {
    399             depth--;
    400             if (depth < 0) {
    401                 break;
    402             }
    403             continue;
    404         }
    405         if (code != ResXMLTree::START_TAG) {
    406             continue;
    407         }
    408         depth++;
    409         String8 tag(tree.getElementName(&len));
    410         if (tag == "screen") {
    411             int32_t screenSize = getIntegerAttribute(tree,
    412                     SCREEN_SIZE_ATTR, NULL, -1);
    413             int32_t screenDensity = getIntegerAttribute(tree,
    414                     SCREEN_DENSITY_ATTR, NULL, -1);
    415             if (screenSize > 0 && screenDensity > 0) {
    416                 if (!first) {
    417                     printf(",");
    418                 }
    419                 first = false;
    420                 printf("'%d/%d'", screenSize, screenDensity);
    421             }
    422         }
    423     }
    424     printf("\n");
    425 }
    426 
    427 /*
    428  * Handle the "dump" command, to extract select data from an archive.
    429  */
    430 extern char CONSOLE_DATA[2925]; // see EOF
    431 int doDump(Bundle* bundle)
    432 {
    433     status_t result = UNKNOWN_ERROR;
    434     Asset* asset = NULL;
    435 
    436     if (bundle->getFileSpecCount() < 1) {
    437         fprintf(stderr, "ERROR: no dump option specified\n");
    438         return 1;
    439     }
    440 
    441     if (bundle->getFileSpecCount() < 2) {
    442         fprintf(stderr, "ERROR: no dump file specified\n");
    443         return 1;
    444     }
    445 
    446     const char* option = bundle->getFileSpecEntry(0);
    447     const char* filename = bundle->getFileSpecEntry(1);
    448 
    449     AssetManager assets;
    450     void* assetsCookie;
    451     if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
    452         fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
    453         return 1;
    454     }
    455 
    456     // Make a dummy config for retrieving resources...  we need to supply
    457     // non-default values for some configs so that we can retrieve resources
    458     // in the app that don't have a default.  The most important of these is
    459     // the API version because key resources like icons will have an implicit
    460     // version if they are using newer config types like density.
    461     ResTable_config config;
    462     config.language[0] = 'e';
    463     config.language[1] = 'n';
    464     config.country[0] = 'U';
    465     config.country[1] = 'S';
    466     config.orientation = ResTable_config::ORIENTATION_PORT;
    467     config.density = ResTable_config::DENSITY_MEDIUM;
    468     config.sdkVersion = 10000; // Very high.
    469     config.screenWidthDp = 320;
    470     config.screenHeightDp = 480;
    471     config.smallestScreenWidthDp = 320;
    472     assets.setConfiguration(config);
    473 
    474     const ResTable& res = assets.getResources(false);
    475     if (&res == NULL) {
    476         fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
    477         goto bail;
    478     }
    479 
    480     if (strcmp("resources", option) == 0) {
    481 #ifndef HAVE_ANDROID_OS
    482         res.print(bundle->getValues());
    483 #endif
    484 
    485     } else if (strcmp("strings", option) == 0) {
    486         const ResStringPool* pool = res.getTableStringBlock(0);
    487         printStringPool(pool);
    488 
    489     } else if (strcmp("xmltree", option) == 0) {
    490         if (bundle->getFileSpecCount() < 3) {
    491             fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
    492             goto bail;
    493         }
    494 
    495         for (int i=2; i<bundle->getFileSpecCount(); i++) {
    496             const char* resname = bundle->getFileSpecEntry(i);
    497             ResXMLTree tree;
    498             asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
    499             if (asset == NULL) {
    500                 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
    501                 goto bail;
    502             }
    503 
    504             if (tree.setTo(asset->getBuffer(true),
    505                            asset->getLength()) != NO_ERROR) {
    506                 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
    507                 goto bail;
    508             }
    509             tree.restart();
    510             printXMLBlock(&tree);
    511             tree.uninit();
    512             delete asset;
    513             asset = NULL;
    514         }
    515 
    516     } else if (strcmp("xmlstrings", option) == 0) {
    517         if (bundle->getFileSpecCount() < 3) {
    518             fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
    519             goto bail;
    520         }
    521 
    522         for (int i=2; i<bundle->getFileSpecCount(); i++) {
    523             const char* resname = bundle->getFileSpecEntry(i);
    524             ResXMLTree tree;
    525             asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
    526             if (asset == NULL) {
    527                 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
    528                 goto bail;
    529             }
    530 
    531             if (tree.setTo(asset->getBuffer(true),
    532                            asset->getLength()) != NO_ERROR) {
    533                 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
    534                 goto bail;
    535             }
    536             printStringPool(&tree.getStrings());
    537             delete asset;
    538             asset = NULL;
    539         }
    540 
    541     } else {
    542         ResXMLTree tree;
    543         asset = assets.openNonAsset("AndroidManifest.xml",
    544                                             Asset::ACCESS_BUFFER);
    545         if (asset == NULL) {
    546             fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
    547             goto bail;
    548         }
    549 
    550         if (tree.setTo(asset->getBuffer(true),
    551                        asset->getLength()) != NO_ERROR) {
    552             fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
    553             goto bail;
    554         }
    555         tree.restart();
    556 
    557         if (strcmp("permissions", option) == 0) {
    558             size_t len;
    559             ResXMLTree::event_code_t code;
    560             int depth = 0;
    561             while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
    562                 if (code == ResXMLTree::END_TAG) {
    563                     depth--;
    564                     continue;
    565                 }
    566                 if (code != ResXMLTree::START_TAG) {
    567                     continue;
    568                 }
    569                 depth++;
    570                 String8 tag(tree.getElementName(&len));
    571                 //printf("Depth %d tag %s\n", depth, tag.string());
    572                 if (depth == 1) {
    573                     if (tag != "manifest") {
    574                         fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
    575                         goto bail;
    576                     }
    577                     String8 pkg = getAttribute(tree, NULL, "package", NULL);
    578                     printf("package: %s\n", pkg.string());
    579                 } else if (depth == 2 && tag == "permission") {
    580                     String8 error;
    581                     String8 name = getAttribute(tree, NAME_ATTR, &error);
    582                     if (error != "") {
    583                         fprintf(stderr, "ERROR: %s\n", error.string());
    584                         goto bail;
    585                     }
    586                     printf("permission: %s\n", name.string());
    587                 } else if (depth == 2 && tag == "uses-permission") {
    588                     String8 error;
    589                     String8 name = getAttribute(tree, NAME_ATTR, &error);
    590                     if (error != "") {
    591                         fprintf(stderr, "ERROR: %s\n", error.string());
    592                         goto bail;
    593                     }
    594                     printf("uses-permission: %s\n", name.string());
    595                     int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
    596                     if (!req) {
    597                         printf("optional-permission: %s\n", name.string());
    598                     }
    599                 }
    600             }
    601         } else if (strcmp("badging", option) == 0) {
    602             Vector<String8> locales;
    603             res.getLocales(&locales);
    604 
    605             Vector<ResTable_config> configs;
    606             res.getConfigurations(&configs);
    607             SortedVector<int> densities;
    608             const size_t NC = configs.size();
    609             for (size_t i=0; i<NC; i++) {
    610                 int dens = configs[i].density;
    611                 if (dens == 0) dens = 160;
    612                 densities.add(dens);
    613             }
    614 
    615             size_t len;
    616             ResXMLTree::event_code_t code;
    617             int depth = 0;
    618             String8 error;
    619             bool withinActivity = false;
    620             bool isMainActivity = false;
    621             bool isLauncherActivity = false;
    622             bool isSearchable = false;
    623             bool withinApplication = false;
    624             bool withinReceiver = false;
    625             bool withinService = false;
    626             bool withinIntentFilter = false;
    627             bool hasMainActivity = false;
    628             bool hasOtherActivities = false;
    629             bool hasOtherReceivers = false;
    630             bool hasOtherServices = false;
    631             bool hasWallpaperService = false;
    632             bool hasImeService = false;
    633             bool hasWidgetReceivers = false;
    634             bool hasIntentFilter = false;
    635             bool actMainActivity = false;
    636             bool actWidgetReceivers = false;
    637             bool actImeService = false;
    638             bool actWallpaperService = false;
    639 
    640             // These two implement the implicit permissions that are granted
    641             // to pre-1.6 applications.
    642             bool hasWriteExternalStoragePermission = false;
    643             bool hasReadPhoneStatePermission = false;
    644 
    645             // If an app requests write storage, they will also get read storage.
    646             bool hasReadExternalStoragePermission = false;
    647 
    648             // Implement transition to read and write call log.
    649             bool hasReadContactsPermission = false;
    650             bool hasWriteContactsPermission = false;
    651             bool hasReadCallLogPermission = false;
    652             bool hasWriteCallLogPermission = false;
    653 
    654             // This next group of variables is used to implement a group of
    655             // backward-compatibility heuristics necessitated by the addition of
    656             // some new uses-feature constants in 2.1 and 2.2. In most cases, the
    657             // heuristic is "if an app requests a permission but doesn't explicitly
    658             // request the corresponding <uses-feature>, presume it's there anyway".
    659             bool specCameraFeature = false; // camera-related
    660             bool specCameraAutofocusFeature = false;
    661             bool reqCameraAutofocusFeature = false;
    662             bool reqCameraFlashFeature = false;
    663             bool hasCameraPermission = false;
    664             bool specLocationFeature = false; // location-related
    665             bool specNetworkLocFeature = false;
    666             bool reqNetworkLocFeature = false;
    667             bool specGpsFeature = false;
    668             bool reqGpsFeature = false;
    669             bool hasMockLocPermission = false;
    670             bool hasCoarseLocPermission = false;
    671             bool hasGpsPermission = false;
    672             bool hasGeneralLocPermission = false;
    673             bool specBluetoothFeature = false; // Bluetooth API-related
    674             bool hasBluetoothPermission = false;
    675             bool specMicrophoneFeature = false; // microphone-related
    676             bool hasRecordAudioPermission = false;
    677             bool specWiFiFeature = false;
    678             bool hasWiFiPermission = false;
    679             bool specTelephonyFeature = false; // telephony-related
    680             bool reqTelephonySubFeature = false;
    681             bool hasTelephonyPermission = false;
    682             bool specTouchscreenFeature = false; // touchscreen-related
    683             bool specMultitouchFeature = false;
    684             bool reqDistinctMultitouchFeature = false;
    685             bool specScreenPortraitFeature = false;
    686             bool specScreenLandscapeFeature = false;
    687             bool reqScreenPortraitFeature = false;
    688             bool reqScreenLandscapeFeature = false;
    689             // 2.2 also added some other features that apps can request, but that
    690             // have no corresponding permission, so we cannot implement any
    691             // back-compatibility heuristic for them. The below are thus unnecessary
    692             // (but are retained here for documentary purposes.)
    693             //bool specCompassFeature = false;
    694             //bool specAccelerometerFeature = false;
    695             //bool specProximityFeature = false;
    696             //bool specAmbientLightFeature = false;
    697             //bool specLiveWallpaperFeature = false;
    698 
    699             int targetSdk = 0;
    700             int smallScreen = 1;
    701             int normalScreen = 1;
    702             int largeScreen = 1;
    703             int xlargeScreen = 1;
    704             int anyDensity = 1;
    705             int requiresSmallestWidthDp = 0;
    706             int compatibleWidthLimitDp = 0;
    707             int largestWidthLimitDp = 0;
    708             String8 pkg;
    709             String8 activityName;
    710             String8 activityLabel;
    711             String8 activityIcon;
    712             String8 receiverName;
    713             String8 serviceName;
    714             while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
    715                 if (code == ResXMLTree::END_TAG) {
    716                     depth--;
    717                     if (depth < 2) {
    718                         withinApplication = false;
    719                     } else if (depth < 3) {
    720                         if (withinActivity && isMainActivity && isLauncherActivity) {
    721                             const char *aName = getComponentName(pkg, activityName);
    722                             printf("launchable-activity:");
    723                             if (aName != NULL) {
    724                                 printf(" name='%s' ", aName);
    725                             }
    726                             printf(" label='%s' icon='%s'\n",
    727                                     activityLabel.string(),
    728                                     activityIcon.string());
    729                         }
    730                         if (!hasIntentFilter) {
    731                             hasOtherActivities |= withinActivity;
    732                             hasOtherReceivers |= withinReceiver;
    733                             hasOtherServices |= withinService;
    734                         }
    735                         withinActivity = false;
    736                         withinService = false;
    737                         withinReceiver = false;
    738                         hasIntentFilter = false;
    739                         isMainActivity = isLauncherActivity = false;
    740                     } else if (depth < 4) {
    741                         if (withinIntentFilter) {
    742                             if (withinActivity) {
    743                                 hasMainActivity |= actMainActivity;
    744                                 hasOtherActivities |= !actMainActivity;
    745                             } else if (withinReceiver) {
    746                                 hasWidgetReceivers |= actWidgetReceivers;
    747                                 hasOtherReceivers |= !actWidgetReceivers;
    748                             } else if (withinService) {
    749                                 hasImeService |= actImeService;
    750                                 hasWallpaperService |= actWallpaperService;
    751                                 hasOtherServices |= (!actImeService && !actWallpaperService);
    752                             }
    753                         }
    754                         withinIntentFilter = false;
    755                     }
    756                     continue;
    757                 }
    758                 if (code != ResXMLTree::START_TAG) {
    759                     continue;
    760                 }
    761                 depth++;
    762                 String8 tag(tree.getElementName(&len));
    763                 //printf("Depth %d,  %s\n", depth, tag.string());
    764                 if (depth == 1) {
    765                     if (tag != "manifest") {
    766                         fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
    767                         goto bail;
    768                     }
    769                     pkg = getAttribute(tree, NULL, "package", NULL);
    770                     printf("package: name='%s' ", pkg.string());
    771                     int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
    772                     if (error != "") {
    773                         fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
    774                         goto bail;
    775                     }
    776                     if (versionCode > 0) {
    777                         printf("versionCode='%d' ", versionCode);
    778                     } else {
    779                         printf("versionCode='' ");
    780                     }
    781                     String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
    782                     if (error != "") {
    783                         fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
    784                         goto bail;
    785                     }
    786                     printf("versionName='%s'\n", versionName.string());
    787                 } else if (depth == 2) {
    788                     withinApplication = false;
    789                     if (tag == "application") {
    790                         withinApplication = true;
    791 
    792                         String8 label;
    793                         const size_t NL = locales.size();
    794                         for (size_t i=0; i<NL; i++) {
    795                             const char* localeStr =  locales[i].string();
    796                             assets.setLocale(localeStr != NULL ? localeStr : "");
    797                             String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
    798                             if (llabel != "") {
    799                                 if (localeStr == NULL || strlen(localeStr) == 0) {
    800                                     label = llabel;
    801                                     printf("application-label:'%s'\n", llabel.string());
    802                                 } else {
    803                                     if (label == "") {
    804                                         label = llabel;
    805                                     }
    806                                     printf("application-label-%s:'%s'\n", localeStr,
    807                                             llabel.string());
    808                                 }
    809                             }
    810                         }
    811 
    812                         ResTable_config tmpConfig = config;
    813                         const size_t ND = densities.size();
    814                         for (size_t i=0; i<ND; i++) {
    815                             tmpConfig.density = densities[i];
    816                             assets.setConfiguration(tmpConfig);
    817                             String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
    818                             if (icon != "") {
    819                                 printf("application-icon-%d:'%s'\n", densities[i], icon.string());
    820                             }
    821                         }
    822                         assets.setConfiguration(config);
    823 
    824                         String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
    825                         if (error != "") {
    826                             fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
    827                             goto bail;
    828                         }
    829                         int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
    830                         if (error != "") {
    831                             fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
    832                             goto bail;
    833                         }
    834                         printf("application: label='%s' ", label.string());
    835                         printf("icon='%s'\n", icon.string());
    836                         if (testOnly != 0) {
    837                             printf("testOnly='%d'\n", testOnly);
    838                         }
    839 
    840                         int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0);
    841                         if (error != "") {
    842                             fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string());
    843                             goto bail;
    844                         }
    845                         if (debuggable != 0) {
    846                             printf("application-debuggable\n");
    847                         }
    848                     } else if (tag == "uses-sdk") {
    849                         int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
    850                         if (error != "") {
    851                             error = "";
    852                             String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
    853                             if (error != "") {
    854                                 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
    855                                         error.string());
    856                                 goto bail;
    857                             }
    858                             if (name == "Donut") targetSdk = 4;
    859                             printf("sdkVersion:'%s'\n", name.string());
    860                         } else if (code != -1) {
    861                             targetSdk = code;
    862                             printf("sdkVersion:'%d'\n", code);
    863                         }
    864                         code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
    865                         if (code != -1) {
    866                             printf("maxSdkVersion:'%d'\n", code);
    867                         }
    868                         code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
    869                         if (error != "") {
    870                             error = "";
    871                             String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
    872                             if (error != "") {
    873                                 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
    874                                         error.string());
    875                                 goto bail;
    876                             }
    877                             if (name == "Donut" && targetSdk < 4) targetSdk = 4;
    878                             printf("targetSdkVersion:'%s'\n", name.string());
    879                         } else if (code != -1) {
    880                             if (targetSdk < code) {
    881                                 targetSdk = code;
    882                             }
    883                             printf("targetSdkVersion:'%d'\n", code);
    884                         }
    885                     } else if (tag == "uses-configuration") {
    886                         int32_t reqTouchScreen = getIntegerAttribute(tree,
    887                                 REQ_TOUCH_SCREEN_ATTR, NULL, 0);
    888                         int32_t reqKeyboardType = getIntegerAttribute(tree,
    889                                 REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
    890                         int32_t reqHardKeyboard = getIntegerAttribute(tree,
    891                                 REQ_HARD_KEYBOARD_ATTR, NULL, 0);
    892                         int32_t reqNavigation = getIntegerAttribute(tree,
    893                                 REQ_NAVIGATION_ATTR, NULL, 0);
    894                         int32_t reqFiveWayNav = getIntegerAttribute(tree,
    895                                 REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
    896                         printf("uses-configuration:");
    897                         if (reqTouchScreen != 0) {
    898                             printf(" reqTouchScreen='%d'", reqTouchScreen);
    899                         }
    900                         if (reqKeyboardType != 0) {
    901                             printf(" reqKeyboardType='%d'", reqKeyboardType);
    902                         }
    903                         if (reqHardKeyboard != 0) {
    904                             printf(" reqHardKeyboard='%d'", reqHardKeyboard);
    905                         }
    906                         if (reqNavigation != 0) {
    907                             printf(" reqNavigation='%d'", reqNavigation);
    908                         }
    909                         if (reqFiveWayNav != 0) {
    910                             printf(" reqFiveWayNav='%d'", reqFiveWayNav);
    911                         }
    912                         printf("\n");
    913                     } else if (tag == "supports-screens") {
    914                         smallScreen = getIntegerAttribute(tree,
    915                                 SMALL_SCREEN_ATTR, NULL, 1);
    916                         normalScreen = getIntegerAttribute(tree,
    917                                 NORMAL_SCREEN_ATTR, NULL, 1);
    918                         largeScreen = getIntegerAttribute(tree,
    919                                 LARGE_SCREEN_ATTR, NULL, 1);
    920                         xlargeScreen = getIntegerAttribute(tree,
    921                                 XLARGE_SCREEN_ATTR, NULL, 1);
    922                         anyDensity = getIntegerAttribute(tree,
    923                                 ANY_DENSITY_ATTR, NULL, 1);
    924                         requiresSmallestWidthDp = getIntegerAttribute(tree,
    925                                 REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
    926                         compatibleWidthLimitDp = getIntegerAttribute(tree,
    927                                 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
    928                         largestWidthLimitDp = getIntegerAttribute(tree,
    929                                 LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
    930                     } else if (tag == "uses-feature") {
    931                         String8 name = getAttribute(tree, NAME_ATTR, &error);
    932 
    933                         if (name != "" && error == "") {
    934                             int req = getIntegerAttribute(tree,
    935                                     REQUIRED_ATTR, NULL, 1);
    936 
    937                             if (name == "android.hardware.camera") {
    938                                 specCameraFeature = true;
    939                             } else if (name == "android.hardware.camera.autofocus") {
    940                                 // these have no corresponding permission to check for,
    941                                 // but should imply the foundational camera permission
    942                                 reqCameraAutofocusFeature = reqCameraAutofocusFeature || req;
    943                                 specCameraAutofocusFeature = true;
    944                             } else if (req && (name == "android.hardware.camera.flash")) {
    945                                 // these have no corresponding permission to check for,
    946                                 // but should imply the foundational camera permission
    947                                 reqCameraFlashFeature = true;
    948                             } else if (name == "android.hardware.location") {
    949                                 specLocationFeature = true;
    950                             } else if (name == "android.hardware.location.network") {
    951                                 specNetworkLocFeature = true;
    952                                 reqNetworkLocFeature = reqNetworkLocFeature || req;
    953                             } else if (name == "android.hardware.location.gps") {
    954                                 specGpsFeature = true;
    955                                 reqGpsFeature = reqGpsFeature || req;
    956                             } else if (name == "android.hardware.bluetooth") {
    957                                 specBluetoothFeature = true;
    958                             } else if (name == "android.hardware.touchscreen") {
    959                                 specTouchscreenFeature = true;
    960                             } else if (name == "android.hardware.touchscreen.multitouch") {
    961                                 specMultitouchFeature = true;
    962                             } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
    963                                 reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req;
    964                             } else if (name == "android.hardware.microphone") {
    965                                 specMicrophoneFeature = true;
    966                             } else if (name == "android.hardware.wifi") {
    967                                 specWiFiFeature = true;
    968                             } else if (name == "android.hardware.telephony") {
    969                                 specTelephonyFeature = true;
    970                             } else if (req && (name == "android.hardware.telephony.gsm" ||
    971                                                name == "android.hardware.telephony.cdma")) {
    972                                 // these have no corresponding permission to check for,
    973                                 // but should imply the foundational telephony permission
    974                                 reqTelephonySubFeature = true;
    975                             } else if (name == "android.hardware.screen.portrait") {
    976                                 specScreenPortraitFeature = true;
    977                             } else if (name == "android.hardware.screen.landscape") {
    978                                 specScreenLandscapeFeature = true;
    979                             }
    980                             printf("uses-feature%s:'%s'\n",
    981                                     req ? "" : "-not-required", name.string());
    982                         } else {
    983                             int vers = getIntegerAttribute(tree,
    984                                     GL_ES_VERSION_ATTR, &error);
    985                             if (error == "") {
    986                                 printf("uses-gl-es:'0x%x'\n", vers);
    987                             }
    988                         }
    989                     } else if (tag == "uses-permission") {
    990                         String8 name = getAttribute(tree, NAME_ATTR, &error);
    991                         if (name != "" && error == "") {
    992                             if (name == "android.permission.CAMERA") {
    993                                 hasCameraPermission = true;
    994                             } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
    995                                 hasGpsPermission = true;
    996                             } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
    997                                 hasMockLocPermission = true;
    998                             } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
    999                                 hasCoarseLocPermission = true;
   1000                             } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
   1001                                        name == "android.permission.INSTALL_LOCATION_PROVIDER") {
   1002                                 hasGeneralLocPermission = true;
   1003                             } else if (name == "android.permission.BLUETOOTH" ||
   1004                                        name == "android.permission.BLUETOOTH_ADMIN") {
   1005                                 hasBluetoothPermission = true;
   1006                             } else if (name == "android.permission.RECORD_AUDIO") {
   1007                                 hasRecordAudioPermission = true;
   1008                             } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
   1009                                        name == "android.permission.CHANGE_WIFI_STATE" ||
   1010                                        name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
   1011                                 hasWiFiPermission = true;
   1012                             } else if (name == "android.permission.CALL_PHONE" ||
   1013                                        name == "android.permission.CALL_PRIVILEGED" ||
   1014                                        name == "android.permission.MODIFY_PHONE_STATE" ||
   1015                                        name == "android.permission.PROCESS_OUTGOING_CALLS" ||
   1016                                        name == "android.permission.READ_SMS" ||
   1017                                        name == "android.permission.RECEIVE_SMS" ||
   1018                                        name == "android.permission.RECEIVE_MMS" ||
   1019                                        name == "android.permission.RECEIVE_WAP_PUSH" ||
   1020                                        name == "android.permission.SEND_SMS" ||
   1021                                        name == "android.permission.WRITE_APN_SETTINGS" ||
   1022                                        name == "android.permission.WRITE_SMS") {
   1023                                 hasTelephonyPermission = true;
   1024                             } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
   1025                                 hasWriteExternalStoragePermission = true;
   1026                             } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
   1027                                 hasReadExternalStoragePermission = true;
   1028                             } else if (name == "android.permission.READ_PHONE_STATE") {
   1029                                 hasReadPhoneStatePermission = true;
   1030                             } else if (name == "android.permission.READ_CONTACTS") {
   1031                                 hasReadContactsPermission = true;
   1032                             } else if (name == "android.permission.WRITE_CONTACTS") {
   1033                                 hasWriteContactsPermission = true;
   1034                             } else if (name == "android.permission.READ_CALL_LOG") {
   1035                                 hasReadCallLogPermission = true;
   1036                             } else if (name == "android.permission.WRITE_CALL_LOG") {
   1037                                 hasWriteCallLogPermission = true;
   1038                             }
   1039                             printf("uses-permission:'%s'\n", name.string());
   1040                             int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
   1041                             if (!req) {
   1042                                 printf("optional-permission:'%s'\n", name.string());
   1043                             }
   1044                         } else {
   1045                             fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
   1046                                     error.string());
   1047                             goto bail;
   1048                         }
   1049                     } else if (tag == "uses-package") {
   1050                         String8 name = getAttribute(tree, NAME_ATTR, &error);
   1051                         if (name != "" && error == "") {
   1052                             printf("uses-package:'%s'\n", name.string());
   1053                         } else {
   1054                             fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
   1055                                     error.string());
   1056                                 goto bail;
   1057                         }
   1058                     } else if (tag == "original-package") {
   1059                         String8 name = getAttribute(tree, NAME_ATTR, &error);
   1060                         if (name != "" && error == "") {
   1061                             printf("original-package:'%s'\n", name.string());
   1062                         } else {
   1063                             fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
   1064                                     error.string());
   1065                                 goto bail;
   1066                         }
   1067                     } else if (tag == "supports-gl-texture") {
   1068                         String8 name = getAttribute(tree, NAME_ATTR, &error);
   1069                         if (name != "" && error == "") {
   1070                             printf("supports-gl-texture:'%s'\n", name.string());
   1071                         } else {
   1072                             fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
   1073                                     error.string());
   1074                                 goto bail;
   1075                         }
   1076                     } else if (tag == "compatible-screens") {
   1077                         printCompatibleScreens(tree);
   1078                         depth--;
   1079                     } else if (tag == "package-verifier") {
   1080                         String8 name = getAttribute(tree, NAME_ATTR, &error);
   1081                         if (name != "" && error == "") {
   1082                             String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
   1083                             if (publicKey != "" && error == "") {
   1084                                 printf("package-verifier: name='%s' publicKey='%s'\n",
   1085                                         name.string(), publicKey.string());
   1086                             }
   1087                         }
   1088                     }
   1089                 } else if (depth == 3 && withinApplication) {
   1090                     withinActivity = false;
   1091                     withinReceiver = false;
   1092                     withinService = false;
   1093                     hasIntentFilter = false;
   1094                     if(tag == "activity") {
   1095                         withinActivity = true;
   1096                         activityName = getAttribute(tree, NAME_ATTR, &error);
   1097                         if (error != "") {
   1098                             fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
   1099                             goto bail;
   1100                         }
   1101 
   1102                         activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
   1103                         if (error != "") {
   1104                             fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string());
   1105                             goto bail;
   1106                         }
   1107 
   1108                         activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
   1109                         if (error != "") {
   1110                             fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
   1111                             goto bail;
   1112                         }
   1113 
   1114                         int32_t orien = getResolvedIntegerAttribute(&res, tree,
   1115                                 SCREEN_ORIENTATION_ATTR, &error);
   1116                         if (error == "") {
   1117                             if (orien == 0 || orien == 6 || orien == 8) {
   1118                                 // Requests landscape, sensorLandscape, or reverseLandscape.
   1119                                 reqScreenLandscapeFeature = true;
   1120                             } else if (orien == 1 || orien == 7 || orien == 9) {
   1121                                 // Requests portrait, sensorPortrait, or reversePortrait.
   1122                                 reqScreenPortraitFeature = true;
   1123                             }
   1124                         }
   1125                     } else if (tag == "uses-library") {
   1126                         String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
   1127                         if (error != "") {
   1128                             fprintf(stderr, "ERROR getting 'android:name' attribute for uses-library: %s\n", error.string());
   1129                             goto bail;
   1130                         }
   1131                         int req = getIntegerAttribute(tree,
   1132                                 REQUIRED_ATTR, NULL, 1);
   1133                         printf("uses-library%s:'%s'\n",
   1134                                 req ? "" : "-not-required", libraryName.string());
   1135                     } else if (tag == "receiver") {
   1136                         withinReceiver = true;
   1137                         receiverName = getAttribute(tree, NAME_ATTR, &error);
   1138 
   1139                         if (error != "") {
   1140                             fprintf(stderr, "ERROR getting 'android:name' attribute for receiver: %s\n", error.string());
   1141                             goto bail;
   1142                         }
   1143                     } else if (tag == "service") {
   1144                         withinService = true;
   1145                         serviceName = getAttribute(tree, NAME_ATTR, &error);
   1146 
   1147                         if (error != "") {
   1148                             fprintf(stderr, "ERROR getting 'android:name' attribute for service: %s\n", error.string());
   1149                             goto bail;
   1150                         }
   1151                     }
   1152                 } else if ((depth == 4) && (tag == "intent-filter")) {
   1153                     hasIntentFilter = true;
   1154                     withinIntentFilter = true;
   1155                     actMainActivity = actWidgetReceivers = actImeService = actWallpaperService = false;
   1156                 } else if ((depth == 5) && withinIntentFilter){
   1157                     String8 action;
   1158                     if (tag == "action") {
   1159                         action = getAttribute(tree, NAME_ATTR, &error);
   1160                         if (error != "") {
   1161                             fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
   1162                             goto bail;
   1163                         }
   1164                         if (withinActivity) {
   1165                             if (action == "android.intent.action.MAIN") {
   1166                                 isMainActivity = true;
   1167                                 actMainActivity = true;
   1168                             }
   1169                         } else if (withinReceiver) {
   1170                             if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
   1171                                 actWidgetReceivers = true;
   1172                             }
   1173                         } else if (withinService) {
   1174                             if (action == "android.view.InputMethod") {
   1175                                 actImeService = true;
   1176                             } else if (action == "android.service.wallpaper.WallpaperService") {
   1177                                 actWallpaperService = true;
   1178                             }
   1179                         }
   1180                         if (action == "android.intent.action.SEARCH") {
   1181                             isSearchable = true;
   1182                         }
   1183                     }
   1184 
   1185                     if (tag == "category") {
   1186                         String8 category = getAttribute(tree, NAME_ATTR, &error);
   1187                         if (error != "") {
   1188                             fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string());
   1189                             goto bail;
   1190                         }
   1191                         if (withinActivity) {
   1192                             if (category == "android.intent.category.LAUNCHER") {
   1193                                 isLauncherActivity = true;
   1194                             }
   1195                         }
   1196                     }
   1197                 }
   1198             }
   1199 
   1200             // Pre-1.6 implicitly granted permission compatibility logic
   1201             if (targetSdk < 4) {
   1202                 if (!hasWriteExternalStoragePermission) {
   1203                     printf("uses-permission:'android.permission.WRITE_EXTERNAL_STORAGE'\n");
   1204                     printf("uses-implied-permission:'android.permission.WRITE_EXTERNAL_STORAGE'," \
   1205                             "'targetSdkVersion < 4'\n");
   1206                     hasWriteExternalStoragePermission = true;
   1207                 }
   1208                 if (!hasReadPhoneStatePermission) {
   1209                     printf("uses-permission:'android.permission.READ_PHONE_STATE'\n");
   1210                     printf("uses-implied-permission:'android.permission.READ_PHONE_STATE'," \
   1211                             "'targetSdkVersion < 4'\n");
   1212                 }
   1213             }
   1214 
   1215             // If the application has requested WRITE_EXTERNAL_STORAGE, we will
   1216             // force them to always take READ_EXTERNAL_STORAGE as well.  We always
   1217             // do this (regardless of target API version) because we can't have
   1218             // an app with write permission but not read permission.
   1219             if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
   1220                 printf("uses-permission:'android.permission.READ_EXTERNAL_STORAGE'\n");
   1221                 printf("uses-implied-permission:'android.permission.READ_EXTERNAL_STORAGE'," \
   1222                         "'requested WRITE_EXTERNAL_STORAGE'\n");
   1223             }
   1224 
   1225             // Pre-JellyBean call log permission compatibility.
   1226             if (targetSdk < 16) {
   1227                 if (!hasReadCallLogPermission && hasReadContactsPermission) {
   1228                     printf("uses-permission:'android.permission.READ_CALL_LOG'\n");
   1229                     printf("uses-implied-permission:'android.permission.READ_CALL_LOG'," \
   1230                             "'targetSdkVersion < 16 and requested READ_CONTACTS'\n");
   1231                 }
   1232                 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
   1233                     printf("uses-permission:'android.permission.WRITE_CALL_LOG'\n");
   1234                     printf("uses-implied-permission:'android.permission.WRITE_CALL_LOG'," \
   1235                             "'targetSdkVersion < 16 and requested WRITE_CONTACTS'\n");
   1236                 }
   1237             }
   1238 
   1239             /* The following blocks handle printing "inferred" uses-features, based
   1240              * on whether related features or permissions are used by the app.
   1241              * Note that the various spec*Feature variables denote whether the
   1242              * relevant tag was *present* in the AndroidManfest, not that it was
   1243              * present and set to true.
   1244              */
   1245             // Camera-related back-compatibility logic
   1246             if (!specCameraFeature) {
   1247                 if (reqCameraFlashFeature) {
   1248                     // if app requested a sub-feature (autofocus or flash) and didn't
   1249                     // request the base camera feature, we infer that it meant to
   1250                     printf("uses-feature:'android.hardware.camera'\n");
   1251                     printf("uses-implied-feature:'android.hardware.camera'," \
   1252                             "'requested android.hardware.camera.flash feature'\n");
   1253                 } else if (reqCameraAutofocusFeature) {
   1254                     // if app requested a sub-feature (autofocus or flash) and didn't
   1255                     // request the base camera feature, we infer that it meant to
   1256                     printf("uses-feature:'android.hardware.camera'\n");
   1257                     printf("uses-implied-feature:'android.hardware.camera'," \
   1258                             "'requested android.hardware.camera.autofocus feature'\n");
   1259                 } else if (hasCameraPermission) {
   1260                     // if app wants to use camera but didn't request the feature, we infer
   1261                     // that it meant to, and further that it wants autofocus
   1262                     // (which was the 1.0 - 1.5 behavior)
   1263                     printf("uses-feature:'android.hardware.camera'\n");
   1264                     if (!specCameraAutofocusFeature) {
   1265                         printf("uses-feature:'android.hardware.camera.autofocus'\n");
   1266                         printf("uses-implied-feature:'android.hardware.camera.autofocus'," \
   1267                                 "'requested android.permission.CAMERA permission'\n");
   1268                     }
   1269                 }
   1270             }
   1271 
   1272             // Location-related back-compatibility logic
   1273             if (!specLocationFeature &&
   1274                 (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission ||
   1275                  hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) {
   1276                 // if app either takes a location-related permission or requests one of the
   1277                 // sub-features, we infer that it also meant to request the base location feature
   1278                 printf("uses-feature:'android.hardware.location'\n");
   1279                 printf("uses-implied-feature:'android.hardware.location'," \
   1280                         "'requested a location access permission'\n");
   1281             }
   1282             if (!specGpsFeature && hasGpsPermission) {
   1283                 // if app takes GPS (FINE location) perm but does not request the GPS
   1284                 // feature, we infer that it meant to
   1285                 printf("uses-feature:'android.hardware.location.gps'\n");
   1286                 printf("uses-implied-feature:'android.hardware.location.gps'," \
   1287                         "'requested android.permission.ACCESS_FINE_LOCATION permission'\n");
   1288             }
   1289             if (!specNetworkLocFeature && hasCoarseLocPermission) {
   1290                 // if app takes Network location (COARSE location) perm but does not request the
   1291                 // network location feature, we infer that it meant to
   1292                 printf("uses-feature:'android.hardware.location.network'\n");
   1293                 printf("uses-implied-feature:'android.hardware.location.network'," \
   1294                         "'requested android.permission.ACCESS_COARSE_LOCATION permission'\n");
   1295             }
   1296 
   1297             // Bluetooth-related compatibility logic
   1298             if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
   1299                 // if app takes a Bluetooth permission but does not request the Bluetooth
   1300                 // feature, we infer that it meant to
   1301                 printf("uses-feature:'android.hardware.bluetooth'\n");
   1302                 printf("uses-implied-feature:'android.hardware.bluetooth'," \
   1303                         "'requested android.permission.BLUETOOTH or android.permission.BLUETOOTH_ADMIN " \
   1304                         "permission and targetSdkVersion > 4'\n");
   1305             }
   1306 
   1307             // Microphone-related compatibility logic
   1308             if (!specMicrophoneFeature && hasRecordAudioPermission) {
   1309                 // if app takes the record-audio permission but does not request the microphone
   1310                 // feature, we infer that it meant to
   1311                 printf("uses-feature:'android.hardware.microphone'\n");
   1312                 printf("uses-implied-feature:'android.hardware.microphone'," \
   1313                         "'requested android.permission.RECORD_AUDIO permission'\n");
   1314             }
   1315 
   1316             // WiFi-related compatibility logic
   1317             if (!specWiFiFeature && hasWiFiPermission) {
   1318                 // if app takes one of the WiFi permissions but does not request the WiFi
   1319                 // feature, we infer that it meant to
   1320                 printf("uses-feature:'android.hardware.wifi'\n");
   1321                 printf("uses-implied-feature:'android.hardware.wifi'," \
   1322                         "'requested android.permission.ACCESS_WIFI_STATE, " \
   1323                         "android.permission.CHANGE_WIFI_STATE, or " \
   1324                         "android.permission.CHANGE_WIFI_MULTICAST_STATE permission'\n");
   1325             }
   1326 
   1327             // Telephony-related compatibility logic
   1328             if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) {
   1329                 // if app takes one of the telephony permissions or requests a sub-feature but
   1330                 // does not request the base telephony feature, we infer that it meant to
   1331                 printf("uses-feature:'android.hardware.telephony'\n");
   1332                 printf("uses-implied-feature:'android.hardware.telephony'," \
   1333                         "'requested a telephony-related permission or feature'\n");
   1334             }
   1335 
   1336             // Touchscreen-related back-compatibility logic
   1337             if (!specTouchscreenFeature) { // not a typo!
   1338                 // all apps are presumed to require a touchscreen, unless they explicitly say
   1339                 // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
   1340                 // Note that specTouchscreenFeature is true if the tag is present, regardless
   1341                 // of whether its value is true or false, so this is safe
   1342                 printf("uses-feature:'android.hardware.touchscreen'\n");
   1343                 printf("uses-implied-feature:'android.hardware.touchscreen'," \
   1344                         "'assumed you require a touch screen unless explicitly made optional'\n");
   1345             }
   1346             if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
   1347                 // if app takes one of the telephony permissions or requests a sub-feature but
   1348                 // does not request the base telephony feature, we infer that it meant to
   1349                 printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
   1350                 printf("uses-implied-feature:'android.hardware.touchscreen.multitouch'," \
   1351                         "'requested android.hardware.touchscreen.multitouch.distinct feature'\n");
   1352             }
   1353 
   1354             // Landscape/portrait-related compatibility logic
   1355             if (!specScreenLandscapeFeature && !specScreenPortraitFeature) {
   1356                 // If the app has specified any activities in its manifest
   1357                 // that request a specific orientation, then assume that
   1358                 // orientation is required.
   1359                 if (reqScreenLandscapeFeature) {
   1360                     printf("uses-feature:'android.hardware.screen.landscape'\n");
   1361                     printf("uses-implied-feature:'android.hardware.screen.landscape'," \
   1362                             "'one or more activities have specified a landscape orientation'\n");
   1363                 }
   1364                 if (reqScreenPortraitFeature) {
   1365                     printf("uses-feature:'android.hardware.screen.portrait'\n");
   1366                     printf("uses-implied-feature:'android.hardware.screen.portrait'," \
   1367                             "'one or more activities have specified a portrait orientation'\n");
   1368                 }
   1369             }
   1370 
   1371             if (hasMainActivity) {
   1372                 printf("main\n");
   1373             }
   1374             if (hasWidgetReceivers) {
   1375                 printf("app-widget\n");
   1376             }
   1377             if (hasImeService) {
   1378                 printf("ime\n");
   1379             }
   1380             if (hasWallpaperService) {
   1381                 printf("wallpaper\n");
   1382             }
   1383             if (hasOtherActivities) {
   1384                 printf("other-activities\n");
   1385             }
   1386             if (isSearchable) {
   1387                 printf("search\n");
   1388             }
   1389             if (hasOtherReceivers) {
   1390                 printf("other-receivers\n");
   1391             }
   1392             if (hasOtherServices) {
   1393                 printf("other-services\n");
   1394             }
   1395 
   1396             // For modern apps, if screen size buckets haven't been specified
   1397             // but the new width ranges have, then infer the buckets from them.
   1398             if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
   1399                     && requiresSmallestWidthDp > 0) {
   1400                 int compatWidth = compatibleWidthLimitDp;
   1401                 if (compatWidth <= 0) compatWidth = requiresSmallestWidthDp;
   1402                 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
   1403                     smallScreen = -1;
   1404                 } else {
   1405                     smallScreen = 0;
   1406                 }
   1407                 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
   1408                     normalScreen = -1;
   1409                 } else {
   1410                     normalScreen = 0;
   1411                 }
   1412                 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
   1413                     largeScreen = -1;
   1414                 } else {
   1415                     largeScreen = 0;
   1416                 }
   1417                 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
   1418                     xlargeScreen = -1;
   1419                 } else {
   1420                     xlargeScreen = 0;
   1421                 }
   1422             }
   1423 
   1424             // Determine default values for any unspecified screen sizes,
   1425             // based on the target SDK of the package.  As of 4 (donut)
   1426             // the screen size support was introduced, so all default to
   1427             // enabled.
   1428             if (smallScreen > 0) {
   1429                 smallScreen = targetSdk >= 4 ? -1 : 0;
   1430             }
   1431             if (normalScreen > 0) {
   1432                 normalScreen = -1;
   1433             }
   1434             if (largeScreen > 0) {
   1435                 largeScreen = targetSdk >= 4 ? -1 : 0;
   1436             }
   1437             if (xlargeScreen > 0) {
   1438                 // Introduced in Gingerbread.
   1439                 xlargeScreen = targetSdk >= 9 ? -1 : 0;
   1440             }
   1441             if (anyDensity > 0) {
   1442                 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
   1443                         || compatibleWidthLimitDp > 0) ? -1 : 0;
   1444             }
   1445             printf("supports-screens:");
   1446             if (smallScreen != 0) printf(" 'small'");
   1447             if (normalScreen != 0) printf(" 'normal'");
   1448             if (largeScreen != 0) printf(" 'large'");
   1449             if (xlargeScreen != 0) printf(" 'xlarge'");
   1450             printf("\n");
   1451             printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
   1452             if (requiresSmallestWidthDp > 0) {
   1453                 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
   1454             }
   1455             if (compatibleWidthLimitDp > 0) {
   1456                 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
   1457             }
   1458             if (largestWidthLimitDp > 0) {
   1459                 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
   1460             }
   1461 
   1462             printf("locales:");
   1463             const size_t NL = locales.size();
   1464             for (size_t i=0; i<NL; i++) {
   1465                 const char* localeStr =  locales[i].string();
   1466                 if (localeStr == NULL || strlen(localeStr) == 0) {
   1467                     localeStr = "--_--";
   1468                 }
   1469                 printf(" '%s'", localeStr);
   1470             }
   1471             printf("\n");
   1472 
   1473             printf("densities:");
   1474             const size_t ND = densities.size();
   1475             for (size_t i=0; i<ND; i++) {
   1476                 printf(" '%d'", densities[i]);
   1477             }
   1478             printf("\n");
   1479 
   1480             AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
   1481             if (dir != NULL) {
   1482                 if (dir->getFileCount() > 0) {
   1483                     printf("native-code:");
   1484                     for (size_t i=0; i<dir->getFileCount(); i++) {
   1485                         printf(" '%s'", dir->getFileName(i).string());
   1486                     }
   1487                     printf("\n");
   1488                 }
   1489                 delete dir;
   1490             }
   1491         } else if (strcmp("badger", option) == 0) {
   1492             printf("%s", CONSOLE_DATA);
   1493         } else if (strcmp("configurations", option) == 0) {
   1494             Vector<ResTable_config> configs;
   1495             res.getConfigurations(&configs);
   1496             const size_t N = configs.size();
   1497             for (size_t i=0; i<N; i++) {
   1498                 printf("%s\n", configs[i].toString().string());
   1499             }
   1500         } else {
   1501             fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
   1502             goto bail;
   1503         }
   1504     }
   1505 
   1506     result = NO_ERROR;
   1507 
   1508 bail:
   1509     if (asset) {
   1510         delete asset;
   1511     }
   1512     return (result != NO_ERROR);
   1513 }
   1514 
   1515 
   1516 /*
   1517  * Handle the "add" command, which wants to add files to a new or
   1518  * pre-existing archive.
   1519  */
   1520 int doAdd(Bundle* bundle)
   1521 {
   1522     ZipFile* zip = NULL;
   1523     status_t result = UNKNOWN_ERROR;
   1524     const char* zipFileName;
   1525 
   1526     if (bundle->getUpdate()) {
   1527         /* avoid confusion */
   1528         fprintf(stderr, "ERROR: can't use '-u' with add\n");
   1529         goto bail;
   1530     }
   1531 
   1532     if (bundle->getFileSpecCount() < 1) {
   1533         fprintf(stderr, "ERROR: must specify zip file name\n");
   1534         goto bail;
   1535     }
   1536     zipFileName = bundle->getFileSpecEntry(0);
   1537 
   1538     if (bundle->getFileSpecCount() < 2) {
   1539         fprintf(stderr, "NOTE: nothing to do\n");
   1540         goto bail;
   1541     }
   1542 
   1543     zip = openReadWrite(zipFileName, true);
   1544     if (zip == NULL) {
   1545         fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
   1546         goto bail;
   1547     }
   1548 
   1549     for (int i = 1; i < bundle->getFileSpecCount(); i++) {
   1550         const char* fileName = bundle->getFileSpecEntry(i);
   1551 
   1552         if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
   1553             printf(" '%s'... (from gzip)\n", fileName);
   1554             result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
   1555         } else {
   1556             if (bundle->getJunkPath()) {
   1557                 String8 storageName = String8(fileName).getPathLeaf();
   1558                 printf(" '%s' as '%s'...\n", fileName, storageName.string());
   1559                 result = zip->add(fileName, storageName.string(),
   1560                                   bundle->getCompressionMethod(), NULL);
   1561             } else {
   1562                 printf(" '%s'...\n", fileName);
   1563                 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
   1564             }
   1565         }
   1566         if (result != NO_ERROR) {
   1567             fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
   1568             if (result == NAME_NOT_FOUND)
   1569                 fprintf(stderr, ": file not found\n");
   1570             else if (result == ALREADY_EXISTS)
   1571                 fprintf(stderr, ": already exists in archive\n");
   1572             else
   1573                 fprintf(stderr, "\n");
   1574             goto bail;
   1575         }
   1576     }
   1577 
   1578     result = NO_ERROR;
   1579 
   1580 bail:
   1581     delete zip;
   1582     return (result != NO_ERROR);
   1583 }
   1584 
   1585 
   1586 /*
   1587  * Delete files from an existing archive.
   1588  */
   1589 int doRemove(Bundle* bundle)
   1590 {
   1591     ZipFile* zip = NULL;
   1592     status_t result = UNKNOWN_ERROR;
   1593     const char* zipFileName;
   1594 
   1595     if (bundle->getFileSpecCount() < 1) {
   1596         fprintf(stderr, "ERROR: must specify zip file name\n");
   1597         goto bail;
   1598     }
   1599     zipFileName = bundle->getFileSpecEntry(0);
   1600 
   1601     if (bundle->getFileSpecCount() < 2) {
   1602         fprintf(stderr, "NOTE: nothing to do\n");
   1603         goto bail;
   1604     }
   1605 
   1606     zip = openReadWrite(zipFileName, false);
   1607     if (zip == NULL) {
   1608         fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
   1609             zipFileName);
   1610         goto bail;
   1611     }
   1612 
   1613     for (int i = 1; i < bundle->getFileSpecCount(); i++) {
   1614         const char* fileName = bundle->getFileSpecEntry(i);
   1615         ZipEntry* entry;
   1616 
   1617         entry = zip->getEntryByName(fileName);
   1618         if (entry == NULL) {
   1619             printf(" '%s' NOT FOUND\n", fileName);
   1620             continue;
   1621         }
   1622 
   1623         result = zip->remove(entry);
   1624 
   1625         if (result != NO_ERROR) {
   1626             fprintf(stderr, "Unable to delete '%s' from '%s'\n",
   1627                 bundle->getFileSpecEntry(i), zipFileName);
   1628             goto bail;
   1629         }
   1630     }
   1631 
   1632     /* update the archive */
   1633     zip->flush();
   1634 
   1635 bail:
   1636     delete zip;
   1637     return (result != NO_ERROR);
   1638 }
   1639 
   1640 
   1641 /*
   1642  * Package up an asset directory and associated application files.
   1643  */
   1644 int doPackage(Bundle* bundle)
   1645 {
   1646     const char* outputAPKFile;
   1647     int retVal = 1;
   1648     status_t err;
   1649     sp<AaptAssets> assets;
   1650     int N;
   1651     FILE* fp;
   1652     String8 dependencyFile;
   1653 
   1654     // -c zz_ZZ means do pseudolocalization
   1655     ResourceFilter filter;
   1656     err = filter.parse(bundle->getConfigurations());
   1657     if (err != NO_ERROR) {
   1658         goto bail;
   1659     }
   1660     if (filter.containsPseudo()) {
   1661         bundle->setPseudolocalize(true);
   1662     }
   1663 
   1664     N = bundle->getFileSpecCount();
   1665     if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
   1666             && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDir() == NULL) {
   1667         fprintf(stderr, "ERROR: no input files\n");
   1668         goto bail;
   1669     }
   1670 
   1671     outputAPKFile = bundle->getOutputAPKFile();
   1672 
   1673     // Make sure the filenames provided exist and are of the appropriate type.
   1674     if (outputAPKFile) {
   1675         FileType type;
   1676         type = getFileType(outputAPKFile);
   1677         if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
   1678             fprintf(stderr,
   1679                 "ERROR: output file '%s' exists but is not regular file\n",
   1680                 outputAPKFile);
   1681             goto bail;
   1682         }
   1683     }
   1684 
   1685     // Load the assets.
   1686     assets = new AaptAssets();
   1687 
   1688     // Set up the resource gathering in assets if we're going to generate
   1689     // dependency files. Every time we encounter a resource while slurping
   1690     // the tree, we'll add it to these stores so we have full resource paths
   1691     // to write to a dependency file.
   1692     if (bundle->getGenDependencies()) {
   1693         sp<FilePathStore> resPathStore = new FilePathStore;
   1694         assets->setFullResPaths(resPathStore);
   1695         sp<FilePathStore> assetPathStore = new FilePathStore;
   1696         assets->setFullAssetPaths(assetPathStore);
   1697     }
   1698 
   1699     err = assets->slurpFromArgs(bundle);
   1700     if (err < 0) {
   1701         goto bail;
   1702     }
   1703 
   1704     if (bundle->getVerbose()) {
   1705         assets->print(String8());
   1706     }
   1707 
   1708     // If they asked for any fileAs that need to be compiled, do so.
   1709     if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
   1710         err = buildResources(bundle, assets);
   1711         if (err != 0) {
   1712             goto bail;
   1713         }
   1714     }
   1715 
   1716     // At this point we've read everything and processed everything.  From here
   1717     // on out it's just writing output files.
   1718     if (SourcePos::hasErrors()) {
   1719         goto bail;
   1720     }
   1721 
   1722     // Update symbols with information about which ones are needed as Java symbols.
   1723     assets->applyJavaSymbols();
   1724     if (SourcePos::hasErrors()) {
   1725         goto bail;
   1726     }
   1727 
   1728     // If we've been asked to generate a dependency file, do that here
   1729     if (bundle->getGenDependencies()) {
   1730         // If this is the packaging step, generate the dependency file next to
   1731         // the output apk (e.g. bin/resources.ap_.d)
   1732         if (outputAPKFile) {
   1733             dependencyFile = String8(outputAPKFile);
   1734             // Add the .d extension to the dependency file.
   1735             dependencyFile.append(".d");
   1736         } else {
   1737             // Else if this is the R.java dependency generation step,
   1738             // generate the dependency file in the R.java package subdirectory
   1739             // e.g. gen/com/foo/app/R.java.d
   1740             dependencyFile = String8(bundle->getRClassDir());
   1741             dependencyFile.appendPath("R.java.d");
   1742         }
   1743         // Make sure we have a clean dependency file to start with
   1744         fp = fopen(dependencyFile, "w");
   1745         fclose(fp);
   1746     }
   1747 
   1748     // Write out R.java constants
   1749     if (!assets->havePrivateSymbols()) {
   1750         if (bundle->getCustomPackage() == NULL) {
   1751             // Write the R.java file into the appropriate class directory
   1752             // e.g. gen/com/foo/app/R.java
   1753             err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
   1754         } else {
   1755             const String8 customPkg(bundle->getCustomPackage());
   1756             err = writeResourceSymbols(bundle, assets, customPkg, true);
   1757         }
   1758         if (err < 0) {
   1759             goto bail;
   1760         }
   1761         // If we have library files, we're going to write our R.java file into
   1762         // the appropriate class directory for those libraries as well.
   1763         // e.g. gen/com/foo/app/lib/R.java
   1764         if (bundle->getExtraPackages() != NULL) {
   1765             // Split on colon
   1766             String8 libs(bundle->getExtraPackages());
   1767             char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
   1768             while (packageString != NULL) {
   1769                 // Write the R.java file out with the correct package name
   1770                 err = writeResourceSymbols(bundle, assets, String8(packageString), true);
   1771                 if (err < 0) {
   1772                     goto bail;
   1773                 }
   1774                 packageString = strtok(NULL, ":");
   1775             }
   1776             libs.unlockBuffer();
   1777         }
   1778     } else {
   1779         err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
   1780         if (err < 0) {
   1781             goto bail;
   1782         }
   1783         err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
   1784         if (err < 0) {
   1785             goto bail;
   1786         }
   1787     }
   1788 
   1789     // Write out the ProGuard file
   1790     err = writeProguardFile(bundle, assets);
   1791     if (err < 0) {
   1792         goto bail;
   1793     }
   1794 
   1795     // Write the apk
   1796     if (outputAPKFile) {
   1797         err = writeAPK(bundle, assets, String8(outputAPKFile));
   1798         if (err != NO_ERROR) {
   1799             fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
   1800             goto bail;
   1801         }
   1802     }
   1803 
   1804     // If we've been asked to generate a dependency file, we need to finish up here.
   1805     // the writeResourceSymbols and writeAPK functions have already written the target
   1806     // half of the dependency file, now we need to write the prerequisites. (files that
   1807     // the R.java file or .ap_ file depend on)
   1808     if (bundle->getGenDependencies()) {
   1809         // Now that writeResourceSymbols or writeAPK has taken care of writing
   1810         // the targets to our dependency file, we'll write the prereqs
   1811         fp = fopen(dependencyFile, "a+");
   1812         fprintf(fp, " : ");
   1813         bool includeRaw = (outputAPKFile != NULL);
   1814         err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
   1815         // Also manually add the AndroidManifeset since it's not under res/ or assets/
   1816         // and therefore was not added to our pathstores during slurping
   1817         fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
   1818         fclose(fp);
   1819     }
   1820 
   1821     retVal = 0;
   1822 bail:
   1823     if (SourcePos::hasErrors()) {
   1824         SourcePos::printErrors(stderr);
   1825     }
   1826     return retVal;
   1827 }
   1828 
   1829 /*
   1830  * Do PNG Crunching
   1831  * PRECONDITIONS
   1832  *  -S flag points to a source directory containing drawable* folders
   1833  *  -C flag points to destination directory. The folder structure in the
   1834  *     source directory will be mirrored to the destination (cache) directory
   1835  *
   1836  * POSTCONDITIONS
   1837  *  Destination directory will be updated to match the PNG files in
   1838  *  the source directory.
   1839  */
   1840 int doCrunch(Bundle* bundle)
   1841 {
   1842     fprintf(stdout, "Crunching PNG Files in ");
   1843     fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
   1844     fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
   1845 
   1846     updatePreProcessedCache(bundle);
   1847 
   1848     return NO_ERROR;
   1849 }
   1850 
   1851 /*
   1852  * Do PNG Crunching on a single flag
   1853  *  -i points to a single png file
   1854  *  -o points to a single png output file
   1855  */
   1856 int doSingleCrunch(Bundle* bundle)
   1857 {
   1858     fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
   1859     fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
   1860 
   1861     String8 input(bundle->getSingleCrunchInputFile());
   1862     String8 output(bundle->getSingleCrunchOutputFile());
   1863 
   1864     if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
   1865         // we can't return the status_t as it gets truncate to the lower 8 bits.
   1866         return 42;
   1867     }
   1868 
   1869     return NO_ERROR;
   1870 }
   1871 
   1872 char CONSOLE_DATA[2925] = {
   1873     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1874     32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1875     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1876     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
   1877     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
   1878     86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
   1879     62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1880     32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1881     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
   1882     81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
   1883     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
   1884     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1885     32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
   1886     59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1887     32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1888     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
   1889     59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
   1890     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1891     10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1892     32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
   1893     58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
   1894     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
   1895     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
   1896     47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
   1897     121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1898     32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1899     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
   1900     81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
   1901     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
   1902     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1903     32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
   1904     59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1905     32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1906     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
   1907     59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
   1908     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1909     32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1910     32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
   1911     70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
   1912     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
   1913     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1914     32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
   1915     81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1916     32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1917     32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
   1918     81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
   1919     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
   1920     32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
   1921     59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
   1922     60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1923     32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
   1924     61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
   1925     61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
   1926     46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1927     32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
   1928     59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
   1929     109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
   1930     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
   1931     67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
   1932     59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
   1933     61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
   1934     32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
   1935     59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
   1936     73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
   1937     59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
   1938     32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
   1939     59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
   1940     46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
   1941     97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
   1942     46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
   1943     119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
   1944     59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
   1945     32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1946     32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
   1947     119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
   1948     59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
   1949     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1950     32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
   1951     81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
   1952     87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
   1953     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
   1954     81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
   1955     45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
   1956     32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1957     32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
   1958     81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
   1959     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
   1960     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
   1961     59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
   1962     59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1963     32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1964     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
   1965     81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
   1966     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1967     10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1968     32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
   1969     81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1970     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
   1971     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
   1972     81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
   1973     32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1974     32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1975     32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
   1976     81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
   1977     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
   1978     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1979     58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
   1980     61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1981     32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1982     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
   1983     81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
   1984     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1985     32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1986     32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
   1987     81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1988     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
   1989     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
   1990     59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
   1991     59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1992     32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   1993     32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
   1994     58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
   1995     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
   1996     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
   1997     61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
   1998     59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
   1999     32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
   2000     32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
   2001     32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
   2002     61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   2003     32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   2004     32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
   2005     59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
   2006     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
   2007     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
   2008     59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
   2009     59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   2010     32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   2011     32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
   2012     32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
   2013     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
   2014     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   2015     46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
   2016     46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   2017     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
   2018     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
   2019     59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
   2020     59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   2021     32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   2022     32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
   2023     32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
   2024     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
   2025     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
   2026     59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
   2027     59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   2028     32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   2029     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
   2030     32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
   2031     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   2032     10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   2033     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   2034     32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   2035     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
   2036   };
   2037