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