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