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