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