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