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