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