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