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