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