/**
* Shadow - Anonymous web browser for Android devices
* Copyright (C) 2009 Connell Gauld
*
* Thanks to University of Cambridge,
* Alastair Beresford and Andrew Rice
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package info.guardianproject.browser;
import java.util.HashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
* Manages the cache of CacheObjects.
* @author Connell Gauld
*
*/
public class CacheManager {
private static CacheManager mCacheManagerInstance = null;
private Browser mBrowser = null;
public static CacheManager getCacheManager() {
if (mCacheManagerInstance == null) mCacheManagerInstance = new CacheManager();
return mCacheManagerInstance;
}
private CacheManager() {
// Nothing to be done
}
public void setBrowser (Browser browser)
{
mBrowser = browser;
}
private static final float CACHE_SIZE_PROP = 0.75f;
private static final long MAX_CACHE_SIZE = 6 * 1024 * 1024; // 6 MB
HashMap<String, CacheObject> mCacheObjects = new HashMap<String, CacheObject>();
// LRU stack for cache objects
ConcurrentLinkedQueue<CacheObject> mLeastUsedIndex = new ConcurrentLinkedQueue<CacheObject>();
private long mCacheSize = 0;
/**
* Returns a cached object with the given url.
*
* @param url
* the url to get
* @return the CacheObject or null if the object with the given url is not
* in the cache
*/
public synchronized CacheObject getCacheObject(String url) {
CacheObject o = mCacheObjects.get(url);
if (o != null) {
// Remove from wherever it is in the queue
mLeastUsedIndex.remove(o);
// Add it to the end
mLeastUsedIndex.add(o);
}
return o;
}
/**
* Returns the amount of space available for caching.
* @return available space in bytes
*/
private long getAvailableCache() {
long availableToAllocate = (long) ((float) Runtime.getRuntime()
.freeMemory() * CACHE_SIZE_PROP);
long availableBeforeMax = MAX_CACHE_SIZE - mCacheSize;
// Return the smaller of the two
if (availableBeforeMax > availableToAllocate)
return availableToAllocate;
else
return availableBeforeMax;
}
/**
* Add an object to the cache.
* @param url the url corresponding to the object
* @param c the object itself
*/
public synchronized void addCacheObject(String url, CacheObject c) {
long availableCache = getAvailableCache();
long size = c.getSize();
// If the cache is full then clear some items
while (size > availableCache) {
if (!clearOneItemFromCache())
return; // Clear item failed - don't cache this obj
}
// Put the object in the hashmap and the LRU queue
mCacheObjects.put(url, c);
mLeastUsedIndex.add(c);
mCacheSize += c.getSize();
//Log.d("CacheManager", "Cache now at size: " + mCacheSize + " bytes");
//Log.d("CacheManager", "Remaining available cache: " + availableCache
// + " bytes");
}
/**
* Removes the least recently used item from the cache.
* @return true if an item was successfully cleared otherwise false
*/
private boolean clearOneItemFromCache() {
try {
CacheObject o = mLeastUsedIndex.poll();
if (o == null)
return false;
//Log.d("CacheManager", "Cache too large. Clearing one item: "
// + o.getUrl());
mCacheSize -= o.getSize();
mCacheObjects.remove(o.getUrl());
return true;
} catch (Exception e) {
return false;
}
}
/**
* Empties the cache.
*/
public synchronized void clearCache() {
mCacheObjects.clear();
mCacheSize = 0;
mLeastUsedIndex.clear();
if (mBrowser != null)
mBrowser.clearCachedData();
//Log.i("CacheManager", "Cache cleared");
}
}