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