package com.mopub.common; import android.content.Context; import android.graphics.Bitmap; import android.os.AsyncTask; import android.support.v4.util.LruCache; import com.mopub.common.util.DeviceUtils; import com.mopub.common.logging.MoPubLog; import com.mopub.common.util.Streams; import com.mopub.common.util.Utils; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import static com.mopub.common.DiskLruCache.open; public class CacheService { public static interface DiskLruCacheGetListener { void onComplete(final String key, final byte[] content); } static final String UNIQUE_CACHE_NAME = "mopub-cache"; private static final int APP_VERSION = 1; // The number of values per cache entry. Must be positive. private static final int VALUE_COUNT = 1; private static final int DISK_CACHE_INDEX = 0; private static DiskLruCache sDiskLruCache; public static boolean initializeDiskCache(final Context context) { if (context == null) { return false; } if (sDiskLruCache == null) { final File cacheDirectory = getDiskCacheDirectory(context); final long diskCacheSizeBytes = DeviceUtils.diskCacheSizeBytes(cacheDirectory); try { sDiskLruCache = open( cacheDirectory, APP_VERSION, VALUE_COUNT, diskCacheSizeBytes ); } catch (IOException e) { MoPubLog.d("Unable to create DiskLruCache", e); } } return true; } public static void initialize(final Context context) { initializeDiskCache(context); } public static String createValidDiskCacheKey(final String key) { return Utils.sha1(key); } public static File getDiskCacheDirectory(final Context context) { final String cachePath = context.getCacheDir().getPath(); return new File(cachePath + File.separator + UNIQUE_CACHE_NAME); } public static boolean containsKeyDiskCache(final String key) { if (sDiskLruCache == null) { return false; } try { final DiskLruCache.Snapshot snapshot = sDiskLruCache.get(createValidDiskCacheKey(key)); return snapshot != null; } catch (Exception e) { return false; } } public static String getFilePathDiskCache(final String key) { if (sDiskLruCache == null) { return null; } // This violates encapsulation but there is no convenience method to get a filename from // DiskLruCache. Filename was derived from private class method Entry#getCleanFile // in DiskLruCache.java return sDiskLruCache.getDirectory() + File.separator + createValidDiskCacheKey(key) + "." + DISK_CACHE_INDEX; } public static byte[] getFromDiskCache(final String key) { if (sDiskLruCache == null) { return null; } byte[] bytes = null; DiskLruCache.Snapshot snapshot = null; try { snapshot = sDiskLruCache.get(createValidDiskCacheKey(key)); if (snapshot == null) { return null; } final InputStream in = snapshot.getInputStream(DISK_CACHE_INDEX); if (in != null) { bytes = new byte[(int) snapshot.getLength(0)]; final BufferedInputStream buffIn = new BufferedInputStream(in); try { Streams.readStream(buffIn, bytes); } finally { Streams.closeStream(buffIn); } } } catch (Exception e) { MoPubLog.d("Unable to get from DiskLruCache", e); } finally { if (snapshot != null) { snapshot.close(); } } return bytes; } public static void getFromDiskCacheAsync(final String key, final DiskLruCacheGetListener diskLruCacheGetListener) { new DiskLruCacheGetTask(key, diskLruCacheGetListener).execute(); } public static boolean putToDiskCache(final String key, final byte[] content) { return putToDiskCache(key, new ByteArrayInputStream(content)); } public static boolean putToDiskCache(final String key, final InputStream content) { if (sDiskLruCache == null) { return false; } DiskLruCache.Editor editor = null; try { editor = sDiskLruCache.edit(createValidDiskCacheKey(key)); if (editor == null) { // another edit is in progress return false; } final OutputStream outputStream = new BufferedOutputStream(editor.newOutputStream(DISK_CACHE_INDEX)); Streams.copyContent(content, outputStream); outputStream.flush(); outputStream.close(); sDiskLruCache.flush(); editor.commit(); } catch (Exception e) { MoPubLog.d("Unable to put to DiskLruCache", e); try { if (editor != null) { editor.abort(); } } catch (IOException ignore) { // ignore } return false; } return true; } public static void putToDiskCacheAsync(final String key, final byte[] content) { new DiskLruCachePutTask(key, content).execute(); } private static class DiskLruCacheGetTask extends AsyncTask<Void, Void, byte[]> { private final DiskLruCacheGetListener mDiskLruCacheGetListener; private final String mKey; DiskLruCacheGetTask(final String key, final DiskLruCacheGetListener diskLruCacheGetListener) { mDiskLruCacheGetListener = diskLruCacheGetListener; mKey = key; } @Override protected byte[] doInBackground(Void... voids) { return CacheService.getFromDiskCache(mKey); } @Override protected void onPostExecute(final byte[] bytes) { if (isCancelled()) { onCancelled(); return; } if (mDiskLruCacheGetListener != null) { mDiskLruCacheGetListener.onComplete(mKey, bytes); } } @Override protected void onCancelled() { if (mDiskLruCacheGetListener != null) { mDiskLruCacheGetListener.onComplete(mKey, null); } } } private static class DiskLruCachePutTask extends AsyncTask<Void, Void, Void> { private final String mKey; private final byte[] mContent; DiskLruCachePutTask(final String key, final byte[] content) { mKey = key; mContent = content; } @Override protected Void doInBackground(Void... voids) { CacheService.putToDiskCache(mKey, mContent); return null; } } // Testing @Deprecated @VisibleForTesting public static void clearAndNullCaches() { if (sDiskLruCache != null) { try { sDiskLruCache.delete(); sDiskLruCache = null; } catch (IOException ignore) { sDiskLruCache = null; } } } // Testing @Deprecated @VisibleForTesting public static DiskLruCache getDiskLruCache() { return sDiskLruCache; } }