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