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