AssetManager.cpp revision 224ffd3cb2b4f1cf031877d7ecba50562b3b9571
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
39#include <assert.h>
40#include <dirent.h>
41#include <errno.h>
42#include <string.h> // strerror
43#include <strings.h>
44
45#ifndef TEMP_FAILURE_RETRY
46/* Used to retry syscalls that can return EINTR. */
47#define TEMP_FAILURE_RETRY(exp) ({         \
48    typeof (exp) _rc;                      \
49    do {                                   \
50        _rc = (exp);                       \
51    } while (_rc == -1 && errno == EINTR); \
52    _rc; })
53#endif
54
55using namespace android;
56
57static const bool kIsDebug = false;
58
59/*
60 * Names for default app, locale, and vendor.  We might want to change
61 * these to be an actual locale, e.g. always use en-US as the default.
62 */
63static const char* kDefaultLocale = "default";
64static const char* kDefaultVendor = "default";
65static const char* kAssetsRoot = "assets";
66static const char* kAppZipName = NULL; //"classes.jar";
67static const char* kSystemAssets = "framework/framework-res.apk";
68static const char* kResourceCache = "resource-cache";
69
70static const char* kExcludeExtension = ".EXCLUDE";
71
72static Asset* const kExcludedAsset = (Asset*) 0xd000000d;
73
74static volatile int32_t gCount = 0;
75
76const char* AssetManager::RESOURCES_FILENAME = "resources.arsc";
77const char* AssetManager::IDMAP_BIN = "/system/bin/idmap";
78const char* AssetManager::OVERLAY_DIR = "/vendor/overlay";
79const char* AssetManager::OVERLAY_SKU_DIR_PROPERTY = "ro.boot.vendor.overlay.sku";
80const char* AssetManager::TARGET_PACKAGE_NAME = "android";
81const char* AssetManager::TARGET_APK_PATH = "/system/framework/framework-res.apk";
82const char* AssetManager::IDMAP_DIR = "/data/resource-cache";
83
84namespace {
85    String8 idmapPathForPackagePath(const String8& pkgPath)
86    {
87        const char* root = getenv("ANDROID_DATA");
88        LOG_ALWAYS_FATAL_IF(root == NULL, "ANDROID_DATA not set");
89        String8 path(root);
90        path.appendPath(kResourceCache);
91
92        char buf[256]; // 256 chars should be enough for anyone...
93        strncpy(buf, pkgPath.string(), 255);
94        buf[255] = '\0';
95        char* filename = buf;
96        while (*filename && *filename == '/') {
97            ++filename;
98        }
99        char* p = filename;
100        while (*p) {
101            if (*p == '/') {
102                *p = '@';
103            }
104            ++p;
105        }
106        path.appendPath(filename);
107        path.append("@idmap");
108
109        return path;
110    }
111
112    /*
113     * Like strdup(), but uses C++ "new" operator instead of malloc.
114     */
115    static char* strdupNew(const char* str)
116    {
117        char* newStr;
118        int len;
119
120        if (str == NULL)
121            return NULL;
122
123        len = strlen(str);
124        newStr = new char[len+1];
125        memcpy(newStr, str, len+1);
126
127        return newStr;
128    }
129}
130
131/*
132 * ===========================================================================
133 *      AssetManager
134 * ===========================================================================
135 */
136
137int32_t AssetManager::getGlobalCount()
138{
139    return gCount;
140}
141
142AssetManager::AssetManager(CacheMode cacheMode)
143    : mLocale(NULL), mVendor(NULL),
144      mResources(NULL), mConfig(new ResTable_config),
145      mCacheMode(cacheMode), mCacheValid(false)
146{
147    int count = android_atomic_inc(&gCount) + 1;
148    if (kIsDebug) {
149        ALOGI("Creating AssetManager %p #%d\n", this, count);
150    }
151    memset(mConfig, 0, sizeof(ResTable_config));
152}
153
154AssetManager::~AssetManager(void)
155{
156    int count = android_atomic_dec(&gCount);
157    if (kIsDebug) {
158        ALOGI("Destroying AssetManager in %p #%d\n", this, count);
159    }
160
161    delete mConfig;
162    delete mResources;
163
164    // don't have a String class yet, so make sure we clean up
165    delete[] mLocale;
166    delete[] mVendor;
167}
168
169bool AssetManager::addAssetPath(
170        const String8& path, int32_t* cookie, bool appAsLib, bool isSystemAsset)
171{
172    AutoMutex _l(mLock);
173
174    asset_path ap;
175
176    String8 realPath(path);
177    if (kAppZipName) {
178        realPath.appendPath(kAppZipName);
179    }
180    ap.type = ::getFileType(realPath.string());
181    if (ap.type == kFileTypeRegular) {
182        ap.path = realPath;
183    } else {
184        ap.path = path;
185        ap.type = ::getFileType(path.string());
186        if (ap.type != kFileTypeDirectory && ap.type != kFileTypeRegular) {
187            ALOGW("Asset path %s is neither a directory nor file (type=%d).",
188                 path.string(), (int)ap.type);
189            return false;
190        }
191    }
192
193    // Skip if we have it already.
194    for (size_t i=0; i<mAssetPaths.size(); i++) {
195        if (mAssetPaths[i].path == ap.path) {
196            if (cookie) {
197                *cookie = static_cast<int32_t>(i+1);
198            }
199            return true;
200        }
201    }
202
203    ALOGV("In %p Asset %s path: %s", this,
204         ap.type == kFileTypeDirectory ? "dir" : "zip", ap.path.string());
205
206    ap.isSystemAsset = isSystemAsset;
207    mAssetPaths.add(ap);
208
209    // new paths are always added at the end
210    if (cookie) {
211        *cookie = static_cast<int32_t>(mAssetPaths.size());
212    }
213
214#ifdef __ANDROID__
215    // Load overlays, if any
216    asset_path oap;
217    for (size_t idx = 0; mZipSet.getOverlay(ap.path, idx, &oap); idx++) {
218        oap.isSystemAsset = isSystemAsset;
219        mAssetPaths.add(oap);
220    }
221#endif
222
223    if (mResources != NULL) {
224        appendPathToResTable(ap, appAsLib);
225    }
226
227    return true;
228}
229
230bool AssetManager::addOverlayPath(const String8& packagePath, int32_t* cookie)
231{
232    const String8 idmapPath = idmapPathForPackagePath(packagePath);
233
234    AutoMutex _l(mLock);
235
236    for (size_t i = 0; i < mAssetPaths.size(); ++i) {
237        if (mAssetPaths[i].idmap == idmapPath) {
238           *cookie = static_cast<int32_t>(i + 1);
239            return true;
240         }
241     }
242
243    Asset* idmap = NULL;
244    if ((idmap = openAssetFromFileLocked(idmapPath, Asset::ACCESS_BUFFER)) == NULL) {
245        ALOGW("failed to open idmap file %s\n", idmapPath.string());
246        return false;
247    }
248
249    String8 targetPath;
250    String8 overlayPath;
251    if (!ResTable::getIdmapInfo(idmap->getBuffer(false), idmap->getLength(),
252                NULL, NULL, NULL, &targetPath, &overlayPath)) {
253        ALOGW("failed to read idmap file %s\n", idmapPath.string());
254        delete idmap;
255        return false;
256    }
257    delete idmap;
258
259    if (overlayPath != packagePath) {
260        ALOGW("idmap file %s inconcistent: expected path %s does not match actual path %s\n",
261                idmapPath.string(), packagePath.string(), overlayPath.string());
262        return false;
263    }
264    if (access(targetPath.string(), R_OK) != 0) {
265        ALOGW("failed to access file %s: %s\n", targetPath.string(), strerror(errno));
266        return false;
267    }
268    if (access(idmapPath.string(), R_OK) != 0) {
269        ALOGW("failed to access file %s: %s\n", idmapPath.string(), strerror(errno));
270        return false;
271    }
272    if (access(overlayPath.string(), R_OK) != 0) {
273        ALOGW("failed to access file %s: %s\n", overlayPath.string(), strerror(errno));
274        return false;
275    }
276
277    asset_path oap;
278    oap.path = overlayPath;
279    oap.type = ::getFileType(overlayPath.string());
280    oap.idmap = idmapPath;
281#if 0
282    ALOGD("Overlay added: targetPath=%s overlayPath=%s idmapPath=%s\n",
283            targetPath.string(), overlayPath.string(), idmapPath.string());
284#endif
285    mAssetPaths.add(oap);
286    *cookie = static_cast<int32_t>(mAssetPaths.size());
287
288    if (mResources != NULL) {
289        appendPathToResTable(oap);
290    }
291
292    return true;
293 }
294
295bool AssetManager::createIdmap(const char* targetApkPath, const char* overlayApkPath,
296        uint32_t targetCrc, uint32_t overlayCrc, uint32_t** outData, size_t* outSize)
297{
298    AutoMutex _l(mLock);
299    const String8 paths[2] = { String8(targetApkPath), String8(overlayApkPath) };
300    ResTable tables[2];
301
302    for (int i = 0; i < 2; ++i) {
303        asset_path ap;
304        ap.type = kFileTypeRegular;
305        ap.path = paths[i];
306        Asset* ass = openNonAssetInPathLocked("resources.arsc", Asset::ACCESS_BUFFER, ap);
307        if (ass == NULL) {
308            ALOGW("failed to find resources.arsc in %s\n", ap.path.string());
309            return false;
310        }
311        tables[i].add(ass);
312    }
313
314    return tables[0].createIdmap(tables[1], targetCrc, overlayCrc,
315            targetApkPath, overlayApkPath, (void**)outData, outSize) == NO_ERROR;
316}
317
318bool AssetManager::addDefaultAssets()
319{
320    const char* root = getenv("ANDROID_ROOT");
321    LOG_ALWAYS_FATAL_IF(root == NULL, "ANDROID_ROOT not set");
322
323    String8 path(root);
324    path.appendPath(kSystemAssets);
325
326    return addAssetPath(path, NULL, false /* appAsLib */, true /* isSystemAsset */);
327}
328
329int32_t AssetManager::nextAssetPath(const int32_t cookie) const
330{
331    AutoMutex _l(mLock);
332    const size_t next = static_cast<size_t>(cookie) + 1;
333    return next > mAssetPaths.size() ? -1 : next;
334}
335
336String8 AssetManager::getAssetPath(const int32_t cookie) const
337{
338    AutoMutex _l(mLock);
339    const size_t which = static_cast<size_t>(cookie) - 1;
340    if (which < mAssetPaths.size()) {
341        return mAssetPaths[which].path;
342    }
343    return String8();
344}
345
346/*
347 * Set the current locale.  Use NULL to indicate no locale.
348 *
349 * Close and reopen Zip archives as appropriate, and reset cached
350 * information in the locale-specific sections of the tree.
351 */
352void AssetManager::setLocale(const char* locale)
353{
354    AutoMutex _l(mLock);
355    setLocaleLocked(locale);
356}
357
358
359static const char kFilPrefix[] = "fil";
360static const char kTlPrefix[] = "tl";
361
362// The sizes of the prefixes, excluding the 0 suffix.
363// char.
364static const int kFilPrefixLen = sizeof(kFilPrefix) - 1;
365static const int kTlPrefixLen = sizeof(kTlPrefix) - 1;
366
367void AssetManager::setLocaleLocked(const char* locale)
368{
369    if (mLocale != NULL) {
370        /* previously set, purge cached data */
371        purgeFileNameCacheLocked();
372        //mZipSet.purgeLocale();
373        delete[] mLocale;
374    }
375
376    // If we're attempting to set a locale that starts with "fil",
377    // we should convert it to "tl" for backwards compatibility since
378    // we've been using "tl" instead of "fil" prior to L.
379    //
380    // If the resource table already has entries for "fil", we use that
381    // instead of attempting a fallback.
382    if (strncmp(locale, kFilPrefix, kFilPrefixLen) == 0) {
383        Vector<String8> locales;
384        ResTable* res = mResources;
385        if (res != NULL) {
386            res->getLocales(&locales);
387        }
388        const size_t localesSize = locales.size();
389        bool hasFil = false;
390        for (size_t i = 0; i < localesSize; ++i) {
391            if (locales[i].find(kFilPrefix) == 0) {
392                hasFil = true;
393                break;
394            }
395        }
396
397
398        if (!hasFil) {
399            const size_t newLocaleLen = strlen(locale);
400            // This isn't a bug. We really do want mLocale to be 1 byte
401            // shorter than locale, because we're replacing "fil-" with
402            // "tl-".
403            mLocale = new char[newLocaleLen];
404            // Copy over "tl".
405            memcpy(mLocale, kTlPrefix, kTlPrefixLen);
406            // Copy the rest of |locale|, including the terminating '\0'.
407            memcpy(mLocale + kTlPrefixLen, locale + kFilPrefixLen,
408                   newLocaleLen - kFilPrefixLen + 1);
409            updateResourceParamsLocked();
410            return;
411        }
412    }
413
414    mLocale = strdupNew(locale);
415    updateResourceParamsLocked();
416}
417
418/*
419 * Set the current vendor.  Use NULL to indicate no vendor.
420 *
421 * Close and reopen Zip archives as appropriate, and reset cached
422 * information in the vendor-specific sections of the tree.
423 */
424void AssetManager::setVendor(const char* vendor)
425{
426    AutoMutex _l(mLock);
427
428    if (mVendor != NULL) {
429        /* previously set, purge cached data */
430        purgeFileNameCacheLocked();
431        //mZipSet.purgeVendor();
432        delete[] mVendor;
433    }
434    mVendor = strdupNew(vendor);
435}
436
437void AssetManager::setConfiguration(const ResTable_config& config, const char* locale)
438{
439    AutoMutex _l(mLock);
440    *mConfig = config;
441    if (locale) {
442        setLocaleLocked(locale);
443    } else if (config.language[0] != 0) {
444        char spec[RESTABLE_MAX_LOCALE_LEN];
445        config.getBcp47Locale(spec);
446        setLocaleLocked(spec);
447    } else {
448        updateResourceParamsLocked();
449    }
450}
451
452void AssetManager::getConfiguration(ResTable_config* outConfig) const
453{
454    AutoMutex _l(mLock);
455    *outConfig = *mConfig;
456}
457
458/*
459 * Open an asset.
460 *
461 * The data could be;
462 *  - In a file on disk (assetBase + fileName).
463 *  - In a compressed file on disk (assetBase + fileName.gz).
464 *  - In a Zip archive, uncompressed or compressed.
465 *
466 * It can be in a number of different directories and Zip archives.
467 * The search order is:
468 *  - [appname]
469 *    - locale + vendor
470 *    - "default" + vendor
471 *    - locale + "default"
472 *    - "default + "default"
473 *  - "common"
474 *    - (same as above)
475 *
476 * To find a particular file, we have to try up to eight paths with
477 * all three forms of data.
478 *
479 * We should probably reject requests for "illegal" filenames, e.g. those
480 * with illegal characters or "../" backward relative paths.
481 */
482Asset* AssetManager::open(const char* fileName, AccessMode mode)
483{
484    AutoMutex _l(mLock);
485
486    LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager");
487
488
489    if (mCacheMode != CACHE_OFF && !mCacheValid)
490        loadFileNameCacheLocked();
491
492    String8 assetName(kAssetsRoot);
493    assetName.appendPath(fileName);
494
495    /*
496     * For each top-level asset path, search for the asset.
497     */
498
499    size_t i = mAssetPaths.size();
500    while (i > 0) {
501        i--;
502        ALOGV("Looking for asset '%s' in '%s'\n",
503                assetName.string(), mAssetPaths.itemAt(i).path.string());
504        Asset* pAsset = openNonAssetInPathLocked(assetName.string(), mode, mAssetPaths.itemAt(i));
505        if (pAsset != NULL) {
506            return pAsset != kExcludedAsset ? pAsset : NULL;
507        }
508    }
509
510    return NULL;
511}
512
513/*
514 * Open a non-asset file as if it were an asset.
515 *
516 * The "fileName" is the partial path starting from the application
517 * name.
518 */
519Asset* AssetManager::openNonAsset(const char* fileName, AccessMode mode, int32_t* outCookie)
520{
521    AutoMutex _l(mLock);
522
523    LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager");
524
525
526    if (mCacheMode != CACHE_OFF && !mCacheValid)
527        loadFileNameCacheLocked();
528
529    /*
530     * For each top-level asset path, search for the asset.
531     */
532
533    size_t i = mAssetPaths.size();
534    while (i > 0) {
535        i--;
536        ALOGV("Looking for non-asset '%s' in '%s'\n", fileName, mAssetPaths.itemAt(i).path.string());
537        Asset* pAsset = openNonAssetInPathLocked(
538            fileName, mode, mAssetPaths.itemAt(i));
539        if (pAsset != NULL) {
540            if (outCookie != NULL) *outCookie = static_cast<int32_t>(i + 1);
541            return pAsset != kExcludedAsset ? pAsset : NULL;
542        }
543    }
544
545    return NULL;
546}
547
548Asset* AssetManager::openNonAsset(const int32_t cookie, const char* fileName, AccessMode mode)
549{
550    const size_t which = static_cast<size_t>(cookie) - 1;
551
552    AutoMutex _l(mLock);
553
554    LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager");
555
556    if (mCacheMode != CACHE_OFF && !mCacheValid)
557        loadFileNameCacheLocked();
558
559    if (which < mAssetPaths.size()) {
560        ALOGV("Looking for non-asset '%s' in '%s'\n", fileName,
561                mAssetPaths.itemAt(which).path.string());
562        Asset* pAsset = openNonAssetInPathLocked(
563            fileName, mode, mAssetPaths.itemAt(which));
564        if (pAsset != NULL) {
565            return pAsset != kExcludedAsset ? pAsset : NULL;
566        }
567    }
568
569    return NULL;
570}
571
572/*
573 * Get the type of a file in the asset namespace.
574 *
575 * This currently only works for regular files.  All others (including
576 * directories) will return kFileTypeNonexistent.
577 */
578FileType AssetManager::getFileType(const char* fileName)
579{
580    Asset* pAsset = NULL;
581
582    /*
583     * Open the asset.  This is less efficient than simply finding the
584     * file, but it's not too bad (we don't uncompress or mmap data until
585     * the first read() call).
586     */
587    pAsset = open(fileName, Asset::ACCESS_STREAMING);
588    delete pAsset;
589
590    if (pAsset == NULL)
591        return kFileTypeNonexistent;
592    else
593        return kFileTypeRegular;
594}
595
596bool AssetManager::appendPathToResTable(const asset_path& ap, bool appAsLib) const {
597    // skip those ap's that correspond to system overlays
598    if (ap.isSystemOverlay) {
599        return true;
600    }
601
602    Asset* ass = NULL;
603    ResTable* sharedRes = NULL;
604    bool shared = true;
605    bool onlyEmptyResources = true;
606    ATRACE_NAME(ap.path.string());
607    Asset* idmap = openIdmapLocked(ap);
608    size_t nextEntryIdx = mResources->getTableCount();
609    ALOGV("Looking for resource asset in '%s'\n", ap.path.string());
610    if (ap.type != kFileTypeDirectory) {
611        if (nextEntryIdx == 0) {
612            // The first item is typically the framework resources,
613            // which we want to avoid parsing every time.
614            sharedRes = const_cast<AssetManager*>(this)->
615                mZipSet.getZipResourceTable(ap.path);
616            if (sharedRes != NULL) {
617                // skip ahead the number of system overlay packages preloaded
618                nextEntryIdx = sharedRes->getTableCount();
619            }
620        }
621        if (sharedRes == NULL) {
622            ass = const_cast<AssetManager*>(this)->
623                mZipSet.getZipResourceTableAsset(ap.path);
624            if (ass == NULL) {
625                ALOGV("loading resource table %s\n", ap.path.string());
626                ass = const_cast<AssetManager*>(this)->
627                    openNonAssetInPathLocked("resources.arsc",
628                                             Asset::ACCESS_BUFFER,
629                                             ap);
630                if (ass != NULL && ass != kExcludedAsset) {
631                    ass = const_cast<AssetManager*>(this)->
632                        mZipSet.setZipResourceTableAsset(ap.path, ass);
633                }
634            }
635
636            if (nextEntryIdx == 0 && ass != NULL) {
637                // If this is the first resource table in the asset
638                // manager, then we are going to cache it so that we
639                // can quickly copy it out for others.
640                ALOGV("Creating shared resources for %s", ap.path.string());
641                sharedRes = new ResTable();
642                sharedRes->add(ass, idmap, nextEntryIdx + 1, false);
643#ifdef __ANDROID__
644                const char* data = getenv("ANDROID_DATA");
645                LOG_ALWAYS_FATAL_IF(data == NULL, "ANDROID_DATA not set");
646                String8 overlaysListPath(data);
647                overlaysListPath.appendPath(kResourceCache);
648                overlaysListPath.appendPath("overlays.list");
649                addSystemOverlays(overlaysListPath.string(), ap.path, sharedRes, nextEntryIdx);
650#endif
651                sharedRes = const_cast<AssetManager*>(this)->
652                    mZipSet.setZipResourceTable(ap.path, sharedRes);
653            }
654        }
655    } else {
656        ALOGV("loading resource table %s\n", ap.path.string());
657        ass = const_cast<AssetManager*>(this)->
658            openNonAssetInPathLocked("resources.arsc",
659                                     Asset::ACCESS_BUFFER,
660                                     ap);
661        shared = false;
662    }
663
664    if ((ass != NULL || sharedRes != NULL) && ass != kExcludedAsset) {
665        ALOGV("Installing resource asset %p in to table %p\n", ass, mResources);
666        if (sharedRes != NULL) {
667            ALOGV("Copying existing resources for %s", ap.path.string());
668            mResources->add(sharedRes, ap.isSystemAsset);
669        } else {
670            ALOGV("Parsing resources for %s", ap.path.string());
671            mResources->add(ass, idmap, nextEntryIdx + 1, !shared, appAsLib, ap.isSystemAsset);
672        }
673        onlyEmptyResources = false;
674
675        if (!shared) {
676            delete ass;
677        }
678    } else {
679        ALOGV("Installing empty resources in to table %p\n", mResources);
680        mResources->addEmpty(nextEntryIdx + 1);
681    }
682
683    if (idmap != NULL) {
684        delete idmap;
685    }
686    return onlyEmptyResources;
687}
688
689const ResTable* AssetManager::getResTable(bool required) const
690{
691    ResTable* rt = mResources;
692    if (rt) {
693        return rt;
694    }
695
696    // Iterate through all asset packages, collecting resources from each.
697
698    AutoMutex _l(mLock);
699
700    if (mResources != NULL) {
701        return mResources;
702    }
703
704    if (required) {
705        LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager");
706    }
707
708    if (mCacheMode != CACHE_OFF && !mCacheValid) {
709        const_cast<AssetManager*>(this)->loadFileNameCacheLocked();
710    }
711
712    mResources = new ResTable();
713    updateResourceParamsLocked();
714
715    bool onlyEmptyResources = true;
716    const size_t N = mAssetPaths.size();
717    for (size_t i=0; i<N; i++) {
718        bool empty = appendPathToResTable(mAssetPaths.itemAt(i));
719        onlyEmptyResources = onlyEmptyResources && empty;
720    }
721
722    if (required && onlyEmptyResources) {
723        ALOGW("Unable to find resources file resources.arsc");
724        delete mResources;
725        mResources = NULL;
726    }
727
728    return mResources;
729}
730
731void AssetManager::updateResourceParamsLocked() const
732{
733    ATRACE_CALL();
734    ResTable* res = mResources;
735    if (!res) {
736        return;
737    }
738
739    if (mLocale) {
740        mConfig->setBcp47Locale(mLocale);
741    } else {
742        mConfig->clearLocale();
743    }
744
745    res->setParameters(mConfig);
746}
747
748Asset* AssetManager::openIdmapLocked(const struct asset_path& ap) const
749{
750    Asset* ass = NULL;
751    if (ap.idmap.size() != 0) {
752        ass = const_cast<AssetManager*>(this)->
753            openAssetFromFileLocked(ap.idmap, Asset::ACCESS_BUFFER);
754        if (ass) {
755            ALOGV("loading idmap %s\n", ap.idmap.string());
756        } else {
757            ALOGW("failed to load idmap %s\n", ap.idmap.string());
758        }
759    }
760    return ass;
761}
762
763void AssetManager::addSystemOverlays(const char* pathOverlaysList,
764        const String8& targetPackagePath, ResTable* sharedRes, size_t offset) const
765{
766    FILE* fin = fopen(pathOverlaysList, "r");
767    if (fin == NULL) {
768        return;
769    }
770
771    char buf[1024];
772    while (fgets(buf, sizeof(buf), fin)) {
773        // format of each line:
774        //   <path to apk><space><path to idmap><newline>
775        char* space = strchr(buf, ' ');
776        char* newline = strchr(buf, '\n');
777        asset_path oap;
778
779        if (space == NULL || newline == NULL || newline < space) {
780            continue;
781        }
782
783        oap.path = String8(buf, space - buf);
784        oap.type = kFileTypeRegular;
785        oap.idmap = String8(space + 1, newline - space - 1);
786        oap.isSystemOverlay = true;
787
788        Asset* oass = const_cast<AssetManager*>(this)->
789            openNonAssetInPathLocked("resources.arsc",
790                    Asset::ACCESS_BUFFER,
791                    oap);
792
793        if (oass != NULL) {
794            Asset* oidmap = openIdmapLocked(oap);
795            offset++;
796            sharedRes->add(oass, oidmap, offset + 1, false);
797            const_cast<AssetManager*>(this)->mAssetPaths.add(oap);
798            const_cast<AssetManager*>(this)->mZipSet.addOverlay(targetPackagePath, oap);
799        }
800    }
801    fclose(fin);
802}
803
804const ResTable& AssetManager::getResources(bool required) const
805{
806    const ResTable* rt = getResTable(required);
807    return *rt;
808}
809
810bool AssetManager::isUpToDate()
811{
812    AutoMutex _l(mLock);
813    return mZipSet.isUpToDate();
814}
815
816void AssetManager::getLocales(Vector<String8>* locales, bool includeSystemLocales) const
817{
818    ResTable* res = mResources;
819    if (res != NULL) {
820        res->getLocales(locales, includeSystemLocales);
821    }
822
823    const size_t numLocales = locales->size();
824    for (size_t i = 0; i < numLocales; ++i) {
825        const String8& localeStr = locales->itemAt(i);
826        if (localeStr.find(kTlPrefix) == 0) {
827            String8 replaced("fil");
828            replaced += (localeStr.string() + kTlPrefixLen);
829            locales->editItemAt(i) = replaced;
830        }
831    }
832}
833
834/*
835 * Open a non-asset file as if it were an asset, searching for it in the
836 * specified app.
837 *
838 * Pass in a NULL values for "appName" if the common app directory should
839 * be used.
840 */
841Asset* AssetManager::openNonAssetInPathLocked(const char* fileName, AccessMode mode,
842    const asset_path& ap)
843{
844    Asset* pAsset = NULL;
845
846    /* look at the filesystem on disk */
847    if (ap.type == kFileTypeDirectory) {
848        String8 path(ap.path);
849        path.appendPath(fileName);
850
851        pAsset = openAssetFromFileLocked(path, mode);
852
853        if (pAsset == NULL) {
854            /* try again, this time with ".gz" */
855            path.append(".gz");
856            pAsset = openAssetFromFileLocked(path, mode);
857        }
858
859        if (pAsset != NULL) {
860            //printf("FOUND NA '%s' on disk\n", fileName);
861            pAsset->setAssetSource(path);
862        }
863
864    /* look inside the zip file */
865    } else {
866        String8 path(fileName);
867
868        /* check the appropriate Zip file */
869        ZipFileRO* pZip = getZipFileLocked(ap);
870        if (pZip != NULL) {
871            //printf("GOT zip, checking NA '%s'\n", (const char*) path);
872            ZipEntryRO entry = pZip->findEntryByName(path.string());
873            if (entry != NULL) {
874                //printf("FOUND NA in Zip file for %s\n", appName ? appName : kAppCommon);
875                pAsset = openAssetFromZipLocked(pZip, entry, mode, path);
876                pZip->releaseEntry(entry);
877            }
878        }
879
880        if (pAsset != NULL) {
881            /* create a "source" name, for debug/display */
882            pAsset->setAssetSource(
883                    createZipSourceNameLocked(ZipSet::getPathName(ap.path.string()), String8(""),
884                                                String8(fileName)));
885        }
886    }
887
888    return pAsset;
889}
890
891/*
892 * Open an asset, searching for it in the directory hierarchy for the
893 * specified app.
894 *
895 * Pass in a NULL values for "appName" if the common app directory should
896 * be used.
897 */
898Asset* AssetManager::openInPathLocked(const char* fileName, AccessMode mode,
899    const asset_path& ap)
900{
901    Asset* pAsset = NULL;
902
903    /*
904     * Try various combinations of locale and vendor.
905     */
906    if (mLocale != NULL && mVendor != NULL)
907        pAsset = openInLocaleVendorLocked(fileName, mode, ap, mLocale, mVendor);
908    if (pAsset == NULL && mVendor != NULL)
909        pAsset = openInLocaleVendorLocked(fileName, mode, ap, NULL, mVendor);
910    if (pAsset == NULL && mLocale != NULL)
911        pAsset = openInLocaleVendorLocked(fileName, mode, ap, mLocale, NULL);
912    if (pAsset == NULL)
913        pAsset = openInLocaleVendorLocked(fileName, mode, ap, NULL, NULL);
914
915    return pAsset;
916}
917
918/*
919 * Open an asset, searching for it in the directory hierarchy for the
920 * specified locale and vendor.
921 *
922 * We also search in "app.jar".
923 *
924 * Pass in NULL values for "appName", "locale", and "vendor" if the
925 * defaults should be used.
926 */
927Asset* AssetManager::openInLocaleVendorLocked(const char* fileName, AccessMode mode,
928    const asset_path& ap, const char* locale, const char* vendor)
929{
930    Asset* pAsset = NULL;
931
932    if (ap.type == kFileTypeDirectory) {
933        if (mCacheMode == CACHE_OFF) {
934            /* look at the filesystem on disk */
935            String8 path(createPathNameLocked(ap, locale, vendor));
936            path.appendPath(fileName);
937
938            String8 excludeName(path);
939            excludeName.append(kExcludeExtension);
940            if (::getFileType(excludeName.string()) != kFileTypeNonexistent) {
941                /* say no more */
942                //printf("+++ excluding '%s'\n", (const char*) excludeName);
943                return kExcludedAsset;
944            }
945
946            pAsset = openAssetFromFileLocked(path, mode);
947
948            if (pAsset == NULL) {
949                /* try again, this time with ".gz" */
950                path.append(".gz");
951                pAsset = openAssetFromFileLocked(path, mode);
952            }
953
954            if (pAsset != NULL)
955                pAsset->setAssetSource(path);
956        } else {
957            /* find in cache */
958            String8 path(createPathNameLocked(ap, locale, vendor));
959            path.appendPath(fileName);
960
961            AssetDir::FileInfo tmpInfo;
962            bool found = false;
963
964            String8 excludeName(path);
965            excludeName.append(kExcludeExtension);
966
967            if (mCache.indexOf(excludeName) != NAME_NOT_FOUND) {
968                /* go no farther */
969                //printf("+++ Excluding '%s'\n", (const char*) excludeName);
970                return kExcludedAsset;
971            }
972
973            /*
974             * File compression extensions (".gz") don't get stored in the
975             * name cache, so we have to try both here.
976             */
977            if (mCache.indexOf(path) != NAME_NOT_FOUND) {
978                found = true;
979                pAsset = openAssetFromFileLocked(path, mode);
980                if (pAsset == NULL) {
981                    /* try again, this time with ".gz" */
982                    path.append(".gz");
983                    pAsset = openAssetFromFileLocked(path, mode);
984                }
985            }
986
987            if (pAsset != NULL)
988                pAsset->setAssetSource(path);
989
990            /*
991             * Don't continue the search into the Zip files.  Our cached info
992             * said it was a file on disk; to be consistent with openDir()
993             * we want to return the loose asset.  If the cached file gets
994             * removed, we fail.
995             *
996             * The alternative is to update our cache when files get deleted,
997             * or make some sort of "best effort" promise, but for now I'm
998             * taking the hard line.
999             */
1000            if (found) {
1001                if (pAsset == NULL)
1002                    ALOGD("Expected file not found: '%s'\n", path.string());
1003                return pAsset;
1004            }
1005        }
1006    }
1007
1008    /*
1009     * Either it wasn't found on disk or on the cached view of the disk.
1010     * Dig through the currently-opened set of Zip files.  If caching
1011     * is disabled, the Zip file may get reopened.
1012     */
1013    if (pAsset == NULL && ap.type == kFileTypeRegular) {
1014        String8 path;
1015
1016        path.appendPath((locale != NULL) ? locale : kDefaultLocale);
1017        path.appendPath((vendor != NULL) ? vendor : kDefaultVendor);
1018        path.appendPath(fileName);
1019
1020        /* check the appropriate Zip file */
1021        ZipFileRO* pZip = getZipFileLocked(ap);
1022        if (pZip != NULL) {
1023            //printf("GOT zip, checking '%s'\n", (const char*) path);
1024            ZipEntryRO entry = pZip->findEntryByName(path.string());
1025            if (entry != NULL) {
1026                //printf("FOUND in Zip file for %s/%s-%s\n",
1027                //    appName, locale, vendor);
1028                pAsset = openAssetFromZipLocked(pZip, entry, mode, path);
1029                pZip->releaseEntry(entry);
1030            }
1031        }
1032
1033        if (pAsset != NULL) {
1034            /* create a "source" name, for debug/display */
1035            pAsset->setAssetSource(createZipSourceNameLocked(ZipSet::getPathName(ap.path.string()),
1036                                                             String8(""), String8(fileName)));
1037        }
1038    }
1039
1040    return pAsset;
1041}
1042
1043/*
1044 * Create a "source name" for a file from a Zip archive.
1045 */
1046String8 AssetManager::createZipSourceNameLocked(const String8& zipFileName,
1047    const String8& dirName, const String8& fileName)
1048{
1049    String8 sourceName("zip:");
1050    sourceName.append(zipFileName);
1051    sourceName.append(":");
1052    if (dirName.length() > 0) {
1053        sourceName.appendPath(dirName);
1054    }
1055    sourceName.appendPath(fileName);
1056    return sourceName;
1057}
1058
1059/*
1060 * Create a path to a loose asset (asset-base/app/locale/vendor).
1061 */
1062String8 AssetManager::createPathNameLocked(const asset_path& ap, const char* locale,
1063    const char* vendor)
1064{
1065    String8 path(ap.path);
1066    path.appendPath((locale != NULL) ? locale : kDefaultLocale);
1067    path.appendPath((vendor != NULL) ? vendor : kDefaultVendor);
1068    return path;
1069}
1070
1071/*
1072 * Create a path to a loose asset (asset-base/app/rootDir).
1073 */
1074String8 AssetManager::createPathNameLocked(const asset_path& ap, const char* rootDir)
1075{
1076    String8 path(ap.path);
1077    if (rootDir != NULL) path.appendPath(rootDir);
1078    return path;
1079}
1080
1081/*
1082 * Return a pointer to one of our open Zip archives.  Returns NULL if no
1083 * matching Zip file exists.
1084 *
1085 * Right now we have 2 possible Zip files (1 each in app/"common").
1086 *
1087 * If caching is set to CACHE_OFF, to get the expected behavior we
1088 * need to reopen the Zip file on every request.  That would be silly
1089 * and expensive, so instead we just check the file modification date.
1090 *
1091 * Pass in NULL values for "appName", "locale", and "vendor" if the
1092 * generics should be used.
1093 */
1094ZipFileRO* AssetManager::getZipFileLocked(const asset_path& ap)
1095{
1096    ALOGV("getZipFileLocked() in %p\n", this);
1097
1098    return mZipSet.getZip(ap.path);
1099}
1100
1101/*
1102 * Try to open an asset from a file on disk.
1103 *
1104 * If the file is compressed with gzip, we seek to the start of the
1105 * deflated data and pass that in (just like we would for a Zip archive).
1106 *
1107 * For uncompressed data, we may already have an mmap()ed version sitting
1108 * around.  If so, we want to hand that to the Asset instead.
1109 *
1110 * This returns NULL if the file doesn't exist, couldn't be opened, or
1111 * claims to be a ".gz" but isn't.
1112 */
1113Asset* AssetManager::openAssetFromFileLocked(const String8& pathName,
1114    AccessMode mode)
1115{
1116    Asset* pAsset = NULL;
1117
1118    if (strcasecmp(pathName.getPathExtension().string(), ".gz") == 0) {
1119        //printf("TRYING '%s'\n", (const char*) pathName);
1120        pAsset = Asset::createFromCompressedFile(pathName.string(), mode);
1121    } else {
1122        //printf("TRYING '%s'\n", (const char*) pathName);
1123        pAsset = Asset::createFromFile(pathName.string(), mode);
1124    }
1125
1126    return pAsset;
1127}
1128
1129/*
1130 * Given an entry in a Zip archive, create a new Asset object.
1131 *
1132 * If the entry is uncompressed, we may want to create or share a
1133 * slice of shared memory.
1134 */
1135Asset* AssetManager::openAssetFromZipLocked(const ZipFileRO* pZipFile,
1136    const ZipEntryRO entry, AccessMode mode, const String8& entryName)
1137{
1138    Asset* pAsset = NULL;
1139
1140    // TODO: look for previously-created shared memory slice?
1141    uint16_t method;
1142    uint32_t uncompressedLen;
1143
1144    //printf("USING Zip '%s'\n", pEntry->getFileName());
1145
1146    if (!pZipFile->getEntryInfo(entry, &method, &uncompressedLen, NULL, NULL,
1147            NULL, NULL))
1148    {
1149        ALOGW("getEntryInfo failed\n");
1150        return NULL;
1151    }
1152
1153    FileMap* dataMap = pZipFile->createEntryFileMap(entry);
1154    if (dataMap == NULL) {
1155        ALOGW("create map from entry failed\n");
1156        return NULL;
1157    }
1158
1159    if (method == ZipFileRO::kCompressStored) {
1160        pAsset = Asset::createFromUncompressedMap(dataMap, mode);
1161        ALOGV("Opened uncompressed entry %s in zip %s mode %d: %p", entryName.string(),
1162                dataMap->getFileName(), mode, pAsset);
1163    } else {
1164        pAsset = Asset::createFromCompressedMap(dataMap,
1165            static_cast<size_t>(uncompressedLen), mode);
1166        ALOGV("Opened compressed entry %s in zip %s mode %d: %p", entryName.string(),
1167                dataMap->getFileName(), mode, pAsset);
1168    }
1169    if (pAsset == NULL) {
1170        /* unexpected */
1171        ALOGW("create from segment failed\n");
1172    }
1173
1174    return pAsset;
1175}
1176
1177
1178
1179/*
1180 * Open a directory in the asset namespace.
1181 *
1182 * An "asset directory" is simply the combination of all files in all
1183 * locations, with ".gz" stripped for loose files.  With app, locale, and
1184 * vendor defined, we have 8 directories and 2 Zip archives to scan.
1185 *
1186 * Pass in "" for the root dir.
1187 */
1188AssetDir* AssetManager::openDir(const char* dirName)
1189{
1190    AutoMutex _l(mLock);
1191
1192    AssetDir* pDir = NULL;
1193    SortedVector<AssetDir::FileInfo>* pMergedInfo = NULL;
1194
1195    LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager");
1196    assert(dirName != NULL);
1197
1198    //printf("+++ openDir(%s) in '%s'\n", dirName, (const char*) mAssetBase);
1199
1200    if (mCacheMode != CACHE_OFF && !mCacheValid)
1201        loadFileNameCacheLocked();
1202
1203    pDir = new AssetDir;
1204
1205    /*
1206     * Scan the various directories, merging what we find into a single
1207     * vector.  We want to scan them in reverse priority order so that
1208     * the ".EXCLUDE" processing works correctly.  Also, if we decide we
1209     * want to remember where the file is coming from, we'll get the right
1210     * version.
1211     *
1212     * We start with Zip archives, then do loose files.
1213     */
1214    pMergedInfo = new SortedVector<AssetDir::FileInfo>;
1215
1216    size_t i = mAssetPaths.size();
1217    while (i > 0) {
1218        i--;
1219        const asset_path& ap = mAssetPaths.itemAt(i);
1220        if (ap.type == kFileTypeRegular) {
1221            ALOGV("Adding directory %s from zip %s", dirName, ap.path.string());
1222            scanAndMergeZipLocked(pMergedInfo, ap, kAssetsRoot, dirName);
1223        } else {
1224            ALOGV("Adding directory %s from dir %s", dirName, ap.path.string());
1225            scanAndMergeDirLocked(pMergedInfo, ap, kAssetsRoot, dirName);
1226        }
1227    }
1228
1229#if 0
1230    printf("FILE LIST:\n");
1231    for (i = 0; i < (size_t) pMergedInfo->size(); i++) {
1232        printf(" %d: (%d) '%s'\n", i,
1233            pMergedInfo->itemAt(i).getFileType(),
1234            (const char*) pMergedInfo->itemAt(i).getFileName());
1235    }
1236#endif
1237
1238    pDir->setFileList(pMergedInfo);
1239    return pDir;
1240}
1241
1242/*
1243 * Open a directory in the non-asset namespace.
1244 *
1245 * An "asset directory" is simply the combination of all files in all
1246 * locations, with ".gz" stripped for loose files.  With app, locale, and
1247 * vendor defined, we have 8 directories and 2 Zip archives to scan.
1248 *
1249 * Pass in "" for the root dir.
1250 */
1251AssetDir* AssetManager::openNonAssetDir(const int32_t cookie, const char* dirName)
1252{
1253    AutoMutex _l(mLock);
1254
1255    AssetDir* pDir = NULL;
1256    SortedVector<AssetDir::FileInfo>* pMergedInfo = NULL;
1257
1258    LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager");
1259    assert(dirName != NULL);
1260
1261    //printf("+++ openDir(%s) in '%s'\n", dirName, (const char*) mAssetBase);
1262
1263    if (mCacheMode != CACHE_OFF && !mCacheValid)
1264        loadFileNameCacheLocked();
1265
1266    pDir = new AssetDir;
1267
1268    pMergedInfo = new SortedVector<AssetDir::FileInfo>;
1269
1270    const size_t which = static_cast<size_t>(cookie) - 1;
1271
1272    if (which < mAssetPaths.size()) {
1273        const asset_path& ap = mAssetPaths.itemAt(which);
1274        if (ap.type == kFileTypeRegular) {
1275            ALOGV("Adding directory %s from zip %s", dirName, ap.path.string());
1276            scanAndMergeZipLocked(pMergedInfo, ap, NULL, dirName);
1277        } else {
1278            ALOGV("Adding directory %s from dir %s", dirName, ap.path.string());
1279            scanAndMergeDirLocked(pMergedInfo, ap, NULL, dirName);
1280        }
1281    }
1282
1283#if 0
1284    printf("FILE LIST:\n");
1285    for (i = 0; i < (size_t) pMergedInfo->size(); i++) {
1286        printf(" %d: (%d) '%s'\n", i,
1287            pMergedInfo->itemAt(i).getFileType(),
1288            (const char*) pMergedInfo->itemAt(i).getFileName());
1289    }
1290#endif
1291
1292    pDir->setFileList(pMergedInfo);
1293    return pDir;
1294}
1295
1296/*
1297 * Scan the contents of the specified directory and merge them into the
1298 * "pMergedInfo" vector, removing previous entries if we find "exclude"
1299 * directives.
1300 *
1301 * Returns "false" if we found nothing to contribute.
1302 */
1303bool AssetManager::scanAndMergeDirLocked(SortedVector<AssetDir::FileInfo>* pMergedInfo,
1304    const asset_path& ap, const char* rootDir, const char* dirName)
1305{
1306    SortedVector<AssetDir::FileInfo>* pContents;
1307    String8 path;
1308
1309    assert(pMergedInfo != NULL);
1310
1311    //printf("scanAndMergeDir: %s %s %s %s\n", appName, locale, vendor,dirName);
1312
1313    if (mCacheValid) {
1314        int i, start, count;
1315
1316        pContents = new SortedVector<AssetDir::FileInfo>;
1317
1318        /*
1319         * Get the basic partial path and find it in the cache.  That's
1320         * the start point for the search.
1321         */
1322        path = createPathNameLocked(ap, rootDir);
1323        if (dirName[0] != '\0')
1324            path.appendPath(dirName);
1325
1326        start = mCache.indexOf(path);
1327        if (start == NAME_NOT_FOUND) {
1328            //printf("+++ not found in cache: dir '%s'\n", (const char*) path);
1329            delete pContents;
1330            return false;
1331        }
1332
1333        /*
1334         * The match string looks like "common/default/default/foo/bar/".
1335         * The '/' on the end ensures that we don't match on the directory
1336         * itself or on ".../foo/barfy/".
1337         */
1338        path.append("/");
1339
1340        count = mCache.size();
1341
1342        /*
1343         * Pick out the stuff in the current dir by examining the pathname.
1344         * It needs to match the partial pathname prefix, and not have a '/'
1345         * (fssep) anywhere after the prefix.
1346         */
1347        for (i = start+1; i < count; i++) {
1348            if (mCache[i].getFileName().length() > path.length() &&
1349                strncmp(mCache[i].getFileName().string(), path.string(), path.length()) == 0)
1350            {
1351                const char* name = mCache[i].getFileName().string();
1352                // XXX THIS IS BROKEN!  Looks like we need to store the full
1353                // path prefix separately from the file path.
1354                if (strchr(name + path.length(), '/') == NULL) {
1355                    /* grab it, reducing path to just the filename component */
1356                    AssetDir::FileInfo tmp = mCache[i];
1357                    tmp.setFileName(tmp.getFileName().getPathLeaf());
1358                    pContents->add(tmp);
1359                }
1360            } else {
1361                /* no longer in the dir or its subdirs */
1362                break;
1363            }
1364
1365        }
1366    } else {
1367        path = createPathNameLocked(ap, rootDir);
1368        if (dirName[0] != '\0')
1369            path.appendPath(dirName);
1370        pContents = scanDirLocked(path);
1371        if (pContents == NULL)
1372            return false;
1373    }
1374
1375    // if we wanted to do an incremental cache fill, we would do it here
1376
1377    /*
1378     * Process "exclude" directives.  If we find a filename that ends with
1379     * ".EXCLUDE", we look for a matching entry in the "merged" set, and
1380     * remove it if we find it.  We also delete the "exclude" entry.
1381     */
1382    int i, count, exclExtLen;
1383
1384    count = pContents->size();
1385    exclExtLen = strlen(kExcludeExtension);
1386    for (i = 0; i < count; i++) {
1387        const char* name;
1388        int nameLen;
1389
1390        name = pContents->itemAt(i).getFileName().string();
1391        nameLen = strlen(name);
1392        if (nameLen > exclExtLen &&
1393            strcmp(name + (nameLen - exclExtLen), kExcludeExtension) == 0)
1394        {
1395            String8 match(name, nameLen - exclExtLen);
1396            int matchIdx;
1397
1398            matchIdx = AssetDir::FileInfo::findEntry(pMergedInfo, match);
1399            if (matchIdx > 0) {
1400                ALOGV("Excluding '%s' [%s]\n",
1401                    pMergedInfo->itemAt(matchIdx).getFileName().string(),
1402                    pMergedInfo->itemAt(matchIdx).getSourceName().string());
1403                pMergedInfo->removeAt(matchIdx);
1404            } else {
1405                //printf("+++ no match on '%s'\n", (const char*) match);
1406            }
1407
1408            ALOGD("HEY: size=%d removing %d\n", (int)pContents->size(), i);
1409            pContents->removeAt(i);
1410            i--;        // adjust "for" loop
1411            count--;    //  and loop limit
1412        }
1413    }
1414
1415    mergeInfoLocked(pMergedInfo, pContents);
1416
1417    delete pContents;
1418
1419    return true;
1420}
1421
1422/*
1423 * Scan the contents of the specified directory, and stuff what we find
1424 * into a newly-allocated vector.
1425 *
1426 * Files ending in ".gz" will have their extensions removed.
1427 *
1428 * We should probably think about skipping files with "illegal" names,
1429 * e.g. illegal characters (/\:) or excessive length.
1430 *
1431 * Returns NULL if the specified directory doesn't exist.
1432 */
1433SortedVector<AssetDir::FileInfo>* AssetManager::scanDirLocked(const String8& path)
1434{
1435    SortedVector<AssetDir::FileInfo>* pContents = NULL;
1436    DIR* dir;
1437    struct dirent* entry;
1438    FileType fileType;
1439
1440    ALOGV("Scanning dir '%s'\n", path.string());
1441
1442    dir = opendir(path.string());
1443    if (dir == NULL)
1444        return NULL;
1445
1446    pContents = new SortedVector<AssetDir::FileInfo>;
1447
1448    while (1) {
1449        entry = readdir(dir);
1450        if (entry == NULL)
1451            break;
1452
1453        if (strcmp(entry->d_name, ".") == 0 ||
1454            strcmp(entry->d_name, "..") == 0)
1455            continue;
1456
1457#ifdef _DIRENT_HAVE_D_TYPE
1458        if (entry->d_type == DT_REG)
1459            fileType = kFileTypeRegular;
1460        else if (entry->d_type == DT_DIR)
1461            fileType = kFileTypeDirectory;
1462        else
1463            fileType = kFileTypeUnknown;
1464#else
1465        // stat the file
1466        fileType = ::getFileType(path.appendPathCopy(entry->d_name).string());
1467#endif
1468
1469        if (fileType != kFileTypeRegular && fileType != kFileTypeDirectory)
1470            continue;
1471
1472        AssetDir::FileInfo info;
1473        info.set(String8(entry->d_name), fileType);
1474        if (strcasecmp(info.getFileName().getPathExtension().string(), ".gz") == 0)
1475            info.setFileName(info.getFileName().getBasePath());
1476        info.setSourceName(path.appendPathCopy(info.getFileName()));
1477        pContents->add(info);
1478    }
1479
1480    closedir(dir);
1481    return pContents;
1482}
1483
1484/*
1485 * Scan the contents out of the specified Zip archive, and merge what we
1486 * find into "pMergedInfo".  If the Zip archive in question doesn't exist,
1487 * we return immediately.
1488 *
1489 * Returns "false" if we found nothing to contribute.
1490 */
1491bool AssetManager::scanAndMergeZipLocked(SortedVector<AssetDir::FileInfo>* pMergedInfo,
1492    const asset_path& ap, const char* rootDir, const char* baseDirName)
1493{
1494    ZipFileRO* pZip;
1495    Vector<String8> dirs;
1496    AssetDir::FileInfo info;
1497    SortedVector<AssetDir::FileInfo> contents;
1498    String8 sourceName, zipName, dirName;
1499
1500    pZip = mZipSet.getZip(ap.path);
1501    if (pZip == NULL) {
1502        ALOGW("Failure opening zip %s\n", ap.path.string());
1503        return false;
1504    }
1505
1506    zipName = ZipSet::getPathName(ap.path.string());
1507
1508    /* convert "sounds" to "rootDir/sounds" */
1509    if (rootDir != NULL) dirName = rootDir;
1510    dirName.appendPath(baseDirName);
1511
1512    /*
1513     * Scan through the list of files, looking for a match.  The files in
1514     * the Zip table of contents are not in sorted order, so we have to
1515     * process the entire list.  We're looking for a string that begins
1516     * with the characters in "dirName", is followed by a '/', and has no
1517     * subsequent '/' in the stuff that follows.
1518     *
1519     * What makes this especially fun is that directories are not stored
1520     * explicitly in Zip archives, so we have to infer them from context.
1521     * When we see "sounds/foo.wav" we have to leave a note to ourselves
1522     * to insert a directory called "sounds" into the list.  We store
1523     * these in temporary vector so that we only return each one once.
1524     *
1525     * Name comparisons are case-sensitive to match UNIX filesystem
1526     * semantics.
1527     */
1528    int dirNameLen = dirName.length();
1529    void *iterationCookie;
1530    if (!pZip->startIteration(&iterationCookie, dirName.string(), NULL)) {
1531        ALOGW("ZipFileRO::startIteration returned false");
1532        return false;
1533    }
1534
1535    ZipEntryRO entry;
1536    while ((entry = pZip->nextEntry(iterationCookie)) != NULL) {
1537        char nameBuf[256];
1538
1539        if (pZip->getEntryFileName(entry, nameBuf, sizeof(nameBuf)) != 0) {
1540            // TODO: fix this if we expect to have long names
1541            ALOGE("ARGH: name too long?\n");
1542            continue;
1543        }
1544        //printf("Comparing %s in %s?\n", nameBuf, dirName.string());
1545        if (dirNameLen == 0 || nameBuf[dirNameLen] == '/')
1546        {
1547            const char* cp;
1548            const char* nextSlash;
1549
1550            cp = nameBuf + dirNameLen;
1551            if (dirNameLen != 0)
1552                cp++;       // advance past the '/'
1553
1554            nextSlash = strchr(cp, '/');
1555//xxx this may break if there are bare directory entries
1556            if (nextSlash == NULL) {
1557                /* this is a file in the requested directory */
1558
1559                info.set(String8(nameBuf).getPathLeaf(), kFileTypeRegular);
1560
1561                info.setSourceName(
1562                    createZipSourceNameLocked(zipName, dirName, info.getFileName()));
1563
1564                contents.add(info);
1565                //printf("FOUND: file '%s'\n", info.getFileName().string());
1566            } else {
1567                /* this is a subdir; add it if we don't already have it*/
1568                String8 subdirName(cp, nextSlash - cp);
1569                size_t j;
1570                size_t N = dirs.size();
1571
1572                for (j = 0; j < N; j++) {
1573                    if (subdirName == dirs[j]) {
1574                        break;
1575                    }
1576                }
1577                if (j == N) {
1578                    dirs.add(subdirName);
1579                }
1580
1581                //printf("FOUND: dir '%s'\n", subdirName.string());
1582            }
1583        }
1584    }
1585
1586    pZip->endIteration(iterationCookie);
1587
1588    /*
1589     * Add the set of unique directories.
1590     */
1591    for (int i = 0; i < (int) dirs.size(); i++) {
1592        info.set(dirs[i], kFileTypeDirectory);
1593        info.setSourceName(
1594            createZipSourceNameLocked(zipName, dirName, info.getFileName()));
1595        contents.add(info);
1596    }
1597
1598    mergeInfoLocked(pMergedInfo, &contents);
1599
1600    return true;
1601}
1602
1603
1604/*
1605 * Merge two vectors of FileInfo.
1606 *
1607 * The merged contents will be stuffed into *pMergedInfo.
1608 *
1609 * If an entry for a file exists in both "pMergedInfo" and "pContents",
1610 * we use the newer "pContents" entry.
1611 */
1612void AssetManager::mergeInfoLocked(SortedVector<AssetDir::FileInfo>* pMergedInfo,
1613    const SortedVector<AssetDir::FileInfo>* pContents)
1614{
1615    /*
1616     * Merge what we found in this directory with what we found in
1617     * other places.
1618     *
1619     * Two basic approaches:
1620     * (1) Create a new array that holds the unique values of the two
1621     *     arrays.
1622     * (2) Take the elements from pContents and shove them into pMergedInfo.
1623     *
1624     * Because these are vectors of complex objects, moving elements around
1625     * inside the vector requires constructing new objects and allocating
1626     * storage for members.  With approach #1, we're always adding to the
1627     * end, whereas with #2 we could be inserting multiple elements at the
1628     * front of the vector.  Approach #1 requires a full copy of the
1629     * contents of pMergedInfo, but approach #2 requires the same copy for
1630     * every insertion at the front of pMergedInfo.
1631     *
1632     * (We should probably use a SortedVector interface that allows us to
1633     * just stuff items in, trusting us to maintain the sort order.)
1634     */
1635    SortedVector<AssetDir::FileInfo>* pNewSorted;
1636    int mergeMax, contMax;
1637    int mergeIdx, contIdx;
1638
1639    pNewSorted = new SortedVector<AssetDir::FileInfo>;
1640    mergeMax = pMergedInfo->size();
1641    contMax = pContents->size();
1642    mergeIdx = contIdx = 0;
1643
1644    while (mergeIdx < mergeMax || contIdx < contMax) {
1645        if (mergeIdx == mergeMax) {
1646            /* hit end of "merge" list, copy rest of "contents" */
1647            pNewSorted->add(pContents->itemAt(contIdx));
1648            contIdx++;
1649        } else if (contIdx == contMax) {
1650            /* hit end of "cont" list, copy rest of "merge" */
1651            pNewSorted->add(pMergedInfo->itemAt(mergeIdx));
1652            mergeIdx++;
1653        } else if (pMergedInfo->itemAt(mergeIdx) == pContents->itemAt(contIdx))
1654        {
1655            /* items are identical, add newer and advance both indices */
1656            pNewSorted->add(pContents->itemAt(contIdx));
1657            mergeIdx++;
1658            contIdx++;
1659        } else if (pMergedInfo->itemAt(mergeIdx) < pContents->itemAt(contIdx))
1660        {
1661            /* "merge" is lower, add that one */
1662            pNewSorted->add(pMergedInfo->itemAt(mergeIdx));
1663            mergeIdx++;
1664        } else {
1665            /* "cont" is lower, add that one */
1666            assert(pContents->itemAt(contIdx) < pMergedInfo->itemAt(mergeIdx));
1667            pNewSorted->add(pContents->itemAt(contIdx));
1668            contIdx++;
1669        }
1670    }
1671
1672    /*
1673     * Overwrite the "merged" list with the new stuff.
1674     */
1675    *pMergedInfo = *pNewSorted;
1676    delete pNewSorted;
1677
1678#if 0       // for Vector, rather than SortedVector
1679    int i, j;
1680    for (i = pContents->size() -1; i >= 0; i--) {
1681        bool add = true;
1682
1683        for (j = pMergedInfo->size() -1; j >= 0; j--) {
1684            /* case-sensitive comparisons, to behave like UNIX fs */
1685            if (strcmp(pContents->itemAt(i).mFileName,
1686                       pMergedInfo->itemAt(j).mFileName) == 0)
1687            {
1688                /* match, don't add this entry */
1689                add = false;
1690                break;
1691            }
1692        }
1693
1694        if (add)
1695            pMergedInfo->add(pContents->itemAt(i));
1696    }
1697#endif
1698}
1699
1700
1701/*
1702 * Load all files into the file name cache.  We want to do this across
1703 * all combinations of { appname, locale, vendor }, performing a recursive
1704 * directory traversal.
1705 *
1706 * This is not the most efficient data structure.  Also, gathering the
1707 * information as we needed it (file-by-file or directory-by-directory)
1708 * would be faster.  However, on the actual device, 99% of the files will
1709 * live in Zip archives, so this list will be very small.  The trouble
1710 * is that we have to check the "loose" files first, so it's important
1711 * that we don't beat the filesystem silly looking for files that aren't
1712 * there.
1713 *
1714 * Note on thread safety: this is the only function that causes updates
1715 * to mCache, and anybody who tries to use it will call here if !mCacheValid,
1716 * so we need to employ a mutex here.
1717 */
1718void AssetManager::loadFileNameCacheLocked(void)
1719{
1720    assert(!mCacheValid);
1721    assert(mCache.size() == 0);
1722
1723#ifdef DO_TIMINGS   // need to link against -lrt for this now
1724    DurationTimer timer;
1725    timer.start();
1726#endif
1727
1728    fncScanLocked(&mCache, "");
1729
1730#ifdef DO_TIMINGS
1731    timer.stop();
1732    ALOGD("Cache scan took %.3fms\n",
1733        timer.durationUsecs() / 1000.0);
1734#endif
1735
1736#if 0
1737    int i;
1738    printf("CACHED FILE LIST (%d entries):\n", mCache.size());
1739    for (i = 0; i < (int) mCache.size(); i++) {
1740        printf(" %d: (%d) '%s'\n", i,
1741            mCache.itemAt(i).getFileType(),
1742            (const char*) mCache.itemAt(i).getFileName());
1743    }
1744#endif
1745
1746    mCacheValid = true;
1747}
1748
1749/*
1750 * Scan up to 8 versions of the specified directory.
1751 */
1752void AssetManager::fncScanLocked(SortedVector<AssetDir::FileInfo>* pMergedInfo,
1753    const char* dirName)
1754{
1755    size_t i = mAssetPaths.size();
1756    while (i > 0) {
1757        i--;
1758        const asset_path& ap = mAssetPaths.itemAt(i);
1759        fncScanAndMergeDirLocked(pMergedInfo, ap, NULL, NULL, dirName);
1760        if (mLocale != NULL)
1761            fncScanAndMergeDirLocked(pMergedInfo, ap, mLocale, NULL, dirName);
1762        if (mVendor != NULL)
1763            fncScanAndMergeDirLocked(pMergedInfo, ap, NULL, mVendor, dirName);
1764        if (mLocale != NULL && mVendor != NULL)
1765            fncScanAndMergeDirLocked(pMergedInfo, ap, mLocale, mVendor, dirName);
1766    }
1767}
1768
1769/*
1770 * Recursively scan this directory and all subdirs.
1771 *
1772 * This is similar to scanAndMergeDir, but we don't remove the .EXCLUDE
1773 * files, and we prepend the extended partial path to the filenames.
1774 */
1775bool AssetManager::fncScanAndMergeDirLocked(
1776    SortedVector<AssetDir::FileInfo>* pMergedInfo,
1777    const asset_path& ap, const char* locale, const char* vendor,
1778    const char* dirName)
1779{
1780    SortedVector<AssetDir::FileInfo>* pContents;
1781    String8 partialPath;
1782    String8 fullPath;
1783
1784    // XXX This is broken -- the filename cache needs to hold the base
1785    // asset path separately from its filename.
1786
1787    partialPath = createPathNameLocked(ap, locale, vendor);
1788    if (dirName[0] != '\0') {
1789        partialPath.appendPath(dirName);
1790    }
1791
1792    fullPath = partialPath;
1793    pContents = scanDirLocked(fullPath);
1794    if (pContents == NULL) {
1795        return false;       // directory did not exist
1796    }
1797
1798    /*
1799     * Scan all subdirectories of the current dir, merging what we find
1800     * into "pMergedInfo".
1801     */
1802    for (int i = 0; i < (int) pContents->size(); i++) {
1803        if (pContents->itemAt(i).getFileType() == kFileTypeDirectory) {
1804            String8 subdir(dirName);
1805            subdir.appendPath(pContents->itemAt(i).getFileName());
1806
1807            fncScanAndMergeDirLocked(pMergedInfo, ap, locale, vendor, subdir.string());
1808        }
1809    }
1810
1811    /*
1812     * To be consistent, we want entries for the root directory.  If
1813     * we're the root, add one now.
1814     */
1815    if (dirName[0] == '\0') {
1816        AssetDir::FileInfo tmpInfo;
1817
1818        tmpInfo.set(String8(""), kFileTypeDirectory);
1819        tmpInfo.setSourceName(createPathNameLocked(ap, locale, vendor));
1820        pContents->add(tmpInfo);
1821    }
1822
1823    /*
1824     * We want to prepend the extended partial path to every entry in
1825     * "pContents".  It's the same value for each entry, so this will
1826     * not change the sorting order of the vector contents.
1827     */
1828    for (int i = 0; i < (int) pContents->size(); i++) {
1829        const AssetDir::FileInfo& info = pContents->itemAt(i);
1830        pContents->editItemAt(i).setFileName(partialPath.appendPathCopy(info.getFileName()));
1831    }
1832
1833    mergeInfoLocked(pMergedInfo, pContents);
1834    delete pContents;
1835    return true;
1836}
1837
1838/*
1839 * Trash the cache.
1840 */
1841void AssetManager::purgeFileNameCacheLocked(void)
1842{
1843    mCacheValid = false;
1844    mCache.clear();
1845}
1846
1847/*
1848 * ===========================================================================
1849 *      AssetManager::SharedZip
1850 * ===========================================================================
1851 */
1852
1853
1854Mutex AssetManager::SharedZip::gLock;
1855DefaultKeyedVector<String8, wp<AssetManager::SharedZip> > AssetManager::SharedZip::gOpen;
1856
1857AssetManager::SharedZip::SharedZip(const String8& path, time_t modWhen)
1858    : mPath(path), mZipFile(NULL), mModWhen(modWhen),
1859      mResourceTableAsset(NULL), mResourceTable(NULL)
1860{
1861    if (kIsDebug) {
1862        ALOGI("Creating SharedZip %p %s\n", this, (const char*)mPath);
1863    }
1864    ALOGV("+++ opening zip '%s'\n", mPath.string());
1865    mZipFile = ZipFileRO::open(mPath.string());
1866    if (mZipFile == NULL) {
1867        ALOGD("failed to open Zip archive '%s'\n", mPath.string());
1868    }
1869}
1870
1871sp<AssetManager::SharedZip> AssetManager::SharedZip::get(const String8& path,
1872        bool createIfNotPresent)
1873{
1874    AutoMutex _l(gLock);
1875    time_t modWhen = getFileModDate(path);
1876    sp<SharedZip> zip = gOpen.valueFor(path).promote();
1877    if (zip != NULL && zip->mModWhen == modWhen) {
1878        return zip;
1879    }
1880    if (zip == NULL && !createIfNotPresent) {
1881        return NULL;
1882    }
1883    zip = new SharedZip(path, modWhen);
1884    gOpen.add(path, zip);
1885    return zip;
1886
1887}
1888
1889ZipFileRO* AssetManager::SharedZip::getZip()
1890{
1891    return mZipFile;
1892}
1893
1894Asset* AssetManager::SharedZip::getResourceTableAsset()
1895{
1896    AutoMutex _l(gLock);
1897    ALOGV("Getting from SharedZip %p resource asset %p\n", this, mResourceTableAsset);
1898    return mResourceTableAsset;
1899}
1900
1901Asset* AssetManager::SharedZip::setResourceTableAsset(Asset* asset)
1902{
1903    {
1904        AutoMutex _l(gLock);
1905        if (mResourceTableAsset == NULL) {
1906            // This is not thread safe the first time it is called, so
1907            // do it here with the global lock held.
1908            asset->getBuffer(true);
1909            mResourceTableAsset = asset;
1910            return asset;
1911        }
1912    }
1913    delete asset;
1914    return mResourceTableAsset;
1915}
1916
1917ResTable* AssetManager::SharedZip::getResourceTable()
1918{
1919    ALOGV("Getting from SharedZip %p resource table %p\n", this, mResourceTable);
1920    return mResourceTable;
1921}
1922
1923ResTable* AssetManager::SharedZip::setResourceTable(ResTable* res)
1924{
1925    {
1926        AutoMutex _l(gLock);
1927        if (mResourceTable == NULL) {
1928            mResourceTable = res;
1929            return res;
1930        }
1931    }
1932    delete res;
1933    return mResourceTable;
1934}
1935
1936bool AssetManager::SharedZip::isUpToDate()
1937{
1938    time_t modWhen = getFileModDate(mPath.string());
1939    return mModWhen == modWhen;
1940}
1941
1942void AssetManager::SharedZip::addOverlay(const asset_path& ap)
1943{
1944    mOverlays.add(ap);
1945}
1946
1947bool AssetManager::SharedZip::getOverlay(size_t idx, asset_path* out) const
1948{
1949    if (idx >= mOverlays.size()) {
1950        return false;
1951    }
1952    *out = mOverlays[idx];
1953    return true;
1954}
1955
1956AssetManager::SharedZip::~SharedZip()
1957{
1958    if (kIsDebug) {
1959        ALOGI("Destroying SharedZip %p %s\n", this, (const char*)mPath);
1960    }
1961    if (mResourceTable != NULL) {
1962        delete mResourceTable;
1963    }
1964    if (mResourceTableAsset != NULL) {
1965        delete mResourceTableAsset;
1966    }
1967    if (mZipFile != NULL) {
1968        delete mZipFile;
1969        ALOGV("Closed '%s'\n", mPath.string());
1970    }
1971}
1972
1973/*
1974 * ===========================================================================
1975 *      AssetManager::ZipSet
1976 * ===========================================================================
1977 */
1978
1979/*
1980 * Constructor.
1981 */
1982AssetManager::ZipSet::ZipSet(void)
1983{
1984}
1985
1986/*
1987 * Destructor.  Close any open archives.
1988 */
1989AssetManager::ZipSet::~ZipSet(void)
1990{
1991    size_t N = mZipFile.size();
1992    for (size_t i = 0; i < N; i++)
1993        closeZip(i);
1994}
1995
1996/*
1997 * Close a Zip file and reset the entry.
1998 */
1999void AssetManager::ZipSet::closeZip(int idx)
2000{
2001    mZipFile.editItemAt(idx) = NULL;
2002}
2003
2004
2005/*
2006 * Retrieve the appropriate Zip file from the set.
2007 */
2008ZipFileRO* AssetManager::ZipSet::getZip(const String8& path)
2009{
2010    int idx = getIndex(path);
2011    sp<SharedZip> zip = mZipFile[idx];
2012    if (zip == NULL) {
2013        zip = SharedZip::get(path);
2014        mZipFile.editItemAt(idx) = zip;
2015    }
2016    return zip->getZip();
2017}
2018
2019Asset* AssetManager::ZipSet::getZipResourceTableAsset(const String8& path)
2020{
2021    int idx = getIndex(path);
2022    sp<SharedZip> zip = mZipFile[idx];
2023    if (zip == NULL) {
2024        zip = SharedZip::get(path);
2025        mZipFile.editItemAt(idx) = zip;
2026    }
2027    return zip->getResourceTableAsset();
2028}
2029
2030Asset* AssetManager::ZipSet::setZipResourceTableAsset(const String8& path,
2031                                                 Asset* asset)
2032{
2033    int idx = getIndex(path);
2034    sp<SharedZip> zip = mZipFile[idx];
2035    // doesn't make sense to call before previously accessing.
2036    return zip->setResourceTableAsset(asset);
2037}
2038
2039ResTable* AssetManager::ZipSet::getZipResourceTable(const String8& path)
2040{
2041    int idx = getIndex(path);
2042    sp<SharedZip> zip = mZipFile[idx];
2043    if (zip == NULL) {
2044        zip = SharedZip::get(path);
2045        mZipFile.editItemAt(idx) = zip;
2046    }
2047    return zip->getResourceTable();
2048}
2049
2050ResTable* AssetManager::ZipSet::setZipResourceTable(const String8& path,
2051                                                    ResTable* res)
2052{
2053    int idx = getIndex(path);
2054    sp<SharedZip> zip = mZipFile[idx];
2055    // doesn't make sense to call before previously accessing.
2056    return zip->setResourceTable(res);
2057}
2058
2059/*
2060 * Generate the partial pathname for the specified archive.  The caller
2061 * gets to prepend the asset root directory.
2062 *
2063 * Returns something like "common/en-US-noogle.jar".
2064 */
2065/*static*/ String8 AssetManager::ZipSet::getPathName(const char* zipPath)
2066{
2067    return String8(zipPath);
2068}
2069
2070bool AssetManager::ZipSet::isUpToDate()
2071{
2072    const size_t N = mZipFile.size();
2073    for (size_t i=0; i<N; i++) {
2074        if (mZipFile[i] != NULL && !mZipFile[i]->isUpToDate()) {
2075            return false;
2076        }
2077    }
2078    return true;
2079}
2080
2081void AssetManager::ZipSet::addOverlay(const String8& path, const asset_path& overlay)
2082{
2083    int idx = getIndex(path);
2084    sp<SharedZip> zip = mZipFile[idx];
2085    zip->addOverlay(overlay);
2086}
2087
2088bool AssetManager::ZipSet::getOverlay(const String8& path, size_t idx, asset_path* out) const
2089{
2090    sp<SharedZip> zip = SharedZip::get(path, false);
2091    if (zip == NULL) {
2092        return false;
2093    }
2094    return zip->getOverlay(idx, out);
2095}
2096
2097/*
2098 * Compute the zip file's index.
2099 *
2100 * "appName", "locale", and "vendor" should be set to NULL to indicate the
2101 * default directory.
2102 */
2103int AssetManager::ZipSet::getIndex(const String8& zip) const
2104{
2105    const size_t N = mZipPath.size();
2106    for (size_t i=0; i<N; i++) {
2107        if (mZipPath[i] == zip) {
2108            return i;
2109        }
2110    }
2111
2112    mZipPath.add(zip);
2113    mZipFile.add(NULL);
2114
2115    return mZipPath.size()-1;
2116}
2117