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