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/PathUtils.h>
30 #include <androidfw/ResourceTypes.h>
31 #include <androidfw/ZipFileRO.h>
32 #include <cutils/atomic.h>
33 #include <utils/Log.h>
34 #include <utils/String8.h>
35 #include <utils/String8.h>
36 #include <utils/threads.h>
37 #include <utils/Timers.h>
38 #include <utils/Trace.h>
39 #ifndef _WIN32
40 #include <sys/file.h>
41 #endif
42 
43 #include <assert.h>
44 #include <dirent.h>
45 #include <errno.h>
46 #include <string.h> // strerror
47 #include <strings.h>
48 
49 #ifndef TEMP_FAILURE_RETRY
50 /* Used to retry syscalls that can return EINTR. */
51 #define TEMP_FAILURE_RETRY(exp) ({         \
52     typeof (exp) _rc;                      \
53     do {                                   \
54         _rc = (exp);                       \
55     } while (_rc == -1 && errno == EINTR); \
56     _rc; })
57 #endif
58 
59 using namespace android;
60 
61 static const bool kIsDebug = false;
62 
63 static const char* kAssetsRoot = "assets";
64 static const char* kAppZipName = NULL; //"classes.jar";
65 static const char* kSystemAssets = "framework/framework-res.apk";
66 static const char* kResourceCache = "resource-cache";
67 
68 static const char* kExcludeExtension = ".EXCLUDE";
69 
70 static Asset* const kExcludedAsset = (Asset*) 0xd000000d;
71 
72 static volatile int32_t gCount = 0;
73 
74 const char* AssetManager::RESOURCES_FILENAME = "resources.arsc";
75 const char* AssetManager::IDMAP_BIN = "/system/bin/idmap";
76 const char* AssetManager::VENDOR_OVERLAY_DIR = "/vendor/overlay";
77 const char* AssetManager::PRODUCT_OVERLAY_DIR = "/product/overlay";
78 const char* AssetManager::SYSTEM_EXT_OVERLAY_DIR = "/system_ext/overlay";
79 const char* AssetManager::ODM_OVERLAY_DIR = "/odm/overlay";
80 const char* AssetManager::OEM_OVERLAY_DIR = "/oem/overlay";
81 const char* AssetManager::OVERLAY_THEME_DIR_PROPERTY = "ro.boot.vendor.overlay.theme";
82 const char* AssetManager::TARGET_PACKAGE_NAME = "android";
83 const char* AssetManager::TARGET_APK_PATH = "/system/framework/framework-res.apk";
84 const char* AssetManager::IDMAP_DIR = "/data/resource-cache";
85 
86 namespace {
87 
idmapPathForPackagePath(const String8 & pkgPath)88 String8 idmapPathForPackagePath(const String8& pkgPath) {
89     const char* root = getenv("ANDROID_DATA");
90     LOG_ALWAYS_FATAL_IF(root == NULL, "ANDROID_DATA not set");
91     String8 path(root);
92     appendPath(path, kResourceCache);
93 
94     char buf[256]; // 256 chars should be enough for anyone...
95     strncpy(buf, pkgPath.c_str(), 255);
96     buf[255] = '\0';
97     char* filename = buf;
98     while (*filename && *filename == '/') {
99         ++filename;
100     }
101     char* p = filename;
102     while (*p) {
103         if (*p == '/') {
104             *p = '@';
105         }
106         ++p;
107     }
108     appendPath(path, filename);
109     path.append("@idmap");
110 
111     return path;
112 }
113 
114 /*
115  * Like strdup(), but uses C++ "new" operator instead of malloc.
116  */
strdupNew(const char * str)117 static char* strdupNew(const char* str) {
118     char* newStr;
119     int len;
120 
121     if (str == NULL)
122         return NULL;
123 
124     len = strlen(str);
125     newStr = new char[len+1];
126     memcpy(newStr, str, len+1);
127 
128     return newStr;
129 }
130 
131 } // namespace
132 
133 /*
134  * ===========================================================================
135  *      AssetManager
136  * ===========================================================================
137  */
138 
getGlobalCount()139 int32_t AssetManager::getGlobalCount() {
140     return gCount;
141 }
142 
AssetManager()143 AssetManager::AssetManager() :
144         mLocale(NULL), mResources(NULL), mConfig(new ResTable_config) {
145     int count = android_atomic_inc(&gCount) + 1;
146     if (kIsDebug) {
147         ALOGI("Creating AssetManager %p #%d\n", this, count);
148     }
149     memset(mConfig, 0, sizeof(ResTable_config));
150 }
151 
~AssetManager()152 AssetManager::~AssetManager() {
153     int count = android_atomic_dec(&gCount);
154     if (kIsDebug) {
155         ALOGI("Destroying AssetManager in %p #%d\n", this, count);
156     } else {
157         ALOGV("Destroying AssetManager in %p #%d\n", this, count);
158     }
159 
160     // Manually close any fd paths for which we have not yet opened their zip (which
161     // will take ownership of the fd and close it when done).
162     for (size_t i=0; i<mAssetPaths.size(); i++) {
163         ALOGV("Cleaning path #%d: fd=%d, zip=%p", (int)i, mAssetPaths[i].rawFd,
164                 mAssetPaths[i].zip.get());
165         if (mAssetPaths[i].rawFd >= 0 && mAssetPaths[i].zip == NULL) {
166             close(mAssetPaths[i].rawFd);
167         }
168     }
169 
170     delete mConfig;
171     delete mResources;
172 
173     // don't have a String class yet, so make sure we clean up
174     delete[] mLocale;
175 }
176 
addAssetPath(const String8 & path,int32_t * cookie,bool appAsLib,bool isSystemAsset)177 bool AssetManager::addAssetPath(
178         const String8& path, int32_t* cookie, bool appAsLib, bool isSystemAsset) {
179     AutoMutex _l(mLock);
180 
181     asset_path ap;
182 
183     String8 realPath(path);
184     if (kAppZipName) {
185         appendPath(realPath, kAppZipName);
186     }
187     ap.type = ::getFileType(realPath.c_str());
188     if (ap.type == kFileTypeRegular) {
189         ap.path = realPath;
190     } else {
191         ap.path = path;
192         ap.type = ::getFileType(path.c_str());
193         if (ap.type != kFileTypeDirectory && ap.type != kFileTypeRegular) {
194             ALOGW("Asset path %s is neither a directory nor file (type=%d).",
195                  path.c_str(), (int)ap.type);
196             return false;
197         }
198     }
199 
200     // Skip if we have it already.
201     for (size_t i=0; i<mAssetPaths.size(); i++) {
202         if (mAssetPaths[i].path == ap.path) {
203             if (cookie) {
204                 *cookie = static_cast<int32_t>(i+1);
205             }
206             return true;
207         }
208     }
209 
210     ALOGV("In %p Asset %s path: %s", this,
211          ap.type == kFileTypeDirectory ? "dir" : "zip", ap.path.c_str());
212 
213     ap.isSystemAsset = isSystemAsset;
214     ssize_t apPos = mAssetPaths.add(ap);
215 
216     // new paths are always added at the end
217     if (cookie) {
218         *cookie = static_cast<int32_t>(mAssetPaths.size());
219     }
220 
221 #ifdef __ANDROID__
222     // Load overlays, if any
223     asset_path oap;
224     for (size_t idx = 0; mZipSet.getOverlay(ap.path, idx, &oap); idx++) {
225         oap.isSystemAsset = isSystemAsset;
226         mAssetPaths.add(oap);
227     }
228 #endif
229 
230     if (mResources != NULL) {
231         appendPathToResTable(mAssetPaths.editItemAt(apPos), appAsLib);
232     }
233 
234     return true;
235 }
236 
addOverlayPath(const String8 & packagePath,int32_t * cookie)237 bool AssetManager::addOverlayPath(const String8& packagePath, int32_t* cookie)
238 {
239     const String8 idmapPath = idmapPathForPackagePath(packagePath);
240 
241     AutoMutex _l(mLock);
242 
243     for (size_t i = 0; i < mAssetPaths.size(); ++i) {
244         if (mAssetPaths[i].idmap == idmapPath) {
245            *cookie = static_cast<int32_t>(i + 1);
246             return true;
247          }
248      }
249 
250     Asset* idmap = NULL;
251     if ((idmap = openAssetFromFileLocked(idmapPath, Asset::ACCESS_BUFFER)) == NULL) {
252         ALOGW("failed to open idmap file %s\n", idmapPath.c_str());
253         return false;
254     }
255 
256     String8 targetPath;
257     String8 overlayPath;
258     if (!ResTable::getIdmapInfo(idmap->getBuffer(false), idmap->getLength(),
259                 NULL, NULL, NULL, &targetPath, &overlayPath)) {
260         ALOGW("failed to read idmap file %s\n", idmapPath.c_str());
261         delete idmap;
262         return false;
263     }
264     delete idmap;
265 
266     if (overlayPath != packagePath) {
267         ALOGW("idmap file %s inconcistent: expected path %s does not match actual path %s\n",
268                 idmapPath.c_str(), packagePath.c_str(), overlayPath.c_str());
269         return false;
270     }
271     if (access(targetPath.c_str(), R_OK) != 0) {
272         ALOGW("failed to access file %s: %s\n", targetPath.c_str(), strerror(errno));
273         return false;
274     }
275     if (access(idmapPath.c_str(), R_OK) != 0) {
276         ALOGW("failed to access file %s: %s\n", idmapPath.c_str(), strerror(errno));
277         return false;
278     }
279     if (access(overlayPath.c_str(), R_OK) != 0) {
280         ALOGW("failed to access file %s: %s\n", overlayPath.c_str(), strerror(errno));
281         return false;
282     }
283 
284     asset_path oap;
285     oap.path = overlayPath;
286     oap.type = ::getFileType(overlayPath.c_str());
287     oap.idmap = idmapPath;
288 #if 0
289     ALOGD("Overlay added: targetPath=%s overlayPath=%s idmapPath=%s\n",
290             targetPath.c_str(), overlayPath.c_str(), idmapPath.c_str());
291 #endif
292     mAssetPaths.add(oap);
293     *cookie = static_cast<int32_t>(mAssetPaths.size());
294 
295     if (mResources != NULL) {
296         appendPathToResTable(oap);
297     }
298 
299     return true;
300 }
301 
addAssetFd(int fd,const String8 & debugPathName,int32_t * cookie,bool appAsLib,bool assume_ownership)302 bool AssetManager::addAssetFd(
303         int fd, const String8& debugPathName, int32_t* cookie, bool appAsLib,
304         bool assume_ownership) {
305     AutoMutex _l(mLock);
306 
307     asset_path ap;
308 
309     ap.path = debugPathName;
310     ap.rawFd = fd;
311     ap.type = kFileTypeRegular;
312     ap.assumeOwnership = assume_ownership;
313 
314     ALOGV("In %p Asset fd %d name: %s", this, fd, ap.path.c_str());
315 
316     ssize_t apPos = mAssetPaths.add(ap);
317 
318     // new paths are always added at the end
319     if (cookie) {
320         *cookie = static_cast<int32_t>(mAssetPaths.size());
321     }
322 
323     if (mResources != NULL) {
324         appendPathToResTable(mAssetPaths.editItemAt(apPos), appAsLib);
325     }
326 
327     return true;
328 }
329 
createIdmap(const char * targetApkPath,const char * overlayApkPath,uint32_t targetCrc,uint32_t overlayCrc,uint32_t ** outData,size_t * outSize)330 bool AssetManager::createIdmap(const char* targetApkPath, const char* overlayApkPath,
331         uint32_t targetCrc, uint32_t overlayCrc, uint32_t** outData, size_t* outSize)
332 {
333     AutoMutex _l(mLock);
334     const String8 paths[2] = { String8(targetApkPath), String8(overlayApkPath) };
335     Asset* assets[2] = {NULL, NULL};
336     bool ret = false;
337     {
338         ResTable tables[2];
339 
340         for (int i = 0; i < 2; ++i) {
341             asset_path ap;
342             ap.type = kFileTypeRegular;
343             ap.path = paths[i];
344             assets[i] = openNonAssetInPathLocked("resources.arsc",
345                     Asset::ACCESS_BUFFER, ap);
346             if (assets[i] == NULL) {
347                 ALOGW("failed to find resources.arsc in %s\n", ap.path.c_str());
348                 goto exit;
349             }
350             if (tables[i].add(assets[i]) != NO_ERROR) {
351                 ALOGW("failed to add %s to resource table", paths[i].c_str());
352                 goto exit;
353             }
354         }
355         ret = tables[1].createIdmap(tables[0], targetCrc, overlayCrc,
356                 targetApkPath, overlayApkPath, (void**)outData, outSize) == NO_ERROR;
357     }
358 
359 exit:
360     delete assets[0];
361     delete assets[1];
362     return ret;
363 }
364 
addDefaultAssets()365 bool AssetManager::addDefaultAssets()
366 {
367     const char* root = getenv("ANDROID_ROOT");
368     LOG_ALWAYS_FATAL_IF(root == NULL, "ANDROID_ROOT not set");
369 
370     String8 path(root);
371     appendPath(path, kSystemAssets);
372 
373     return addAssetPath(path, NULL, false /* appAsLib */, true /* isSystemAsset */);
374 }
375 
nextAssetPath(const int32_t cookie) const376 int32_t AssetManager::nextAssetPath(const int32_t cookie) const
377 {
378     AutoMutex _l(mLock);
379     const size_t next = static_cast<size_t>(cookie) + 1;
380     return next > mAssetPaths.size() ? -1 : next;
381 }
382 
getAssetPath(const int32_t cookie) const383 String8 AssetManager::getAssetPath(const int32_t cookie) const
384 {
385     AutoMutex _l(mLock);
386     const size_t which = static_cast<size_t>(cookie) - 1;
387     if (which < mAssetPaths.size()) {
388         return mAssetPaths[which].path;
389     }
390     return String8();
391 }
392 
setLocaleLocked(const char * locale)393 void AssetManager::setLocaleLocked(const char* locale)
394 {
395     if (mLocale != NULL) {
396         delete[] mLocale;
397     }
398 
399     mLocale = strdupNew(locale);
400     updateResourceParamsLocked();
401 }
402 
setConfiguration(const ResTable_config & config,const char * locale)403 void AssetManager::setConfiguration(const ResTable_config& config, const char* locale)
404 {
405     AutoMutex _l(mLock);
406     *mConfig = config;
407     if (locale) {
408         setLocaleLocked(locale);
409     } else if (config.language[0] != 0) {
410         char spec[RESTABLE_MAX_LOCALE_LEN];
411         config.getBcp47Locale(spec);
412         setLocaleLocked(spec);
413     } else {
414         updateResourceParamsLocked();
415     }
416 }
417 
getConfiguration(ResTable_config * outConfig) const418 void AssetManager::getConfiguration(ResTable_config* outConfig) const
419 {
420     AutoMutex _l(mLock);
421     *outConfig = *mConfig;
422 }
423 
424 /*
425  * Open an asset.
426  *
427  * The data could be in any asset path. Each asset path could be:
428  *  - A directory on disk.
429  *  - A Zip archive, uncompressed or compressed.
430  *
431  * If the file is in a directory, it could have a .gz suffix, meaning it is compressed.
432  *
433  * We should probably reject requests for "illegal" filenames, e.g. those
434  * with illegal characters or "../" backward relative paths.
435  */
open(const char * fileName,AccessMode mode)436 Asset* AssetManager::open(const char* fileName, AccessMode mode)
437 {
438     AutoMutex _l(mLock);
439 
440     LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager");
441 
442     String8 assetName(kAssetsRoot);
443     appendPath(assetName, fileName);
444 
445     /*
446      * For each top-level asset path, search for the asset.
447      */
448 
449     size_t i = mAssetPaths.size();
450     while (i > 0) {
451         i--;
452         ALOGV("Looking for asset '%s' in '%s'\n",
453                 assetName.c_str(), mAssetPaths.itemAt(i).path.c_str());
454         Asset* pAsset = openNonAssetInPathLocked(assetName.c_str(), mode,
455                 mAssetPaths.editItemAt(i));
456         if (pAsset != NULL) {
457             return pAsset != kExcludedAsset ? pAsset : NULL;
458         }
459     }
460 
461     return NULL;
462 }
463 
464 /*
465  * Open a non-asset file as if it were an asset.
466  *
467  * The "fileName" is the partial path starting from the application name.
468  */
openNonAsset(const char * fileName,AccessMode mode,int32_t * outCookie)469 Asset* AssetManager::openNonAsset(const char* fileName, AccessMode mode, int32_t* outCookie)
470 {
471     AutoMutex _l(mLock);
472 
473     LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager");
474 
475     /*
476      * For each top-level asset path, search for the asset.
477      */
478 
479     size_t i = mAssetPaths.size();
480     while (i > 0) {
481         i--;
482         ALOGV("Looking for non-asset '%s' in '%s'\n", fileName, mAssetPaths.itemAt(i).path.c_str());
483         Asset* pAsset = openNonAssetInPathLocked(
484             fileName, mode, mAssetPaths.editItemAt(i));
485         if (pAsset != NULL) {
486             if (outCookie != NULL) *outCookie = static_cast<int32_t>(i + 1);
487             return pAsset != kExcludedAsset ? pAsset : NULL;
488         }
489     }
490 
491     return NULL;
492 }
493 
openNonAsset(const int32_t cookie,const char * fileName,AccessMode mode)494 Asset* AssetManager::openNonAsset(const int32_t cookie, const char* fileName, AccessMode mode)
495 {
496     const size_t which = static_cast<size_t>(cookie) - 1;
497 
498     AutoMutex _l(mLock);
499 
500     LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager");
501 
502     if (which < mAssetPaths.size()) {
503         ALOGV("Looking for non-asset '%s' in '%s'\n", fileName,
504                 mAssetPaths.itemAt(which).path.c_str());
505         Asset* pAsset = openNonAssetInPathLocked(
506             fileName, mode, mAssetPaths.editItemAt(which));
507         if (pAsset != NULL) {
508             return pAsset != kExcludedAsset ? pAsset : NULL;
509         }
510     }
511 
512     return NULL;
513 }
514 
515 /*
516  * Get the type of a file in the asset namespace.
517  *
518  * This currently only works for regular files.  All others (including
519  * directories) will return kFileTypeNonexistent.
520  */
getFileType(const char * fileName)521 FileType AssetManager::getFileType(const char* fileName)
522 {
523     Asset* pAsset = NULL;
524 
525     /*
526      * Open the asset.  This is less efficient than simply finding the
527      * file, but it's not too bad (we don't uncompress or mmap data until
528      * the first read() call).
529      */
530     pAsset = open(fileName, Asset::ACCESS_STREAMING);
531     delete pAsset;
532 
533     if (pAsset == NULL) {
534         return kFileTypeNonexistent;
535     } else {
536         return kFileTypeRegular;
537     }
538 }
539 
appendPathToResTable(asset_path & ap,bool appAsLib) const540 bool AssetManager::appendPathToResTable(asset_path& ap, bool appAsLib) const {
541     // skip those ap's that correspond to system overlays
542     if (ap.isSystemOverlay) {
543         return true;
544     }
545 
546     Asset* ass = NULL;
547     ResTable* sharedRes = NULL;
548     bool shared = true;
549     bool onlyEmptyResources = true;
550     ATRACE_NAME(ap.path.c_str());
551     Asset* idmap = openIdmapLocked(ap);
552     size_t nextEntryIdx = mResources->getTableCount();
553     ALOGV("Looking for resource asset in '%s'\n", ap.path.c_str());
554     if (ap.type != kFileTypeDirectory && ap.rawFd < 0) {
555         if (nextEntryIdx == 0) {
556             // The first item is typically the framework resources,
557             // which we want to avoid parsing every time.
558             sharedRes = const_cast<AssetManager*>(this)->
559                 mZipSet.getZipResourceTable(ap.path);
560             if (sharedRes != NULL) {
561                 // skip ahead the number of system overlay packages preloaded
562                 nextEntryIdx = sharedRes->getTableCount();
563             }
564         }
565         if (sharedRes == NULL) {
566             ass = const_cast<AssetManager*>(this)->
567                 mZipSet.getZipResourceTableAsset(ap.path);
568             if (ass == NULL) {
569                 ALOGV("loading resource table %s\n", ap.path.c_str());
570                 ass = const_cast<AssetManager*>(this)->
571                     openNonAssetInPathLocked("resources.arsc",
572                                              Asset::ACCESS_BUFFER,
573                                              ap);
574                 if (ass != NULL && ass != kExcludedAsset) {
575                     ass = const_cast<AssetManager*>(this)->
576                         mZipSet.setZipResourceTableAsset(ap.path, ass);
577                 }
578             }
579 
580             if (nextEntryIdx == 0 && ass != NULL) {
581                 // If this is the first resource table in the asset
582                 // manager, then we are going to cache it so that we
583                 // can quickly copy it out for others.
584                 ALOGV("Creating shared resources for %s", ap.path.c_str());
585                 sharedRes = new ResTable();
586                 sharedRes->add(ass, idmap, nextEntryIdx + 1, false);
587 #ifdef __ANDROID__
588                 const char* data = getenv("ANDROID_DATA");
589                 LOG_ALWAYS_FATAL_IF(data == NULL, "ANDROID_DATA not set");
590                 String8 overlaysListPath(data);
591                 appendPath(overlaysListPath, kResourceCache);
592                 appendPath(overlaysListPath, "overlays.list");
593                 addSystemOverlays(overlaysListPath.c_str(), ap.path, sharedRes, nextEntryIdx);
594 #endif
595                 sharedRes = const_cast<AssetManager*>(this)->
596                     mZipSet.setZipResourceTable(ap.path, sharedRes);
597             }
598         }
599     } else {
600         ALOGV("loading resource table %s\n", ap.path.c_str());
601         ass = const_cast<AssetManager*>(this)->
602             openNonAssetInPathLocked("resources.arsc",
603                                      Asset::ACCESS_BUFFER,
604                                      ap);
605         shared = false;
606     }
607 
608     if ((ass != NULL || sharedRes != NULL) && ass != kExcludedAsset) {
609         ALOGV("Installing resource asset %p in to table %p\n", ass, mResources);
610         if (sharedRes != NULL) {
611             ALOGV("Copying existing resources for %s", ap.path.c_str());
612             mResources->add(sharedRes, ap.isSystemAsset);
613         } else {
614             ALOGV("Parsing resources for %s", ap.path.c_str());
615             mResources->add(ass, idmap, nextEntryIdx + 1, !shared, appAsLib, ap.isSystemAsset);
616         }
617         onlyEmptyResources = false;
618 
619         if (!shared) {
620             delete ass;
621         }
622     } else {
623         ALOGV("Installing empty resources in to table %p\n", mResources);
624         mResources->addEmpty(nextEntryIdx + 1);
625     }
626 
627     if (idmap != NULL) {
628         delete idmap;
629     }
630     return onlyEmptyResources;
631 }
632 
getResTable(bool required) const633 const ResTable* AssetManager::getResTable(bool required) const
634 {
635     ResTable* rt = mResources;
636     if (rt) {
637         return rt;
638     }
639 
640     // Iterate through all asset packages, collecting resources from each.
641 
642     AutoMutex _l(mLock);
643 
644     if (mResources != NULL) {
645         return mResources;
646     }
647 
648     if (required) {
649         LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager");
650     }
651 
652     mResources = new ResTable();
653     updateResourceParamsLocked();
654 
655     bool onlyEmptyResources = true;
656     const size_t N = mAssetPaths.size();
657     for (size_t i=0; i<N; i++) {
658         bool empty = appendPathToResTable(
659                 const_cast<AssetManager*>(this)->mAssetPaths.editItemAt(i));
660         onlyEmptyResources = onlyEmptyResources && empty;
661     }
662 
663     if (required && onlyEmptyResources) {
664         ALOGW("Unable to find resources file resources.arsc");
665         delete mResources;
666         mResources = NULL;
667     }
668 
669     return mResources;
670 }
671 
updateResourceParamsLocked() const672 void AssetManager::updateResourceParamsLocked() const
673 {
674     ATRACE_CALL();
675     ResTable* res = mResources;
676     if (!res) {
677         return;
678     }
679 
680     if (mLocale) {
681         mConfig->setBcp47Locale(mLocale);
682     } else {
683         mConfig->clearLocale();
684     }
685 
686     res->setParameters(mConfig);
687 }
688 
openIdmapLocked(const struct asset_path & ap) const689 Asset* AssetManager::openIdmapLocked(const struct asset_path& ap) const
690 {
691     Asset* ass = NULL;
692     if (ap.idmap.size() != 0) {
693         ass = const_cast<AssetManager*>(this)->
694             openAssetFromFileLocked(ap.idmap, Asset::ACCESS_BUFFER);
695         if (ass) {
696             ALOGV("loading idmap %s\n", ap.idmap.c_str());
697         } else {
698             ALOGW("failed to load idmap %s\n", ap.idmap.c_str());
699         }
700     }
701     return ass;
702 }
703 
addSystemOverlays(const char * pathOverlaysList,const String8 & targetPackagePath,ResTable * sharedRes,size_t offset) const704 void AssetManager::addSystemOverlays(const char* pathOverlaysList,
705         const String8& targetPackagePath, ResTable* sharedRes, size_t offset) const
706 {
707     FILE* fin = fopen(pathOverlaysList, "r");
708     if (fin == NULL) {
709         return;
710     }
711 
712 #ifndef _WIN32
713     if (TEMP_FAILURE_RETRY(flock(fileno(fin), LOCK_SH)) != 0) {
714         fclose(fin);
715         return;
716     }
717 #endif
718     char buf[1024];
719     while (fgets(buf, sizeof(buf), fin)) {
720         // format of each line:
721         //   <path to apk><space><path to idmap><newline>
722         char* space = strchr(buf, ' ');
723         char* newline = strchr(buf, '\n');
724         asset_path oap;
725 
726         if (space == NULL || newline == NULL || newline < space) {
727             continue;
728         }
729 
730         oap.path = String8(buf, space - buf);
731         oap.type = kFileTypeRegular;
732         oap.idmap = String8(space + 1, newline - space - 1);
733         oap.isSystemOverlay = true;
734 
735         Asset* oass = const_cast<AssetManager*>(this)->
736             openNonAssetInPathLocked("resources.arsc",
737                     Asset::ACCESS_BUFFER,
738                     oap);
739 
740         if (oass != NULL) {
741             Asset* oidmap = openIdmapLocked(oap);
742             offset++;
743             sharedRes->add(oass, oidmap, offset + 1, false);
744             const_cast<AssetManager*>(this)->mAssetPaths.add(oap);
745             const_cast<AssetManager*>(this)->mZipSet.addOverlay(targetPackagePath, oap);
746             delete oidmap;
747         }
748     }
749 
750 #ifndef _WIN32
751     TEMP_FAILURE_RETRY(flock(fileno(fin), LOCK_UN));
752 #endif
753     fclose(fin);
754 }
755 
getResources(bool required) const756 const ResTable& AssetManager::getResources(bool required) const
757 {
758     const ResTable* rt = getResTable(required);
759     return *rt;
760 }
761 
isUpToDate()762 bool AssetManager::isUpToDate()
763 {
764     AutoMutex _l(mLock);
765     return mZipSet.isUpToDate();
766 }
767 
getLocales(Vector<String8> * locales,bool includeSystemLocales) const768 void AssetManager::getLocales(Vector<String8>* locales, bool includeSystemLocales) const
769 {
770     ResTable* res = mResources;
771     if (res != NULL) {
772         res->getLocales(locales, includeSystemLocales, true /* mergeEquivalentLangs */);
773     }
774 }
775 
776 /*
777  * Open a non-asset file as if it were an asset, searching for it in the
778  * specified app.
779  *
780  * Pass in a NULL values for "appName" if the common app directory should
781  * be used.
782  */
openNonAssetInPathLocked(const char * fileName,AccessMode mode,asset_path & ap)783 Asset* AssetManager::openNonAssetInPathLocked(const char* fileName, AccessMode mode,
784     asset_path& ap)
785 {
786     Asset* pAsset = NULL;
787 
788     ALOGV("openNonAssetInPath: name=%s type=%d fd=%d", fileName, ap.type, ap.rawFd);
789 
790     /* look at the filesystem on disk */
791     if (ap.type == kFileTypeDirectory) {
792         String8 path(ap.path);
793         appendPath(path, fileName);
794 
795         pAsset = openAssetFromFileLocked(path, mode);
796 
797         if (pAsset == NULL) {
798             /* try again, this time with ".gz" */
799             path.append(".gz");
800             pAsset = openAssetFromFileLocked(path, mode);
801         }
802 
803         if (pAsset != NULL) {
804             ALOGV("FOUND NA '%s' on disk", fileName);
805             pAsset->setAssetSource(path);
806         }
807 
808     /* look inside the zip file */
809     } else {
810         String8 path(fileName);
811 
812         /* check the appropriate Zip file */
813         ZipFileRO* pZip = getZipFileLocked(ap);
814         if (pZip != NULL) {
815             ALOGV("GOT zip, checking NA '%s'", path.c_str());
816             ZipEntryRO entry = pZip->findEntryByName(path.c_str());
817             if (entry != NULL) {
818                 ALOGV("FOUND NA in Zip file for %s", path.c_str());
819                 pAsset = openAssetFromZipLocked(pZip, entry, mode, path);
820                 pZip->releaseEntry(entry);
821             }
822         }
823 
824         if (pAsset != NULL) {
825             /* create a "source" name, for debug/display */
826             pAsset->setAssetSource(
827                     createZipSourceNameLocked(ZipSet::getPathName(ap.path.c_str()), String8(""),
828                                                 String8(fileName)));
829         }
830     }
831 
832     return pAsset;
833 }
834 
835 /*
836  * Create a "source name" for a file from a Zip archive.
837  */
createZipSourceNameLocked(const String8 & zipFileName,const String8 & dirName,const String8 & fileName)838 String8 AssetManager::createZipSourceNameLocked(const String8& zipFileName,
839     const String8& dirName, const String8& fileName)
840 {
841     String8 sourceName("zip:");
842     sourceName.append(zipFileName);
843     sourceName.append(":");
844     if (dirName.length() > 0) {
845         appendPath(sourceName, dirName);
846     }
847     appendPath(sourceName, fileName);
848     return sourceName;
849 }
850 
851 /*
852  * Create a path to a loose asset (asset-base/app/rootDir).
853  */
createPathNameLocked(const asset_path & ap,const char * rootDir)854 String8 AssetManager::createPathNameLocked(const asset_path& ap, const char* rootDir)
855 {
856     String8 path(ap.path);
857     if (rootDir != NULL) appendPath(path, rootDir);
858     return path;
859 }
860 
861 /*
862  * Return a pointer to one of our open Zip archives.  Returns NULL if no
863  * matching Zip file exists.
864  */
getZipFileLocked(asset_path & ap)865 ZipFileRO* AssetManager::getZipFileLocked(asset_path& ap)
866 {
867     ALOGV("getZipFileLocked() in %p: ap=%p zip=%p", this, &ap, ap.zip.get());
868 
869     if (ap.zip != NULL) {
870         return ap.zip->getZip();
871     }
872 
873     if (ap.rawFd < 0) {
874         ALOGV("getZipFileLocked: Creating new zip from path %s", ap.path.c_str());
875         ap.zip = mZipSet.getSharedZip(ap.path);
876     } else {
877         ALOGV("getZipFileLocked: Creating new zip from fd %d", ap.rawFd);
878         ap.zip = SharedZip::create(ap.rawFd, ap.path);
879 
880     }
881     return ap.zip != NULL ? ap.zip->getZip() : NULL;
882 }
883 
884 /*
885  * Try to open an asset from a file on disk.
886  *
887  * If the file is compressed with gzip, we seek to the start of the
888  * deflated data and pass that in (just like we would for a Zip archive).
889  *
890  * For uncompressed data, we may already have an mmap()ed version sitting
891  * around.  If so, we want to hand that to the Asset instead.
892  *
893  * This returns NULL if the file doesn't exist, couldn't be opened, or
894  * claims to be a ".gz" but isn't.
895  */
openAssetFromFileLocked(const String8 & pathName,AccessMode mode)896 Asset* AssetManager::openAssetFromFileLocked(const String8& pathName,
897     AccessMode mode)
898 {
899     Asset* pAsset = NULL;
900 
901     if (strcasecmp(getPathExtension(pathName).c_str(), ".gz") == 0) {
902         //printf("TRYING '%s'\n", (const char*) pathName);
903         pAsset = Asset::createFromCompressedFile(pathName.c_str(), mode);
904     } else {
905         //printf("TRYING '%s'\n", (const char*) pathName);
906         pAsset = Asset::createFromFile(pathName.c_str(), mode);
907     }
908 
909     return pAsset;
910 }
911 
912 /*
913  * Given an entry in a Zip archive, create a new Asset object.
914  *
915  * If the entry is uncompressed, we may want to create or share a
916  * slice of shared memory.
917  */
openAssetFromZipLocked(const ZipFileRO * pZipFile,const ZipEntryRO entry,AccessMode mode,const String8 & entryName)918 Asset* AssetManager::openAssetFromZipLocked(const ZipFileRO* pZipFile,
919     const ZipEntryRO entry, AccessMode mode, const String8& entryName)
920 {
921     std::unique_ptr<Asset> pAsset;
922 
923     // TODO: look for previously-created shared memory slice?
924     uint16_t method;
925     uint32_t uncompressedLen;
926 
927     //printf("USING Zip '%s'\n", pEntry->getFileName());
928 
929     if (!pZipFile->getEntryInfo(entry, &method, &uncompressedLen, nullptr, nullptr,
930             nullptr, nullptr, nullptr))
931     {
932         ALOGW("getEntryInfo failed\n");
933         return NULL;
934     }
935 
936     std::optional<incfs::IncFsFileMap> dataMap = pZipFile->createEntryIncFsFileMap(entry);
937     if (!dataMap.has_value()) {
938         ALOGW("create map from entry failed\n");
939         return NULL;
940     }
941 
942     if (method == ZipFileRO::kCompressStored) {
943         pAsset = Asset::createFromUncompressedMap(std::move(*dataMap), mode);
944         ALOGV("Opened uncompressed entry %s in zip %s mode %d: %p", entryName.c_str(),
945                 dataMap->file_name(), mode, pAsset.get());
946     } else {
947         pAsset = Asset::createFromCompressedMap(std::move(*dataMap),
948             static_cast<size_t>(uncompressedLen), mode);
949         ALOGV("Opened compressed entry %s in zip %s mode %d: %p", entryName.c_str(),
950                 dataMap->file_name(), mode, pAsset.get());
951     }
952     if (pAsset == NULL) {
953         /* unexpected */
954         ALOGW("create from segment failed\n");
955     }
956 
957     return pAsset.release();
958 }
959 
960 /*
961  * Open a directory in the asset namespace.
962  *
963  * An "asset directory" is simply the combination of all asset paths' "assets/" directories.
964  *
965  * Pass in "" for the root dir.
966  */
openDir(const char * dirName)967 AssetDir* AssetManager::openDir(const char* dirName)
968 {
969     AutoMutex _l(mLock);
970 
971     AssetDir* pDir = NULL;
972     SortedVector<AssetDir::FileInfo>* pMergedInfo = NULL;
973 
974     LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager");
975     assert(dirName != NULL);
976 
977     //printf("+++ openDir(%s) in '%s'\n", dirName, (const char*) mAssetBase);
978 
979     pDir = new AssetDir;
980 
981     /*
982      * Scan the various directories, merging what we find into a single
983      * vector.  We want to scan them in reverse priority order so that
984      * the ".EXCLUDE" processing works correctly.  Also, if we decide we
985      * want to remember where the file is coming from, we'll get the right
986      * version.
987      *
988      * We start with Zip archives, then do loose files.
989      */
990     pMergedInfo = new SortedVector<AssetDir::FileInfo>;
991 
992     size_t i = mAssetPaths.size();
993     while (i > 0) {
994         i--;
995         const asset_path& ap = mAssetPaths.itemAt(i);
996         if (ap.type == kFileTypeRegular) {
997             ALOGV("Adding directory %s from zip %s", dirName, ap.path.c_str());
998             scanAndMergeZipLocked(pMergedInfo, ap, kAssetsRoot, dirName);
999         } else {
1000             ALOGV("Adding directory %s from dir %s", dirName, ap.path.c_str());
1001             scanAndMergeDirLocked(pMergedInfo, ap, kAssetsRoot, dirName);
1002         }
1003     }
1004 
1005 #if 0
1006     printf("FILE LIST:\n");
1007     for (i = 0; i < (size_t) pMergedInfo->size(); i++) {
1008         printf(" %d: (%d) '%s'\n", i,
1009             pMergedInfo->itemAt(i).getFileType(),
1010             (const char*) pMergedInfo->itemAt(i).getFileName());
1011     }
1012 #endif
1013 
1014     pDir->setFileList(pMergedInfo);
1015     return pDir;
1016 }
1017 
1018 /*
1019  * Open a directory in the non-asset namespace.
1020  *
1021  * An "asset directory" is simply the combination of all asset paths' "assets/" directories.
1022  *
1023  * Pass in "" for the root dir.
1024  */
openNonAssetDir(const int32_t cookie,const char * dirName)1025 AssetDir* AssetManager::openNonAssetDir(const int32_t cookie, const char* dirName)
1026 {
1027     AutoMutex _l(mLock);
1028 
1029     AssetDir* pDir = NULL;
1030     SortedVector<AssetDir::FileInfo>* pMergedInfo = NULL;
1031 
1032     LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager");
1033     assert(dirName != NULL);
1034 
1035     //printf("+++ openDir(%s) in '%s'\n", dirName, (const char*) mAssetBase);
1036 
1037     pDir = new AssetDir;
1038 
1039     pMergedInfo = new SortedVector<AssetDir::FileInfo>;
1040 
1041     const size_t which = static_cast<size_t>(cookie) - 1;
1042 
1043     if (which < mAssetPaths.size()) {
1044         const asset_path& ap = mAssetPaths.itemAt(which);
1045         if (ap.type == kFileTypeRegular) {
1046             ALOGV("Adding directory %s from zip %s", dirName, ap.path.c_str());
1047             scanAndMergeZipLocked(pMergedInfo, ap, NULL, dirName);
1048         } else {
1049             ALOGV("Adding directory %s from dir %s", dirName, ap.path.c_str());
1050             scanAndMergeDirLocked(pMergedInfo, ap, NULL, dirName);
1051         }
1052     }
1053 
1054 #if 0
1055     printf("FILE LIST:\n");
1056     for (i = 0; i < (size_t) pMergedInfo->size(); i++) {
1057         printf(" %d: (%d) '%s'\n", i,
1058             pMergedInfo->itemAt(i).getFileType(),
1059             (const char*) pMergedInfo->itemAt(i).getFileName());
1060     }
1061 #endif
1062 
1063     pDir->setFileList(pMergedInfo);
1064     return pDir;
1065 }
1066 
1067 /*
1068  * Scan the contents of the specified directory and merge them into the
1069  * "pMergedInfo" vector, removing previous entries if we find "exclude"
1070  * directives.
1071  *
1072  * Returns "false" if we found nothing to contribute.
1073  */
scanAndMergeDirLocked(SortedVector<AssetDir::FileInfo> * pMergedInfo,const asset_path & ap,const char * rootDir,const char * dirName)1074 bool AssetManager::scanAndMergeDirLocked(SortedVector<AssetDir::FileInfo>* pMergedInfo,
1075     const asset_path& ap, const char* rootDir, const char* dirName)
1076 {
1077     assert(pMergedInfo != NULL);
1078 
1079     //printf("scanAndMergeDir: %s %s %s\n", ap.path.c_str(), rootDir, dirName);
1080 
1081     String8 path = createPathNameLocked(ap, rootDir);
1082     if (dirName[0] != '\0') appendPath(path, 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().c_str();
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().c_str(),
1115                     pMergedInfo->itemAt(matchIdx).getSourceName().c_str());
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.c_str());
1154 
1155     dir = opendir(path.c_str());
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(appendPathCopy(path, entry->d_name).c_str());
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(getPathExtension(info.getFileName()).c_str(), ".gz") == 0)
1188             info.setFileName(getBasePath(info.getFileName()));
1189         info.setSourceName(appendPathCopy(path, 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.c_str());
1216         return false;
1217     }
1218 
1219     zipName = ZipSet::getPathName(ap.path.c_str());
1220 
1221     /* convert "sounds" to "rootDir/sounds" */
1222     if (rootDir != NULL) dirName = rootDir;
1223     appendPath(dirName, 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.c_str(), 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.c_str());
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(getPathLeaf(String8(nameBuf)), kFileTypeRegular);
1273 
1274                 info.setSourceName(
1275                     createZipSourceNameLocked(zipName, dirName, info.getFileName()));
1276 
1277                 contents.add(info);
1278                 //printf("FOUND: file '%s'\n", info.getFileName().c_str());
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.c_str());
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, mPath.c_str());
1429     }
1430     ALOGV("+++ opening zip '%s'\n", mPath.c_str());
1431     mZipFile = ZipFileRO::open(mPath.c_str());
1432     if (mZipFile == NULL) {
1433         ALOGD("failed to open Zip archive '%s'\n", mPath.c_str());
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, mPath.c_str());
1443     }
1444     ALOGV("+++ opening zip fd=%d '%s'\n", fd, mPath.c_str());
1445     mZipFile = ZipFileRO::openFd(fd, mPath.c_str());
1446     if (mZipFile == NULL) {
1447         ::close(fd);
1448         ALOGD("failed to open Zip archive fd=%d '%s'\n", fd, mPath.c_str());
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.c_str());
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.c_str());
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, mPath.c_str());
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.c_str());
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