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