Home | History | Annotate | Download | only in utils
      1 /*
      2  * Copyright (C) 2006 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 //
     18 // Provide access to read-only assets.
     19 //
     20 
     21 #define LOG_TAG "asset"
     22 //#define LOG_NDEBUG 0
     23 
     24 #include <utils/AssetManager.h>
     25 #include <utils/AssetDir.h>
     26 #include <utils/Asset.h>
     27 #include <utils/Atomic.h>
     28 #include <utils/String8.h>
     29 #include <utils/ResourceTypes.h>
     30 #include <utils/String8.h>
     31 #include <utils/ZipFileRO.h>
     32 #include <utils/Log.h>
     33 #include <utils/Timers.h>
     34 #include <utils/threads.h>
     35 
     36 #include <dirent.h>
     37 #include <errno.h>
     38 #include <assert.h>
     39 
     40 using namespace android;
     41 
     42 /*
     43  * Names for default app, locale, and vendor.  We might want to change
     44  * these to be an actual locale, e.g. always use en-US as the default.
     45  */
     46 static const char* kDefaultLocale = "default";
     47 static const char* kDefaultVendor = "default";
     48 static const char* kAssetsRoot = "assets";
     49 static const char* kAppZipName = NULL; //"classes.jar";
     50 static const char* kSystemAssets = "framework/framework-res.apk";
     51 
     52 static const char* kExcludeExtension = ".EXCLUDE";
     53 
     54 static Asset* const kExcludedAsset = (Asset*) 0xd000000d;
     55 
     56 static volatile int32_t gCount = 0;
     57 
     58 
     59 /*
     60  * ===========================================================================
     61  *      AssetManager
     62  * ===========================================================================
     63  */
     64 
     65 int32_t AssetManager::getGlobalCount()
     66 {
     67     return gCount;
     68 }
     69 
     70 AssetManager::AssetManager(CacheMode cacheMode)
     71     : mLocale(NULL), mVendor(NULL),
     72       mResources(NULL), mConfig(new ResTable_config),
     73       mCacheMode(cacheMode), mCacheValid(false)
     74 {
     75     int count = android_atomic_inc(&gCount)+1;
     76     //LOGI("Creating AssetManager %p #%d\n", this, count);
     77     memset(mConfig, 0, sizeof(ResTable_config));
     78 }
     79 
     80 AssetManager::~AssetManager(void)
     81 {
     82     int count = android_atomic_dec(&gCount);
     83     //LOGI("Destroying AssetManager in %p #%d\n", this, count);
     84 
     85     delete mConfig;
     86     delete mResources;
     87 
     88     // don't have a String class yet, so make sure we clean up
     89     delete[] mLocale;
     90     delete[] mVendor;
     91 }
     92 
     93 bool AssetManager::addAssetPath(const String8& path, void** cookie)
     94 {
     95     AutoMutex _l(mLock);
     96 
     97     asset_path ap;
     98 
     99     String8 realPath(path);
    100     if (kAppZipName) {
    101         realPath.appendPath(kAppZipName);
    102     }
    103     ap.type = ::getFileType(realPath.string());
    104     if (ap.type == kFileTypeRegular) {
    105         ap.path = realPath;
    106     } else {
    107         ap.path = path;
    108         ap.type = ::getFileType(path.string());
    109         if (ap.type != kFileTypeDirectory && ap.type != kFileTypeRegular) {
    110             LOGW("Asset path %s is neither a directory nor file (type=%d).",
    111                  path.string(), (int)ap.type);
    112             return false;
    113         }
    114     }
    115 
    116     // Skip if we have it already.
    117     for (size_t i=0; i<mAssetPaths.size(); i++) {
    118         if (mAssetPaths[i].path == ap.path) {
    119             if (cookie) {
    120                 *cookie = (void*)(i+1);
    121             }
    122             return true;
    123         }
    124     }
    125 
    126     LOGV("In %p Asset %s path: %s", this,
    127          ap.type == kFileTypeDirectory ? "dir" : "zip", ap.path.string());
    128 
    129     mAssetPaths.add(ap);
    130 
    131     // new paths are always added at the end
    132     if (cookie) {
    133         *cookie = (void*)mAssetPaths.size();
    134     }
    135 
    136     return true;
    137 }
    138 
    139 bool AssetManager::addDefaultAssets()
    140 {
    141     const char* root = getenv("ANDROID_ROOT");
    142     LOG_ALWAYS_FATAL_IF(root == NULL, "ANDROID_ROOT not set");
    143 
    144     String8 path(root);
    145     path.appendPath(kSystemAssets);
    146 
    147     return addAssetPath(path, NULL);
    148 }
    149 
    150 void* AssetManager::nextAssetPath(void* cookie) const
    151 {
    152     AutoMutex _l(mLock);
    153     size_t next = ((size_t)cookie)+1;
    154     return next > mAssetPaths.size() ? NULL : (void*)next;
    155 }
    156 
    157 String8 AssetManager::getAssetPath(void* cookie) const
    158 {
    159     AutoMutex _l(mLock);
    160     const size_t which = ((size_t)cookie)-1;
    161     if (which < mAssetPaths.size()) {
    162         return mAssetPaths[which].path;
    163     }
    164     return String8();
    165 }
    166 
    167 /*
    168  * Set the current locale.  Use NULL to indicate no locale.
    169  *
    170  * Close and reopen Zip archives as appropriate, and reset cached
    171  * information in the locale-specific sections of the tree.
    172  */
    173 void AssetManager::setLocale(const char* locale)
    174 {
    175     AutoMutex _l(mLock);
    176     setLocaleLocked(locale);
    177 }
    178 
    179 void AssetManager::setLocaleLocked(const char* locale)
    180 {
    181     if (mLocale != NULL) {
    182         /* previously set, purge cached data */
    183         purgeFileNameCacheLocked();
    184         //mZipSet.purgeLocale();
    185         delete[] mLocale;
    186     }
    187     mLocale = strdupNew(locale);
    188 
    189     updateResourceParamsLocked();
    190 }
    191 
    192 /*
    193  * Set the current vendor.  Use NULL to indicate no vendor.
    194  *
    195  * Close and reopen Zip archives as appropriate, and reset cached
    196  * information in the vendor-specific sections of the tree.
    197  */
    198 void AssetManager::setVendor(const char* vendor)
    199 {
    200     AutoMutex _l(mLock);
    201 
    202     if (mVendor != NULL) {
    203         /* previously set, purge cached data */
    204         purgeFileNameCacheLocked();
    205         //mZipSet.purgeVendor();
    206         delete[] mVendor;
    207     }
    208     mVendor = strdupNew(vendor);
    209 }
    210 
    211 void AssetManager::setConfiguration(const ResTable_config& config, const char* locale)
    212 {
    213     AutoMutex _l(mLock);
    214     *mConfig = config;
    215     if (locale) {
    216         setLocaleLocked(locale);
    217     } else if (config.language[0] != 0) {
    218         char spec[9];
    219         spec[0] = config.language[0];
    220         spec[1] = config.language[1];
    221         if (config.country[0] != 0) {
    222             spec[2] = '_';
    223             spec[3] = config.country[0];
    224             spec[4] = config.country[1];
    225             spec[5] = 0;
    226         } else {
    227             spec[3] = 0;
    228         }
    229         setLocaleLocked(spec);
    230     } else {
    231         updateResourceParamsLocked();
    232     }
    233 }
    234 
    235 void AssetManager::getConfiguration(ResTable_config* outConfig) const
    236 {
    237     AutoMutex _l(mLock);
    238     *outConfig = *mConfig;
    239 }
    240 
    241 /*
    242  * Open an asset.
    243  *
    244  * The data could be;
    245  *  - In a file on disk (assetBase + fileName).
    246  *  - In a compressed file on disk (assetBase + fileName.gz).
    247  *  - In a Zip archive, uncompressed or compressed.
    248  *
    249  * It can be in a number of different directories and Zip archives.
    250  * The search order is:
    251  *  - [appname]
    252  *    - locale + vendor
    253  *    - "default" + vendor
    254  *    - locale + "default"
    255  *    - "default + "default"
    256  *  - "common"
    257  *    - (same as above)
    258  *
    259  * To find a particular file, we have to try up to eight paths with
    260  * all three forms of data.
    261  *
    262  * We should probably reject requests for "illegal" filenames, e.g. those
    263  * with illegal characters or "../" backward relative paths.
    264  */
    265 Asset* AssetManager::open(const char* fileName, AccessMode mode)
    266 {
    267     AutoMutex _l(mLock);
    268 
    269     LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager");
    270 
    271 
    272     if (mCacheMode != CACHE_OFF && !mCacheValid)
    273         loadFileNameCacheLocked();
    274 
    275     String8 assetName(kAssetsRoot);
    276     assetName.appendPath(fileName);
    277 
    278     /*
    279      * For each top-level asset path, search for the asset.
    280      */
    281 
    282     size_t i = mAssetPaths.size();
    283     while (i > 0) {
    284         i--;
    285         LOGV("Looking for asset '%s' in '%s'\n",
    286                 assetName.string(), mAssetPaths.itemAt(i).path.string());
    287         Asset* pAsset = openNonAssetInPathLocked(assetName.string(), mode, mAssetPaths.itemAt(i));
    288         if (pAsset != NULL) {
    289             return pAsset != kExcludedAsset ? pAsset : NULL;
    290         }
    291     }
    292 
    293     return NULL;
    294 }
    295 
    296 /*
    297  * Open a non-asset file as if it were an asset.
    298  *
    299  * The "fileName" is the partial path starting from the application
    300  * name.
    301  */
    302 Asset* AssetManager::openNonAsset(const char* fileName, AccessMode mode)
    303 {
    304     AutoMutex _l(mLock);
    305 
    306     LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager");
    307 
    308 
    309     if (mCacheMode != CACHE_OFF && !mCacheValid)
    310         loadFileNameCacheLocked();
    311 
    312     /*
    313      * For each top-level asset path, search for the asset.
    314      */
    315 
    316     size_t i = mAssetPaths.size();
    317     while (i > 0) {
    318         i--;
    319         LOGV("Looking for non-asset '%s' in '%s'\n", fileName, mAssetPaths.itemAt(i).path.string());
    320         Asset* pAsset = openNonAssetInPathLocked(
    321             fileName, mode, mAssetPaths.itemAt(i));
    322         if (pAsset != NULL) {
    323             return pAsset != kExcludedAsset ? pAsset : NULL;
    324         }
    325     }
    326 
    327     return NULL;
    328 }
    329 
    330 Asset* AssetManager::openNonAsset(void* cookie, const char* fileName, AccessMode mode)
    331 {
    332     const size_t which = ((size_t)cookie)-1;
    333 
    334     AutoMutex _l(mLock);
    335 
    336     LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager");
    337 
    338 
    339     if (mCacheMode != CACHE_OFF && !mCacheValid)
    340         loadFileNameCacheLocked();
    341 
    342     if (which < mAssetPaths.size()) {
    343         LOGV("Looking for non-asset '%s' in '%s'\n", fileName,
    344                 mAssetPaths.itemAt(which).path.string());
    345         Asset* pAsset = openNonAssetInPathLocked(
    346             fileName, mode, mAssetPaths.itemAt(which));
    347         if (pAsset != NULL) {
    348             return pAsset != kExcludedAsset ? pAsset : NULL;
    349         }
    350     }
    351 
    352     return NULL;
    353 }
    354 
    355 /*
    356  * Get the type of a file in the asset namespace.
    357  *
    358  * This currently only works for regular files.  All others (including
    359  * directories) will return kFileTypeNonexistent.
    360  */
    361 FileType AssetManager::getFileType(const char* fileName)
    362 {
    363     Asset* pAsset = NULL;
    364 
    365     /*
    366      * Open the asset.  This is less efficient than simply finding the
    367      * file, but it's not too bad (we don't uncompress or mmap data until
    368      * the first read() call).
    369      */
    370     pAsset = open(fileName, Asset::ACCESS_STREAMING);
    371     delete pAsset;
    372 
    373     if (pAsset == NULL)
    374         return kFileTypeNonexistent;
    375     else
    376         return kFileTypeRegular;
    377 }
    378 
    379 const ResTable* AssetManager::getResTable(bool required) const
    380 {
    381     ResTable* rt = mResources;
    382     if (rt) {
    383         return rt;
    384     }
    385 
    386     // Iterate through all asset packages, collecting resources from each.
    387 
    388     AutoMutex _l(mLock);
    389 
    390     if (mResources != NULL) {
    391         return mResources;
    392     }
    393 
    394     if (required) {
    395         LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager");
    396     }
    397 
    398     if (mCacheMode != CACHE_OFF && !mCacheValid)
    399         const_cast<AssetManager*>(this)->loadFileNameCacheLocked();
    400 
    401     const size_t N = mAssetPaths.size();
    402     for (size_t i=0; i<N; i++) {
    403         Asset* ass = NULL;
    404         ResTable* sharedRes = NULL;
    405         bool shared = true;
    406         const asset_path& ap = mAssetPaths.itemAt(i);
    407         LOGV("Looking for resource asset in '%s'\n", ap.path.string());
    408         if (ap.type != kFileTypeDirectory) {
    409             if (i == 0) {
    410                 // The first item is typically the framework resources,
    411                 // which we want to avoid parsing every time.
    412                 sharedRes = const_cast<AssetManager*>(this)->
    413                     mZipSet.getZipResourceTable(ap.path);
    414             }
    415             if (sharedRes == NULL) {
    416                 ass = const_cast<AssetManager*>(this)->
    417                     mZipSet.getZipResourceTableAsset(ap.path);
    418                 if (ass == NULL) {
    419                     LOGV("loading resource table %s\n", ap.path.string());
    420                     ass = const_cast<AssetManager*>(this)->
    421                         openNonAssetInPathLocked("resources.arsc",
    422                                                  Asset::ACCESS_BUFFER,
    423                                                  ap);
    424                     if (ass != NULL && ass != kExcludedAsset) {
    425                         ass = const_cast<AssetManager*>(this)->
    426                             mZipSet.setZipResourceTableAsset(ap.path, ass);
    427                     }
    428                 }
    429 
    430                 if (i == 0 && ass != NULL) {
    431                     // If this is the first resource table in the asset
    432                     // manager, then we are going to cache it so that we
    433                     // can quickly copy it out for others.
    434                     LOGV("Creating shared resources for %s", ap.path.string());
    435                     sharedRes = new ResTable();
    436                     sharedRes->add(ass, (void*)(i+1), false);
    437                     sharedRes = const_cast<AssetManager*>(this)->
    438                         mZipSet.setZipResourceTable(ap.path, sharedRes);
    439                 }
    440             }
    441         } else {
    442             LOGV("loading resource table %s\n", ap.path.string());
    443             Asset* ass = const_cast<AssetManager*>(this)->
    444                 openNonAssetInPathLocked("resources.arsc",
    445                                          Asset::ACCESS_BUFFER,
    446                                          ap);
    447             shared = false;
    448         }
    449         if ((ass != NULL || sharedRes != NULL) && ass != kExcludedAsset) {
    450             if (rt == NULL) {
    451                 mResources = rt = new ResTable();
    452                 updateResourceParamsLocked();
    453             }
    454             LOGV("Installing resource asset %p in to table %p\n", ass, mResources);
    455             if (sharedRes != NULL) {
    456                 LOGV("Copying existing resources for %s", ap.path.string());
    457                 rt->add(sharedRes);
    458             } else {
    459                 LOGV("Parsing resources for %s", ap.path.string());
    460                 rt->add(ass, (void*)(i+1), !shared);
    461             }
    462 
    463             if (!shared) {
    464                 delete ass;
    465             }
    466         }
    467     }
    468 
    469     if (required && !rt) LOGW("Unable to find resources file resources.arsc");
    470     if (!rt) {
    471         mResources = rt = new ResTable();
    472     }
    473     return rt;
    474 }
    475 
    476 void AssetManager::updateResourceParamsLocked() const
    477 {
    478     ResTable* res = mResources;
    479     if (!res) {
    480         return;
    481     }
    482 
    483     size_t llen = mLocale ? strlen(mLocale) : 0;
    484     mConfig->language[0] = 0;
    485     mConfig->language[1] = 0;
    486     mConfig->country[0] = 0;
    487     mConfig->country[1] = 0;
    488     if (llen >= 2) {
    489         mConfig->language[0] = mLocale[0];
    490         mConfig->language[1] = mLocale[1];
    491     }
    492     if (llen >= 5) {
    493         mConfig->country[0] = mLocale[3];
    494         mConfig->country[1] = mLocale[4];
    495     }
    496     mConfig->size = sizeof(*mConfig);
    497 
    498     res->setParameters(mConfig);
    499 }
    500 
    501 const ResTable& AssetManager::getResources(bool required) const
    502 {
    503     const ResTable* rt = getResTable(required);
    504     return *rt;
    505 }
    506 
    507 bool AssetManager::isUpToDate()
    508 {
    509     AutoMutex _l(mLock);
    510     return mZipSet.isUpToDate();
    511 }
    512 
    513 void AssetManager::getLocales(Vector<String8>* locales) const
    514 {
    515     ResTable* res = mResources;
    516     if (res != NULL) {
    517         res->getLocales(locales);
    518     }
    519 }
    520 
    521 /*
    522  * Open a non-asset file as if it were an asset, searching for it in the
    523  * specified app.
    524  *
    525  * Pass in a NULL values for "appName" if the common app directory should
    526  * be used.
    527  */
    528 Asset* AssetManager::openNonAssetInPathLocked(const char* fileName, AccessMode mode,
    529     const asset_path& ap)
    530 {
    531     Asset* pAsset = NULL;
    532 
    533     /* look at the filesystem on disk */
    534     if (ap.type == kFileTypeDirectory) {
    535         String8 path(ap.path);
    536         path.appendPath(fileName);
    537 
    538         pAsset = openAssetFromFileLocked(path, mode);
    539 
    540         if (pAsset == NULL) {
    541             /* try again, this time with ".gz" */
    542             path.append(".gz");
    543             pAsset = openAssetFromFileLocked(path, mode);
    544         }
    545 
    546         if (pAsset != NULL) {
    547             //printf("FOUND NA '%s' on disk\n", fileName);
    548             pAsset->setAssetSource(path);
    549         }
    550 
    551     /* look inside the zip file */
    552     } else {
    553         String8 path(fileName);
    554 
    555         /* check the appropriate Zip file */
    556         ZipFileRO* pZip;
    557         ZipEntryRO entry;
    558 
    559         pZip = getZipFileLocked(ap);
    560         if (pZip != NULL) {
    561             //printf("GOT zip, checking NA '%s'\n", (const char*) path);
    562             entry = pZip->findEntryByName(path.string());
    563             if (entry != NULL) {
    564                 //printf("FOUND NA in Zip file for %s\n", appName ? appName : kAppCommon);
    565                 pAsset = openAssetFromZipLocked(pZip, entry, mode, path);
    566             }
    567         }
    568 
    569         if (pAsset != NULL) {
    570             /* create a "source" name, for debug/display */
    571             pAsset->setAssetSource(
    572                     createZipSourceNameLocked(ZipSet::getPathName(ap.path.string()), String8(""),
    573                                                 String8(fileName)));
    574         }
    575     }
    576 
    577     return pAsset;
    578 }
    579 
    580 /*
    581  * Open an asset, searching for it in the directory hierarchy for the
    582  * specified app.
    583  *
    584  * Pass in a NULL values for "appName" if the common app directory should
    585  * be used.
    586  */
    587 Asset* AssetManager::openInPathLocked(const char* fileName, AccessMode mode,
    588     const asset_path& ap)
    589 {
    590     Asset* pAsset = NULL;
    591 
    592     /*
    593      * Try various combinations of locale and vendor.
    594      */
    595     if (mLocale != NULL && mVendor != NULL)
    596         pAsset = openInLocaleVendorLocked(fileName, mode, ap, mLocale, mVendor);
    597     if (pAsset == NULL && mVendor != NULL)
    598         pAsset = openInLocaleVendorLocked(fileName, mode, ap, NULL, mVendor);
    599     if (pAsset == NULL && mLocale != NULL)
    600         pAsset = openInLocaleVendorLocked(fileName, mode, ap, mLocale, NULL);
    601     if (pAsset == NULL)
    602         pAsset = openInLocaleVendorLocked(fileName, mode, ap, NULL, NULL);
    603 
    604     return pAsset;
    605 }
    606 
    607 /*
    608  * Open an asset, searching for it in the directory hierarchy for the
    609  * specified locale and vendor.
    610  *
    611  * We also search in "app.jar".
    612  *
    613  * Pass in NULL values for "appName", "locale", and "vendor" if the
    614  * defaults should be used.
    615  */
    616 Asset* AssetManager::openInLocaleVendorLocked(const char* fileName, AccessMode mode,
    617     const asset_path& ap, const char* locale, const char* vendor)
    618 {
    619     Asset* pAsset = NULL;
    620 
    621     if (ap.type == kFileTypeDirectory) {
    622         if (mCacheMode == CACHE_OFF) {
    623             /* look at the filesystem on disk */
    624             String8 path(createPathNameLocked(ap, locale, vendor));
    625             path.appendPath(fileName);
    626 
    627             String8 excludeName(path);
    628             excludeName.append(kExcludeExtension);
    629             if (::getFileType(excludeName.string()) != kFileTypeNonexistent) {
    630                 /* say no more */
    631                 //printf("+++ excluding '%s'\n", (const char*) excludeName);
    632                 return kExcludedAsset;
    633             }
    634 
    635             pAsset = openAssetFromFileLocked(path, mode);
    636 
    637             if (pAsset == NULL) {
    638                 /* try again, this time with ".gz" */
    639                 path.append(".gz");
    640                 pAsset = openAssetFromFileLocked(path, mode);
    641             }
    642 
    643             if (pAsset != NULL)
    644                 pAsset->setAssetSource(path);
    645         } else {
    646             /* find in cache */
    647             String8 path(createPathNameLocked(ap, locale, vendor));
    648             path.appendPath(fileName);
    649 
    650             AssetDir::FileInfo tmpInfo;
    651             bool found = false;
    652 
    653             String8 excludeName(path);
    654             excludeName.append(kExcludeExtension);
    655 
    656             if (mCache.indexOf(excludeName) != NAME_NOT_FOUND) {
    657                 /* go no farther */
    658                 //printf("+++ Excluding '%s'\n", (const char*) excludeName);
    659                 return kExcludedAsset;
    660             }
    661 
    662             /*
    663              * File compression extensions (".gz") don't get stored in the
    664              * name cache, so we have to try both here.
    665              */
    666             if (mCache.indexOf(path) != NAME_NOT_FOUND) {
    667                 found = true;
    668                 pAsset = openAssetFromFileLocked(path, mode);
    669                 if (pAsset == NULL) {
    670                     /* try again, this time with ".gz" */
    671                     path.append(".gz");
    672                     pAsset = openAssetFromFileLocked(path, mode);
    673                 }
    674             }
    675 
    676             if (pAsset != NULL)
    677                 pAsset->setAssetSource(path);
    678 
    679             /*
    680              * Don't continue the search into the Zip files.  Our cached info
    681              * said it was a file on disk; to be consistent with openDir()
    682              * we want to return the loose asset.  If the cached file gets
    683              * removed, we fail.
    684              *
    685              * The alternative is to update our cache when files get deleted,
    686              * or make some sort of "best effort" promise, but for now I'm
    687              * taking the hard line.
    688              */
    689             if (found) {
    690                 if (pAsset == NULL)
    691                     LOGD("Expected file not found: '%s'\n", path.string());
    692                 return pAsset;
    693             }
    694         }
    695     }
    696 
    697     /*
    698      * Either it wasn't found on disk or on the cached view of the disk.
    699      * Dig through the currently-opened set of Zip files.  If caching
    700      * is disabled, the Zip file may get reopened.
    701      */
    702     if (pAsset == NULL && ap.type == kFileTypeRegular) {
    703         String8 path;
    704 
    705         path.appendPath((locale != NULL) ? locale : kDefaultLocale);
    706         path.appendPath((vendor != NULL) ? vendor : kDefaultVendor);
    707         path.appendPath(fileName);
    708 
    709         /* check the appropriate Zip file */
    710         ZipFileRO* pZip;
    711         ZipEntryRO entry;
    712 
    713         pZip = getZipFileLocked(ap);
    714         if (pZip != NULL) {
    715             //printf("GOT zip, checking '%s'\n", (const char*) path);
    716             entry = pZip->findEntryByName(path.string());
    717             if (entry != NULL) {
    718                 //printf("FOUND in Zip file for %s/%s-%s\n",
    719                 //    appName, locale, vendor);
    720                 pAsset = openAssetFromZipLocked(pZip, entry, mode, path);
    721             }
    722         }
    723 
    724         if (pAsset != NULL) {
    725             /* create a "source" name, for debug/display */
    726             pAsset->setAssetSource(createZipSourceNameLocked(ZipSet::getPathName(ap.path.string()),
    727                                                              String8(""), String8(fileName)));
    728         }
    729     }
    730 
    731     return pAsset;
    732 }
    733 
    734 /*
    735  * Create a "source name" for a file from a Zip archive.
    736  */
    737 String8 AssetManager::createZipSourceNameLocked(const String8& zipFileName,
    738     const String8& dirName, const String8& fileName)
    739 {
    740     String8 sourceName("zip:");
    741     sourceName.append(zipFileName);
    742     sourceName.append(":");
    743     if (dirName.length() > 0) {
    744         sourceName.appendPath(dirName);
    745     }
    746     sourceName.appendPath(fileName);
    747     return sourceName;
    748 }
    749 
    750 /*
    751  * Create a path to a loose asset (asset-base/app/locale/vendor).
    752  */
    753 String8 AssetManager::createPathNameLocked(const asset_path& ap, const char* locale,
    754     const char* vendor)
    755 {
    756     String8 path(ap.path);
    757     path.appendPath((locale != NULL) ? locale : kDefaultLocale);
    758     path.appendPath((vendor != NULL) ? vendor : kDefaultVendor);
    759     return path;
    760 }
    761 
    762 /*
    763  * Create a path to a loose asset (asset-base/app/rootDir).
    764  */
    765 String8 AssetManager::createPathNameLocked(const asset_path& ap, const char* rootDir)
    766 {
    767     String8 path(ap.path);
    768     if (rootDir != NULL) path.appendPath(rootDir);
    769     return path;
    770 }
    771 
    772 /*
    773  * Return a pointer to one of our open Zip archives.  Returns NULL if no
    774  * matching Zip file exists.
    775  *
    776  * Right now we have 2 possible Zip files (1 each in app/"common").
    777  *
    778  * If caching is set to CACHE_OFF, to get the expected behavior we
    779  * need to reopen the Zip file on every request.  That would be silly
    780  * and expensive, so instead we just check the file modification date.
    781  *
    782  * Pass in NULL values for "appName", "locale", and "vendor" if the
    783  * generics should be used.
    784  */
    785 ZipFileRO* AssetManager::getZipFileLocked(const asset_path& ap)
    786 {
    787     LOGV("getZipFileLocked() in %p\n", this);
    788 
    789     return mZipSet.getZip(ap.path);
    790 }
    791 
    792 /*
    793  * Try to open an asset from a file on disk.
    794  *
    795  * If the file is compressed with gzip, we seek to the start of the
    796  * deflated data and pass that in (just like we would for a Zip archive).
    797  *
    798  * For uncompressed data, we may already have an mmap()ed version sitting
    799  * around.  If so, we want to hand that to the Asset instead.
    800  *
    801  * This returns NULL if the file doesn't exist, couldn't be opened, or
    802  * claims to be a ".gz" but isn't.
    803  */
    804 Asset* AssetManager::openAssetFromFileLocked(const String8& pathName,
    805     AccessMode mode)
    806 {
    807     Asset* pAsset = NULL;
    808 
    809     if (strcasecmp(pathName.getPathExtension().string(), ".gz") == 0) {
    810         //printf("TRYING '%s'\n", (const char*) pathName);
    811         pAsset = Asset::createFromCompressedFile(pathName.string(), mode);
    812     } else {
    813         //printf("TRYING '%s'\n", (const char*) pathName);
    814         pAsset = Asset::createFromFile(pathName.string(), mode);
    815     }
    816 
    817     return pAsset;
    818 }
    819 
    820 /*
    821  * Given an entry in a Zip archive, create a new Asset object.
    822  *
    823  * If the entry is uncompressed, we may want to create or share a
    824  * slice of shared memory.
    825  */
    826 Asset* AssetManager::openAssetFromZipLocked(const ZipFileRO* pZipFile,
    827     const ZipEntryRO entry, AccessMode mode, const String8& entryName)
    828 {
    829     Asset* pAsset = NULL;
    830 
    831     // TODO: look for previously-created shared memory slice?
    832     int method;
    833     size_t uncompressedLen;
    834 
    835     //printf("USING Zip '%s'\n", pEntry->getFileName());
    836 
    837     //pZipFile->getEntryInfo(entry, &method, &uncompressedLen, &compressedLen,
    838     //    &offset);
    839     if (!pZipFile->getEntryInfo(entry, &method, &uncompressedLen, NULL, NULL,
    840             NULL, NULL))
    841     {
    842         LOGW("getEntryInfo failed\n");
    843         return NULL;
    844     }
    845 
    846     FileMap* dataMap = pZipFile->createEntryFileMap(entry);
    847     if (dataMap == NULL) {
    848         LOGW("create map from entry failed\n");
    849         return NULL;
    850     }
    851 
    852     if (method == ZipFileRO::kCompressStored) {
    853         pAsset = Asset::createFromUncompressedMap(dataMap, mode);
    854         LOGV("Opened uncompressed entry %s in zip %s mode %d: %p", entryName.string(),
    855                 dataMap->getFileName(), mode, pAsset);
    856     } else {
    857         pAsset = Asset::createFromCompressedMap(dataMap, method,
    858             uncompressedLen, mode);
    859         LOGV("Opened compressed entry %s in zip %s mode %d: %p", entryName.string(),
    860                 dataMap->getFileName(), mode, pAsset);
    861     }
    862     if (pAsset == NULL) {
    863         /* unexpected */
    864         LOGW("create from segment failed\n");
    865     }
    866 
    867     return pAsset;
    868 }
    869 
    870 
    871 
    872 /*
    873  * Open a directory in the asset namespace.
    874  *
    875  * An "asset directory" is simply the combination of all files in all
    876  * locations, with ".gz" stripped for loose files.  With app, locale, and
    877  * vendor defined, we have 8 directories and 2 Zip archives to scan.
    878  *
    879  * Pass in "" for the root dir.
    880  */
    881 AssetDir* AssetManager::openDir(const char* dirName)
    882 {
    883     AutoMutex _l(mLock);
    884 
    885     AssetDir* pDir = NULL;
    886     SortedVector<AssetDir::FileInfo>* pMergedInfo = NULL;
    887 
    888     LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager");
    889     assert(dirName != NULL);
    890 
    891     //printf("+++ openDir(%s) in '%s'\n", dirName, (const char*) mAssetBase);
    892 
    893     if (mCacheMode != CACHE_OFF && !mCacheValid)
    894         loadFileNameCacheLocked();
    895 
    896     pDir = new AssetDir;
    897 
    898     /*
    899      * Scan the various directories, merging what we find into a single
    900      * vector.  We want to scan them in reverse priority order so that
    901      * the ".EXCLUDE" processing works correctly.  Also, if we decide we
    902      * want to remember where the file is coming from, we'll get the right
    903      * version.
    904      *
    905      * We start with Zip archives, then do loose files.
    906      */
    907     pMergedInfo = new SortedVector<AssetDir::FileInfo>;
    908 
    909     size_t i = mAssetPaths.size();
    910     while (i > 0) {
    911         i--;
    912         const asset_path& ap = mAssetPaths.itemAt(i);
    913         if (ap.type == kFileTypeRegular) {
    914             LOGV("Adding directory %s from zip %s", dirName, ap.path.string());
    915             scanAndMergeZipLocked(pMergedInfo, ap, kAssetsRoot, dirName);
    916         } else {
    917             LOGV("Adding directory %s from dir %s", dirName, ap.path.string());
    918             scanAndMergeDirLocked(pMergedInfo, ap, kAssetsRoot, dirName);
    919         }
    920     }
    921 
    922 #if 0
    923     printf("FILE LIST:\n");
    924     for (i = 0; i < (size_t) pMergedInfo->size(); i++) {
    925         printf(" %d: (%d) '%s'\n", i,
    926             pMergedInfo->itemAt(i).getFileType(),
    927             (const char*) pMergedInfo->itemAt(i).getFileName());
    928     }
    929 #endif
    930 
    931     pDir->setFileList(pMergedInfo);
    932     return pDir;
    933 }
    934 
    935 /*
    936  * Open a directory in the non-asset namespace.
    937  *
    938  * An "asset directory" is simply the combination of all files in all
    939  * locations, with ".gz" stripped for loose files.  With app, locale, and
    940  * vendor defined, we have 8 directories and 2 Zip archives to scan.
    941  *
    942  * Pass in "" for the root dir.
    943  */
    944 AssetDir* AssetManager::openNonAssetDir(void* cookie, const char* dirName)
    945 {
    946     AutoMutex _l(mLock);
    947 
    948     AssetDir* pDir = NULL;
    949     SortedVector<AssetDir::FileInfo>* pMergedInfo = NULL;
    950 
    951     LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager");
    952     assert(dirName != NULL);
    953 
    954     //printf("+++ openDir(%s) in '%s'\n", dirName, (const char*) mAssetBase);
    955 
    956     if (mCacheMode != CACHE_OFF && !mCacheValid)
    957         loadFileNameCacheLocked();
    958 
    959     pDir = new AssetDir;
    960 
    961     pMergedInfo = new SortedVector<AssetDir::FileInfo>;
    962 
    963     const size_t which = ((size_t)cookie)-1;
    964 
    965     if (which < mAssetPaths.size()) {
    966         const asset_path& ap = mAssetPaths.itemAt(which);
    967         if (ap.type == kFileTypeRegular) {
    968             LOGV("Adding directory %s from zip %s", dirName, ap.path.string());
    969             scanAndMergeZipLocked(pMergedInfo, ap, NULL, dirName);
    970         } else {
    971             LOGV("Adding directory %s from dir %s", dirName, ap.path.string());
    972             scanAndMergeDirLocked(pMergedInfo, ap, NULL, dirName);
    973         }
    974     }
    975 
    976 #if 0
    977     printf("FILE LIST:\n");
    978     for (i = 0; i < (size_t) pMergedInfo->size(); i++) {
    979         printf(" %d: (%d) '%s'\n", i,
    980             pMergedInfo->itemAt(i).getFileType(),
    981             (const char*) pMergedInfo->itemAt(i).getFileName());
    982     }
    983 #endif
    984 
    985     pDir->setFileList(pMergedInfo);
    986     return pDir;
    987 }
    988 
    989 /*
    990  * Scan the contents of the specified directory and merge them into the
    991  * "pMergedInfo" vector, removing previous entries if we find "exclude"
    992  * directives.
    993  *
    994  * Returns "false" if we found nothing to contribute.
    995  */
    996 bool AssetManager::scanAndMergeDirLocked(SortedVector<AssetDir::FileInfo>* pMergedInfo,
    997     const asset_path& ap, const char* rootDir, const char* dirName)
    998 {
    999     SortedVector<AssetDir::FileInfo>* pContents;
   1000     String8 path;
   1001 
   1002     assert(pMergedInfo != NULL);
   1003 
   1004     //printf("scanAndMergeDir: %s %s %s %s\n", appName, locale, vendor,dirName);
   1005 
   1006     if (mCacheValid) {
   1007         int i, start, count;
   1008 
   1009         pContents = new SortedVector<AssetDir::FileInfo>;
   1010 
   1011         /*
   1012          * Get the basic partial path and find it in the cache.  That's
   1013          * the start point for the search.
   1014          */
   1015         path = createPathNameLocked(ap, rootDir);
   1016         if (dirName[0] != '\0')
   1017             path.appendPath(dirName);
   1018 
   1019         start = mCache.indexOf(path);
   1020         if (start == NAME_NOT_FOUND) {
   1021             //printf("+++ not found in cache: dir '%s'\n", (const char*) path);
   1022             delete pContents;
   1023             return false;
   1024         }
   1025 
   1026         /*
   1027          * The match string looks like "common/default/default/foo/bar/".
   1028          * The '/' on the end ensures that we don't match on the directory
   1029          * itself or on ".../foo/barfy/".
   1030          */
   1031         path.append("/");
   1032 
   1033         count = mCache.size();
   1034 
   1035         /*
   1036          * Pick out the stuff in the current dir by examining the pathname.
   1037          * It needs to match the partial pathname prefix, and not have a '/'
   1038          * (fssep) anywhere after the prefix.
   1039          */
   1040         for (i = start+1; i < count; i++) {
   1041             if (mCache[i].getFileName().length() > path.length() &&
   1042                 strncmp(mCache[i].getFileName().string(), path.string(), path.length()) == 0)
   1043             {
   1044                 const char* name = mCache[i].getFileName().string();
   1045                 // XXX THIS IS BROKEN!  Looks like we need to store the full
   1046                 // path prefix separately from the file path.
   1047                 if (strchr(name + path.length(), '/') == NULL) {
   1048                     /* grab it, reducing path to just the filename component */
   1049                     AssetDir::FileInfo tmp = mCache[i];
   1050                     tmp.setFileName(tmp.getFileName().getPathLeaf());
   1051                     pContents->add(tmp);
   1052                 }
   1053             } else {
   1054                 /* no longer in the dir or its subdirs */
   1055                 break;
   1056             }
   1057 
   1058         }
   1059     } else {
   1060         path = createPathNameLocked(ap, rootDir);
   1061         if (dirName[0] != '\0')
   1062             path.appendPath(dirName);
   1063         pContents = scanDirLocked(path);
   1064         if (pContents == NULL)
   1065             return false;
   1066     }
   1067 
   1068     // if we wanted to do an incremental cache fill, we would do it here
   1069 
   1070     /*
   1071      * Process "exclude" directives.  If we find a filename that ends with
   1072      * ".EXCLUDE", we look for a matching entry in the "merged" set, and
   1073      * remove it if we find it.  We also delete the "exclude" entry.
   1074      */
   1075     int i, count, exclExtLen;
   1076 
   1077     count = pContents->size();
   1078     exclExtLen = strlen(kExcludeExtension);
   1079     for (i = 0; i < count; i++) {
   1080         const char* name;
   1081         int nameLen;
   1082 
   1083         name = pContents->itemAt(i).getFileName().string();
   1084         nameLen = strlen(name);
   1085         if (nameLen > exclExtLen &&
   1086             strcmp(name + (nameLen - exclExtLen), kExcludeExtension) == 0)
   1087         {
   1088             String8 match(name, nameLen - exclExtLen);
   1089             int matchIdx;
   1090 
   1091             matchIdx = AssetDir::FileInfo::findEntry(pMergedInfo, match);
   1092             if (matchIdx > 0) {
   1093                 LOGV("Excluding '%s' [%s]\n",
   1094                     pMergedInfo->itemAt(matchIdx).getFileName().string(),
   1095                     pMergedInfo->itemAt(matchIdx).getSourceName().string());
   1096                 pMergedInfo->removeAt(matchIdx);
   1097             } else {
   1098                 //printf("+++ no match on '%s'\n", (const char*) match);
   1099             }
   1100 
   1101             LOGD("HEY: size=%d removing %d\n", (int)pContents->size(), i);
   1102             pContents->removeAt(i);
   1103             i--;        // adjust "for" loop
   1104             count--;    //  and loop limit
   1105         }
   1106     }
   1107 
   1108     mergeInfoLocked(pMergedInfo, pContents);
   1109 
   1110     delete pContents;
   1111 
   1112     return true;
   1113 }
   1114 
   1115 /*
   1116  * Scan the contents of the specified directory, and stuff what we find
   1117  * into a newly-allocated vector.
   1118  *
   1119  * Files ending in ".gz" will have their extensions removed.
   1120  *
   1121  * We should probably think about skipping files with "illegal" names,
   1122  * e.g. illegal characters (/\:) or excessive length.
   1123  *
   1124  * Returns NULL if the specified directory doesn't exist.
   1125  */
   1126 SortedVector<AssetDir::FileInfo>* AssetManager::scanDirLocked(const String8& path)
   1127 {
   1128     SortedVector<AssetDir::FileInfo>* pContents = NULL;
   1129     DIR* dir;
   1130     struct dirent* entry;
   1131     FileType fileType;
   1132 
   1133     LOGV("Scanning dir '%s'\n", path.string());
   1134 
   1135     dir = opendir(path.string());
   1136     if (dir == NULL)
   1137         return NULL;
   1138 
   1139     pContents = new SortedVector<AssetDir::FileInfo>;
   1140 
   1141     while (1) {
   1142         entry = readdir(dir);
   1143         if (entry == NULL)
   1144             break;
   1145 
   1146         if (strcmp(entry->d_name, ".") == 0 ||
   1147             strcmp(entry->d_name, "..") == 0)
   1148             continue;
   1149 
   1150 #ifdef _DIRENT_HAVE_D_TYPE
   1151         if (entry->d_type == DT_REG)
   1152             fileType = kFileTypeRegular;
   1153         else if (entry->d_type == DT_DIR)
   1154             fileType = kFileTypeDirectory;
   1155         else
   1156             fileType = kFileTypeUnknown;
   1157 #else
   1158         // stat the file
   1159         fileType = ::getFileType(path.appendPathCopy(entry->d_name).string());
   1160 #endif
   1161 
   1162         if (fileType != kFileTypeRegular && fileType != kFileTypeDirectory)
   1163             continue;
   1164 
   1165         AssetDir::FileInfo info;
   1166         info.set(String8(entry->d_name), fileType);
   1167         if (strcasecmp(info.getFileName().getPathExtension().string(), ".gz") == 0)
   1168             info.setFileName(info.getFileName().getBasePath());
   1169         info.setSourceName(path.appendPathCopy(info.getFileName()));
   1170         pContents->add(info);
   1171     }
   1172 
   1173     closedir(dir);
   1174     return pContents;
   1175 }
   1176 
   1177 /*
   1178  * Scan the contents out of the specified Zip archive, and merge what we
   1179  * find into "pMergedInfo".  If the Zip archive in question doesn't exist,
   1180  * we return immediately.
   1181  *
   1182  * Returns "false" if we found nothing to contribute.
   1183  */
   1184 bool AssetManager::scanAndMergeZipLocked(SortedVector<AssetDir::FileInfo>* pMergedInfo,
   1185     const asset_path& ap, const char* rootDir, const char* baseDirName)
   1186 {
   1187     ZipFileRO* pZip;
   1188     Vector<String8> dirs;
   1189     AssetDir::FileInfo info;
   1190     SortedVector<AssetDir::FileInfo> contents;
   1191     String8 sourceName, zipName, dirName;
   1192 
   1193     pZip = mZipSet.getZip(ap.path);
   1194     if (pZip == NULL) {
   1195         LOGW("Failure opening zip %s\n", ap.path.string());
   1196         return false;
   1197     }
   1198 
   1199     zipName = ZipSet::getPathName(ap.path.string());
   1200 
   1201     /* convert "sounds" to "rootDir/sounds" */
   1202     if (rootDir != NULL) dirName = rootDir;
   1203     dirName.appendPath(baseDirName);
   1204 
   1205     /*
   1206      * Scan through the list of files, looking for a match.  The files in
   1207      * the Zip table of contents are not in sorted order, so we have to
   1208      * process the entire list.  We're looking for a string that begins
   1209      * with the characters in "dirName", is followed by a '/', and has no
   1210      * subsequent '/' in the stuff that follows.
   1211      *
   1212      * What makes this especially fun is that directories are not stored
   1213      * explicitly in Zip archives, so we have to infer them from context.
   1214      * When we see "sounds/foo.wav" we have to leave a note to ourselves
   1215      * to insert a directory called "sounds" into the list.  We store
   1216      * these in temporary vector so that we only return each one once.
   1217      *
   1218      * Name comparisons are case-sensitive to match UNIX filesystem
   1219      * semantics.
   1220      */
   1221     int dirNameLen = dirName.length();
   1222     for (int i = 0; i < pZip->getNumEntries(); i++) {
   1223         ZipEntryRO entry;
   1224         char nameBuf[256];
   1225 
   1226         entry = pZip->findEntryByIndex(i);
   1227         if (pZip->getEntryFileName(entry, nameBuf, sizeof(nameBuf)) != 0) {
   1228             // TODO: fix this if we expect to have long names
   1229             LOGE("ARGH: name too long?\n");
   1230             continue;
   1231         }
   1232         //printf("Comparing %s in %s?\n", nameBuf, dirName.string());
   1233         if (dirNameLen == 0 ||
   1234             (strncmp(nameBuf, dirName.string(), dirNameLen) == 0 &&
   1235              nameBuf[dirNameLen] == '/'))
   1236         {
   1237             const char* cp;
   1238             const char* nextSlash;
   1239 
   1240             cp = nameBuf + dirNameLen;
   1241             if (dirNameLen != 0)
   1242                 cp++;       // advance past the '/'
   1243 
   1244             nextSlash = strchr(cp, '/');
   1245 //xxx this may break if there are bare directory entries
   1246             if (nextSlash == NULL) {
   1247                 /* this is a file in the requested directory */
   1248 
   1249                 info.set(String8(nameBuf).getPathLeaf(), kFileTypeRegular);
   1250 
   1251                 info.setSourceName(
   1252                     createZipSourceNameLocked(zipName, dirName, info.getFileName()));
   1253 
   1254                 contents.add(info);
   1255                 //printf("FOUND: file '%s'\n", info.getFileName().string());
   1256             } else {
   1257                 /* this is a subdir; add it if we don't already have it*/
   1258                 String8 subdirName(cp, nextSlash - cp);
   1259                 size_t j;
   1260                 size_t N = dirs.size();
   1261 
   1262                 for (j = 0; j < N; j++) {
   1263                     if (subdirName == dirs[j]) {
   1264                         break;
   1265                     }
   1266                 }
   1267                 if (j == N) {
   1268                     dirs.add(subdirName);
   1269                 }
   1270 
   1271                 //printf("FOUND: dir '%s'\n", subdirName.string());
   1272             }
   1273         }
   1274     }
   1275 
   1276     /*
   1277      * Add the set of unique directories.
   1278      */
   1279     for (int i = 0; i < (int) dirs.size(); i++) {
   1280         info.set(dirs[i], kFileTypeDirectory);
   1281         info.setSourceName(
   1282             createZipSourceNameLocked(zipName, dirName, info.getFileName()));
   1283         contents.add(info);
   1284     }
   1285 
   1286     mergeInfoLocked(pMergedInfo, &contents);
   1287 
   1288     return true;
   1289 }
   1290 
   1291 
   1292 /*
   1293  * Merge two vectors of FileInfo.
   1294  *
   1295  * The merged contents will be stuffed into *pMergedInfo.
   1296  *
   1297  * If an entry for a file exists in both "pMergedInfo" and "pContents",
   1298  * we use the newer "pContents" entry.
   1299  */
   1300 void AssetManager::mergeInfoLocked(SortedVector<AssetDir::FileInfo>* pMergedInfo,
   1301     const SortedVector<AssetDir::FileInfo>* pContents)
   1302 {
   1303     /*
   1304      * Merge what we found in this directory with what we found in
   1305      * other places.
   1306      *
   1307      * Two basic approaches:
   1308      * (1) Create a new array that holds the unique values of the two
   1309      *     arrays.
   1310      * (2) Take the elements from pContents and shove them into pMergedInfo.
   1311      *
   1312      * Because these are vectors of complex objects, moving elements around
   1313      * inside the vector requires constructing new objects and allocating
   1314      * storage for members.  With approach #1, we're always adding to the
   1315      * end, whereas with #2 we could be inserting multiple elements at the
   1316      * front of the vector.  Approach #1 requires a full copy of the
   1317      * contents of pMergedInfo, but approach #2 requires the same copy for
   1318      * every insertion at the front of pMergedInfo.
   1319      *
   1320      * (We should probably use a SortedVector interface that allows us to
   1321      * just stuff items in, trusting us to maintain the sort order.)
   1322      */
   1323     SortedVector<AssetDir::FileInfo>* pNewSorted;
   1324     int mergeMax, contMax;
   1325     int mergeIdx, contIdx;
   1326 
   1327     pNewSorted = new SortedVector<AssetDir::FileInfo>;
   1328     mergeMax = pMergedInfo->size();
   1329     contMax = pContents->size();
   1330     mergeIdx = contIdx = 0;
   1331 
   1332     while (mergeIdx < mergeMax || contIdx < contMax) {
   1333         if (mergeIdx == mergeMax) {
   1334             /* hit end of "merge" list, copy rest of "contents" */
   1335             pNewSorted->add(pContents->itemAt(contIdx));
   1336             contIdx++;
   1337         } else if (contIdx == contMax) {
   1338             /* hit end of "cont" list, copy rest of "merge" */
   1339             pNewSorted->add(pMergedInfo->itemAt(mergeIdx));
   1340             mergeIdx++;
   1341         } else if (pMergedInfo->itemAt(mergeIdx) == pContents->itemAt(contIdx))
   1342         {
   1343             /* items are identical, add newer and advance both indices */
   1344             pNewSorted->add(pContents->itemAt(contIdx));
   1345             mergeIdx++;
   1346             contIdx++;
   1347         } else if (pMergedInfo->itemAt(mergeIdx) < pContents->itemAt(contIdx))
   1348         {
   1349             /* "merge" is lower, add that one */
   1350             pNewSorted->add(pMergedInfo->itemAt(mergeIdx));
   1351             mergeIdx++;
   1352         } else {
   1353             /* "cont" is lower, add that one */
   1354             assert(pContents->itemAt(contIdx) < pMergedInfo->itemAt(mergeIdx));
   1355             pNewSorted->add(pContents->itemAt(contIdx));
   1356             contIdx++;
   1357         }
   1358     }
   1359 
   1360     /*
   1361      * Overwrite the "merged" list with the new stuff.
   1362      */
   1363     *pMergedInfo = *pNewSorted;
   1364     delete pNewSorted;
   1365 
   1366 #if 0       // for Vector, rather than SortedVector
   1367     int i, j;
   1368     for (i = pContents->size() -1; i >= 0; i--) {
   1369         bool add = true;
   1370 
   1371         for (j = pMergedInfo->size() -1; j >= 0; j--) {
   1372             /* case-sensitive comparisons, to behave like UNIX fs */
   1373             if (strcmp(pContents->itemAt(i).mFileName,
   1374                        pMergedInfo->itemAt(j).mFileName) == 0)
   1375             {
   1376                 /* match, don't add this entry */
   1377                 add = false;
   1378                 break;
   1379             }
   1380         }
   1381 
   1382         if (add)
   1383             pMergedInfo->add(pContents->itemAt(i));
   1384     }
   1385 #endif
   1386 }
   1387 
   1388 
   1389 /*
   1390  * Load all files into the file name cache.  We want to do this across
   1391  * all combinations of { appname, locale, vendor }, performing a recursive
   1392  * directory traversal.
   1393  *
   1394  * This is not the most efficient data structure.  Also, gathering the
   1395  * information as we needed it (file-by-file or directory-by-directory)
   1396  * would be faster.  However, on the actual device, 99% of the files will
   1397  * live in Zip archives, so this list will be very small.  The trouble
   1398  * is that we have to check the "loose" files first, so it's important
   1399  * that we don't beat the filesystem silly looking for files that aren't
   1400  * there.
   1401  *
   1402  * Note on thread safety: this is the only function that causes updates
   1403  * to mCache, and anybody who tries to use it will call here if !mCacheValid,
   1404  * so we need to employ a mutex here.
   1405  */
   1406 void AssetManager::loadFileNameCacheLocked(void)
   1407 {
   1408     assert(!mCacheValid);
   1409     assert(mCache.size() == 0);
   1410 
   1411 #ifdef DO_TIMINGS   // need to link against -lrt for this now
   1412     DurationTimer timer;
   1413     timer.start();
   1414 #endif
   1415 
   1416     fncScanLocked(&mCache, "");
   1417 
   1418 #ifdef DO_TIMINGS
   1419     timer.stop();
   1420     LOGD("Cache scan took %.3fms\n",
   1421         timer.durationUsecs() / 1000.0);
   1422 #endif
   1423 
   1424 #if 0
   1425     int i;
   1426     printf("CACHED FILE LIST (%d entries):\n", mCache.size());
   1427     for (i = 0; i < (int) mCache.size(); i++) {
   1428         printf(" %d: (%d) '%s'\n", i,
   1429             mCache.itemAt(i).getFileType(),
   1430             (const char*) mCache.itemAt(i).getFileName());
   1431     }
   1432 #endif
   1433 
   1434     mCacheValid = true;
   1435 }
   1436 
   1437 /*
   1438  * Scan up to 8 versions of the specified directory.
   1439  */
   1440 void AssetManager::fncScanLocked(SortedVector<AssetDir::FileInfo>* pMergedInfo,
   1441     const char* dirName)
   1442 {
   1443     size_t i = mAssetPaths.size();
   1444     while (i > 0) {
   1445         i--;
   1446         const asset_path& ap = mAssetPaths.itemAt(i);
   1447         fncScanAndMergeDirLocked(pMergedInfo, ap, NULL, NULL, dirName);
   1448         if (mLocale != NULL)
   1449             fncScanAndMergeDirLocked(pMergedInfo, ap, mLocale, NULL, dirName);
   1450         if (mVendor != NULL)
   1451             fncScanAndMergeDirLocked(pMergedInfo, ap, NULL, mVendor, dirName);
   1452         if (mLocale != NULL && mVendor != NULL)
   1453             fncScanAndMergeDirLocked(pMergedInfo, ap, mLocale, mVendor, dirName);
   1454     }
   1455 }
   1456 
   1457 /*
   1458  * Recursively scan this directory and all subdirs.
   1459  *
   1460  * This is similar to scanAndMergeDir, but we don't remove the .EXCLUDE
   1461  * files, and we prepend the extended partial path to the filenames.
   1462  */
   1463 bool AssetManager::fncScanAndMergeDirLocked(
   1464     SortedVector<AssetDir::FileInfo>* pMergedInfo,
   1465     const asset_path& ap, const char* locale, const char* vendor,
   1466     const char* dirName)
   1467 {
   1468     SortedVector<AssetDir::FileInfo>* pContents;
   1469     String8 partialPath;
   1470     String8 fullPath;
   1471 
   1472     // XXX This is broken -- the filename cache needs to hold the base
   1473     // asset path separately from its filename.
   1474 
   1475     partialPath = createPathNameLocked(ap, locale, vendor);
   1476     if (dirName[0] != '\0') {
   1477         partialPath.appendPath(dirName);
   1478     }
   1479 
   1480     fullPath = partialPath;
   1481     pContents = scanDirLocked(fullPath);
   1482     if (pContents == NULL) {
   1483         return false;       // directory did not exist
   1484     }
   1485 
   1486     /*
   1487      * Scan all subdirectories of the current dir, merging what we find
   1488      * into "pMergedInfo".
   1489      */
   1490     for (int i = 0; i < (int) pContents->size(); i++) {
   1491         if (pContents->itemAt(i).getFileType() == kFileTypeDirectory) {
   1492             String8 subdir(dirName);
   1493             subdir.appendPath(pContents->itemAt(i).getFileName());
   1494 
   1495             fncScanAndMergeDirLocked(pMergedInfo, ap, locale, vendor, subdir.string());
   1496         }
   1497     }
   1498 
   1499     /*
   1500      * To be consistent, we want entries for the root directory.  If
   1501      * we're the root, add one now.
   1502      */
   1503     if (dirName[0] == '\0') {
   1504         AssetDir::FileInfo tmpInfo;
   1505 
   1506         tmpInfo.set(String8(""), kFileTypeDirectory);
   1507         tmpInfo.setSourceName(createPathNameLocked(ap, locale, vendor));
   1508         pContents->add(tmpInfo);
   1509     }
   1510 
   1511     /*
   1512      * We want to prepend the extended partial path to every entry in
   1513      * "pContents".  It's the same value for each entry, so this will
   1514      * not change the sorting order of the vector contents.
   1515      */
   1516     for (int i = 0; i < (int) pContents->size(); i++) {
   1517         const AssetDir::FileInfo& info = pContents->itemAt(i);
   1518         pContents->editItemAt(i).setFileName(partialPath.appendPathCopy(info.getFileName()));
   1519     }
   1520 
   1521     mergeInfoLocked(pMergedInfo, pContents);
   1522     return true;
   1523 }
   1524 
   1525 /*
   1526  * Trash the cache.
   1527  */
   1528 void AssetManager::purgeFileNameCacheLocked(void)
   1529 {
   1530     mCacheValid = false;
   1531     mCache.clear();
   1532 }
   1533 
   1534 /*
   1535  * ===========================================================================
   1536  *      AssetManager::SharedZip
   1537  * ===========================================================================
   1538  */
   1539 
   1540 
   1541 Mutex AssetManager::SharedZip::gLock;
   1542 DefaultKeyedVector<String8, wp<AssetManager::SharedZip> > AssetManager::SharedZip::gOpen;
   1543 
   1544 AssetManager::SharedZip::SharedZip(const String8& path, time_t modWhen)
   1545     : mPath(path), mZipFile(NULL), mModWhen(modWhen),
   1546       mResourceTableAsset(NULL), mResourceTable(NULL)
   1547 {
   1548     //LOGI("Creating SharedZip %p %s\n", this, (const char*)mPath);
   1549     mZipFile = new ZipFileRO;
   1550     LOGV("+++ opening zip '%s'\n", mPath.string());
   1551     if (mZipFile->open(mPath.string()) != NO_ERROR) {
   1552         LOGD("failed to open Zip archive '%s'\n", mPath.string());
   1553         delete mZipFile;
   1554         mZipFile = NULL;
   1555     }
   1556 }
   1557 
   1558 sp<AssetManager::SharedZip> AssetManager::SharedZip::get(const String8& path)
   1559 {
   1560     AutoMutex _l(gLock);
   1561     time_t modWhen = getFileModDate(path);
   1562     sp<SharedZip> zip = gOpen.valueFor(path).promote();
   1563     if (zip != NULL && zip->mModWhen == modWhen) {
   1564         return zip;
   1565     }
   1566     zip = new SharedZip(path, modWhen);
   1567     gOpen.add(path, zip);
   1568     return zip;
   1569 
   1570 }
   1571 
   1572 ZipFileRO* AssetManager::SharedZip::getZip()
   1573 {
   1574     return mZipFile;
   1575 }
   1576 
   1577 Asset* AssetManager::SharedZip::getResourceTableAsset()
   1578 {
   1579     LOGV("Getting from SharedZip %p resource asset %p\n", this, mResourceTableAsset);
   1580     return mResourceTableAsset;
   1581 }
   1582 
   1583 Asset* AssetManager::SharedZip::setResourceTableAsset(Asset* asset)
   1584 {
   1585     {
   1586         AutoMutex _l(gLock);
   1587         if (mResourceTableAsset == NULL) {
   1588             mResourceTableAsset = asset;
   1589             // This is not thread safe the first time it is called, so
   1590             // do it here with the global lock held.
   1591             asset->getBuffer(true);
   1592             return asset;
   1593         }
   1594     }
   1595     delete asset;
   1596     return mResourceTableAsset;
   1597 }
   1598 
   1599 ResTable* AssetManager::SharedZip::getResourceTable()
   1600 {
   1601     LOGV("Getting from SharedZip %p resource table %p\n", this, mResourceTable);
   1602     return mResourceTable;
   1603 }
   1604 
   1605 ResTable* AssetManager::SharedZip::setResourceTable(ResTable* res)
   1606 {
   1607     {
   1608         AutoMutex _l(gLock);
   1609         if (mResourceTable == NULL) {
   1610             mResourceTable = res;
   1611             return res;
   1612         }
   1613     }
   1614     delete res;
   1615     return mResourceTable;
   1616 }
   1617 
   1618 bool AssetManager::SharedZip::isUpToDate()
   1619 {
   1620     time_t modWhen = getFileModDate(mPath.string());
   1621     return mModWhen == modWhen;
   1622 }
   1623 
   1624 AssetManager::SharedZip::~SharedZip()
   1625 {
   1626     //LOGI("Destroying SharedZip %p %s\n", this, (const char*)mPath);
   1627     if (mResourceTable != NULL) {
   1628         delete mResourceTable;
   1629     }
   1630     if (mResourceTableAsset != NULL) {
   1631         delete mResourceTableAsset;
   1632     }
   1633     if (mZipFile != NULL) {
   1634         delete mZipFile;
   1635         LOGV("Closed '%s'\n", mPath.string());
   1636     }
   1637 }
   1638 
   1639 /*
   1640  * ===========================================================================
   1641  *      AssetManager::ZipSet
   1642  * ===========================================================================
   1643  */
   1644 
   1645 /*
   1646  * Constructor.
   1647  */
   1648 AssetManager::ZipSet::ZipSet(void)
   1649 {
   1650 }
   1651 
   1652 /*
   1653  * Destructor.  Close any open archives.
   1654  */
   1655 AssetManager::ZipSet::~ZipSet(void)
   1656 {
   1657     size_t N = mZipFile.size();
   1658     for (size_t i = 0; i < N; i++)
   1659         closeZip(i);
   1660 }
   1661 
   1662 /*
   1663  * Close a Zip file and reset the entry.
   1664  */
   1665 void AssetManager::ZipSet::closeZip(int idx)
   1666 {
   1667     mZipFile.editItemAt(idx) = NULL;
   1668 }
   1669 
   1670 
   1671 /*
   1672  * Retrieve the appropriate Zip file from the set.
   1673  */
   1674 ZipFileRO* AssetManager::ZipSet::getZip(const String8& path)
   1675 {
   1676     int idx = getIndex(path);
   1677     sp<SharedZip> zip = mZipFile[idx];
   1678     if (zip == NULL) {
   1679         zip = SharedZip::get(path);
   1680         mZipFile.editItemAt(idx) = zip;
   1681     }
   1682     return zip->getZip();
   1683 }
   1684 
   1685 Asset* AssetManager::ZipSet::getZipResourceTableAsset(const String8& path)
   1686 {
   1687     int idx = getIndex(path);
   1688     sp<SharedZip> zip = mZipFile[idx];
   1689     if (zip == NULL) {
   1690         zip = SharedZip::get(path);
   1691         mZipFile.editItemAt(idx) = zip;
   1692     }
   1693     return zip->getResourceTableAsset();
   1694 }
   1695 
   1696 Asset* AssetManager::ZipSet::setZipResourceTableAsset(const String8& path,
   1697                                                  Asset* asset)
   1698 {
   1699     int idx = getIndex(path);
   1700     sp<SharedZip> zip = mZipFile[idx];
   1701     // doesn't make sense to call before previously accessing.
   1702     return zip->setResourceTableAsset(asset);
   1703 }
   1704 
   1705 ResTable* AssetManager::ZipSet::getZipResourceTable(const String8& path)
   1706 {
   1707     int idx = getIndex(path);
   1708     sp<SharedZip> zip = mZipFile[idx];
   1709     if (zip == NULL) {
   1710         zip = SharedZip::get(path);
   1711         mZipFile.editItemAt(idx) = zip;
   1712     }
   1713     return zip->getResourceTable();
   1714 }
   1715 
   1716 ResTable* AssetManager::ZipSet::setZipResourceTable(const String8& path,
   1717                                                     ResTable* res)
   1718 {
   1719     int idx = getIndex(path);
   1720     sp<SharedZip> zip = mZipFile[idx];
   1721     // doesn't make sense to call before previously accessing.
   1722     return zip->setResourceTable(res);
   1723 }
   1724 
   1725 /*
   1726  * Generate the partial pathname for the specified archive.  The caller
   1727  * gets to prepend the asset root directory.
   1728  *
   1729  * Returns something like "common/en-US-noogle.jar".
   1730  */
   1731 /*static*/ String8 AssetManager::ZipSet::getPathName(const char* zipPath)
   1732 {
   1733     return String8(zipPath);
   1734 }
   1735 
   1736 bool AssetManager::ZipSet::isUpToDate()
   1737 {
   1738     const size_t N = mZipFile.size();
   1739     for (size_t i=0; i<N; i++) {
   1740         if (mZipFile[i] != NULL && !mZipFile[i]->isUpToDate()) {
   1741             return false;
   1742         }
   1743     }
   1744     return true;
   1745 }
   1746 
   1747 /*
   1748  * Compute the zip file's index.
   1749  *
   1750  * "appName", "locale", and "vendor" should be set to NULL to indicate the
   1751  * default directory.
   1752  */
   1753 int AssetManager::ZipSet::getIndex(const String8& zip) const
   1754 {
   1755     const size_t N = mZipPath.size();
   1756     for (size_t i=0; i<N; i++) {
   1757         if (mZipPath[i] == zip) {
   1758             return i;
   1759         }
   1760     }
   1761 
   1762     mZipPath.add(zip);
   1763     mZipFile.add(NULL);
   1764 
   1765     return mZipPath.size()-1;
   1766 }
   1767 
   1768