xref: /aosp_15_r20/frameworks/base/libs/androidfw/AssetManager.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
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