13713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick/* 23713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * Copyright (C) 2011 The Android Open Source Project 33713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * 43713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * Licensed under the Apache License, Version 2.0 (the "License"); 53713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * you may not use this file except in compliance with the License. 63713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * You may obtain a copy of the License at 73713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * 83713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * http://www.apache.org/licenses/LICENSE-2.0 93713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * 103713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * Unless required by applicable law or agreed to in writing, software 113713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * distributed under the License is distributed on an "AS IS" BASIS, 123713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 133713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * See the License for the specific language governing permissions and 143713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * limitations under the License. 153713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick */ 163713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 173713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickpackage com.android.volley.toolbox; 183713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 199c19fc62ddd9d1b371cb3ead4e10bb5ff1100a6cJeff Sharkeyimport android.os.SystemClock; 209c19fc62ddd9d1b371cb3ead4e10bb5ff1100a6cJeff Sharkey 213713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickimport com.android.volley.Cache; 223713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickimport com.android.volley.VolleyLog; 233713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 243713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickimport java.io.File; 253713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickimport java.io.FileInputStream; 263713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickimport java.io.FileOutputStream; 27c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Bartaimport java.io.FilterInputStream; 283713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickimport java.io.IOException; 293713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickimport java.io.InputStream; 303713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickimport java.io.ObjectInputStream; 313713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickimport java.io.ObjectOutputStream; 323713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickimport java.io.OutputStream; 333713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickimport java.util.Iterator; 343713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickimport java.util.LinkedHashMap; 353713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickimport java.util.Map; 363713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 373713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick/** 383713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * Cache implementation that caches files directly onto the hard disk in the specified 393713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * directory. The default disk usage size is 5MB, but is configurable. 403713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick */ 413713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickpublic class DiskBasedCache implements Cache { 423713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 433713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick /** Map of the Key, CacheHeader pairs */ 443713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick private final Map<String, CacheHeader> mEntries = 453713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick new LinkedHashMap<String, CacheHeader>(16, .75f, true); 463713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 473713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick /** Total amount of space currently used by the cache in bytes. */ 483713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick private long mTotalSize = 0; 493713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 503713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick /** The root directory to use for the cache. */ 513713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick private final File mRootDirectory; 523713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 533713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick /** The maximum size of the cache in bytes. */ 543713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick private final int mMaxCacheSizeInBytes; 553713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 563713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick /** Default maximum disk usage in bytes. */ 573713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick private static final int DEFAULT_DISK_USAGE_BYTES = 5 * 1024 * 1024; 583713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 593713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick /** High water mark percentage for the cache */ 603713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick private static final float HYSTERESIS_FACTOR = 0.9f; 613713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 623713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick /** Current cache version */ 633713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick private static final int CACHE_VERSION = 1; 643713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 653713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick /** 663713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * Constructs an instance of the DiskBasedCache at the specified directory. 673713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * @param rootDirectory The root directory of the cache. 683713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * @param maxCacheSizeInBytes The maximum size of the cache in bytes. 693713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick */ 703713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick public DiskBasedCache(File rootDirectory, int maxCacheSizeInBytes) { 713713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick mRootDirectory = rootDirectory; 723713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick mMaxCacheSizeInBytes = maxCacheSizeInBytes; 733713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 743713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 753713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick /** 763713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * Constructs an instance of the DiskBasedCache at the specified directory using 773713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * the default maximum cache size of 5MB. 783713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * @param rootDirectory The root directory of the cache. 793713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick */ 803713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick public DiskBasedCache(File rootDirectory) { 813713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick this(rootDirectory, DEFAULT_DISK_USAGE_BYTES); 823713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 833713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 843713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick /** 853713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * Clears the cache. Deletes all cached files from disk. 863713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick */ 873713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick @Override 883713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick public synchronized void clear() { 893713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick File[] files = mRootDirectory.listFiles(); 903713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick if (files != null) { 913713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick for (File file : files) { 923713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick file.delete(); 933713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 943713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 953713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick mEntries.clear(); 963713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick mTotalSize = 0; 973713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick VolleyLog.d("Cache cleared."); 983713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 993713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 1003713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick /** 1013713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * Returns the cache entry with the specified key if it exists, null otherwise. 1023713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick */ 1033713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick @Override 1043713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick public synchronized Entry get(String key) { 1053713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick CacheHeader entry = mEntries.get(key); 1063713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick // if the entry does not exist, return. 1073713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick if (entry == null) { 1083713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick return null; 1093713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 1103713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 1113713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick File file = getFileForKey(key); 112c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta CountingInputStream cis = null; 1133713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick try { 114c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta cis = new CountingInputStream(new FileInputStream(file)); 115c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta CacheHeader.readHeader(cis); // eat header 116c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta byte[] data = streamToBytes(cis, (int) (file.length() - cis.bytesRead)); 1173713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick return entry.toCacheEntry(data); 1183713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } catch (IOException e) { 1193713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick VolleyLog.d("%s: %s", file.getAbsolutePath(), e.toString()); 1203713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick remove(key); 1213713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick return null; 1223713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } finally { 123c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta if (cis != null) { 1243713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick try { 125c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta cis.close(); 1263713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } catch (IOException ioe) { 1273713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick return null; 1283713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 1293713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 1303713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 1313713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 1323713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 1333713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick /** 1343713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * Initializes the DiskBasedCache by scanning for all files currently in the 1353713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * specified root directory. 1363713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick */ 1373713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick @Override 1383713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick public synchronized void initialize() { 1393713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick File[] files = mRootDirectory.listFiles(); 1403713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick if (files == null) { 1413713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick return; 1423713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 1433713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick for (File file : files) { 1443713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick FileInputStream fis = null; 1453713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick try { 1463713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick fis = new FileInputStream(file); 1473713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick CacheHeader entry = CacheHeader.readHeader(fis); 1483713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick entry.size = file.length(); 1493713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick putEntry(entry.key, entry); 1503713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } catch (IOException e) { 1513713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick if (file != null) { 1523713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick file.delete(); 1533713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 1543713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } finally { 1553713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick try { 1563713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick if (fis != null) { 1573713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick fis.close(); 1583713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 1593713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } catch (IOException ignored) { } 1603713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 1613713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 1623713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 1633713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 1643713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick /** 1653713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * Invalidates an entry in the cache. 1663713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * @param key Cache key 1673713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * @param fullExpire True to fully expire the entry, false to soft expire 1683713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick */ 1693713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick @Override 1703713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick public synchronized void invalidate(String key, boolean fullExpire) { 1713713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick Entry entry = get(key); 1723713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick if (entry != null) { 1733713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick entry.softTtl = 0; 1743713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick if (fullExpire) { 1753713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick entry.ttl = 0; 1763713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 1773713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick put(key, entry); 1783713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 1793713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 1803713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 1813713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 1823713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick /** 1833713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * Puts the entry with the specified key into the cache. 1843713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick */ 1853713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick @Override 1863713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick public synchronized void put(String key, Entry entry) { 1873713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick pruneIfNeeded(entry.data.length); 1883713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick File file = getFileForKey(key); 1893713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick try { 1903713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick FileOutputStream fos = new FileOutputStream(file); 1913713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick CacheHeader e = new CacheHeader(key, entry); 1923713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick e.writeHeader(fos); 1933713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick fos.write(entry.data); 1943713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick fos.close(); 1953713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick putEntry(key, e); 1963713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick return; 1973713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } catch (IOException e) { 1983713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 1993713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick boolean deleted = file.delete(); 2003713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick if (!deleted) { 2013713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick VolleyLog.d("Could not clean up file %s", file.getAbsolutePath()); 2023713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 2033713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 2043713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 2053713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick /** 2063713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * Removes the specified key from the cache if it exists. 2073713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick */ 2083713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick @Override 2093713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick public synchronized void remove(String key) { 2103713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick boolean deleted = getFileForKey(key).delete(); 2113713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick removeEntry(key); 2123713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick if (!deleted) { 2133713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick VolleyLog.d("Could not delete cache entry for key=%s, filename=%s", 2143713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick key, getFilenameForKey(key)); 2153713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 2163713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 2173713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 2183713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick /** 2193713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * Creates a pseudo-unique filename for the specified cache key. 2203713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * @param key The key to generate a file name for. 2213713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * @return A pseudo-unique filename. 2223713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick */ 2233713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick private String getFilenameForKey(String key) { 2243713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick int firstHalfLength = key.length() / 2; 2253713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick String localFilename = String.valueOf(key.substring(0, firstHalfLength).hashCode()); 2263713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick localFilename += String.valueOf(key.substring(firstHalfLength).hashCode()); 2273713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick return localFilename; 2283713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 2293713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 2303713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick /** 2313713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * Returns a file object for the given cache key. 2323713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick */ 233cbbd3af2a6fd57ebbe9da9b1c2f42a3c98171bb1Aurash Mahbod public File getFileForKey(String key) { 2343713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick return new File(mRootDirectory, getFilenameForKey(key)); 2353713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 2363713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 2373713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick /** 2383713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * Prunes the cache to fit the amount of bytes specified. 2393713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * @param neededSpace The amount of bytes we are trying to fit into the cache. 2403713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick */ 2413713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick private void pruneIfNeeded(int neededSpace) { 2423713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick if ((mTotalSize + neededSpace) < mMaxCacheSizeInBytes) { 2433713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick return; 2443713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 2453713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick if (VolleyLog.DEBUG) { 2463713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick VolleyLog.v("Pruning old cache entries."); 2473713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 2483713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 2493713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick long before = mTotalSize; 2503713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick int prunedFiles = 0; 2519c19fc62ddd9d1b371cb3ead4e10bb5ff1100a6cJeff Sharkey long startTime = SystemClock.elapsedRealtime(); 2523713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 2533713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick Iterator<Map.Entry<String, CacheHeader>> iterator = mEntries.entrySet().iterator(); 2543713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick while (iterator.hasNext()) { 2553713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick Map.Entry<String, CacheHeader> entry = iterator.next(); 2563713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick CacheHeader e = entry.getValue(); 2573713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick boolean deleted = getFileForKey(e.key).delete(); 2583713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick if (deleted) { 2593713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick mTotalSize -= e.size; 2603713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } else { 2613713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick VolleyLog.d("Could not delete cache entry for key=%s, filename=%s", 2623713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick e.key, getFilenameForKey(e.key)); 2633713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 2643713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick iterator.remove(); 2653713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick prunedFiles++; 2663713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 2673713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick if ((mTotalSize + neededSpace) < mMaxCacheSizeInBytes * HYSTERESIS_FACTOR) { 2683713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick break; 2693713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 2703713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 2713713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 2723713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick if (VolleyLog.DEBUG) { 2733713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick VolleyLog.v("pruned %d files, %d bytes, %d ms", 2749c19fc62ddd9d1b371cb3ead4e10bb5ff1100a6cJeff Sharkey prunedFiles, (mTotalSize - before), SystemClock.elapsedRealtime() - startTime); 2753713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 2763713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 2773713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 2783713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick /** 2793713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * Puts the entry with the specified key into the cache. 2803713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * @param key The key to identify the entry by. 2813713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * @param entry The entry to cache. 2823713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick */ 2833713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick private void putEntry(String key, CacheHeader entry) { 2843713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick if (!mEntries.containsKey(key)) { 2853713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick mTotalSize += entry.size; 2863713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } else { 2873713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick CacheHeader oldEntry = mEntries.get(key); 2883713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick mTotalSize += (entry.size - oldEntry.size); 2893713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 2903713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick mEntries.put(key, entry); 2913713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 2923713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 2933713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick /** 2943713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * Removes the entry identified by 'key' from the cache. 2953713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick */ 2963713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick private void removeEntry(String key) { 2973713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick CacheHeader entry = mEntries.get(key); 2983713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick if (entry != null) { 2993713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick mTotalSize -= entry.size; 3003713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick mEntries.remove(key); 3013713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 3023713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 3033713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 3043713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick /** 3053713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * Reads the contents of an InputStream into a byte[]. 3063713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * */ 307c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta private static byte[] streamToBytes(InputStream in, int length) throws IOException { 308c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta byte[] bytes = new byte[length]; 3093713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick int count; 310c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta int pos = 0; 311c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta while (pos < length && ((count = in.read(bytes, pos, length - pos)) != -1)) { 312c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta pos += count; 3133713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 314c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta if (pos != length) { 315c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta throw new IOException("Expected " + length + " bytes, read " + pos + " bytes"); 316c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta } 317c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta return bytes; 3183713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 3193713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 3203713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick /** 3213713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * Handles holding onto the cache headers for an entry. 3223713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick */ 3233713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick private static class CacheHeader { 3243713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick /** The size of the data identified by this CacheHeader. (This is not 3253713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * serialized to disk. */ 3263713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick public long size; 3273713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 3283713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick /** The key that identifies the cache entry. */ 3293713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick public String key; 3303713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 3313713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick /** ETag for cache coherence. */ 3323713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick public String etag; 3333713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 3343713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick /** Date of this response as reported by the server. */ 3353713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick public long serverDate; 3363713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 3373713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick /** TTL for this record. */ 3383713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick public long ttl; 3393713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 3403713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick /** Soft TTL for this record. */ 3413713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick public long softTtl; 3423713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 3433713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick private CacheHeader() { } 3443713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 3453713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick /** 3463713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * Instantiates a new CacheHeader object 3473713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * @param key The key that identifies the cache entry 3483713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * @param entry The cache entry. 3493713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick */ 3503713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick public CacheHeader(String key, Entry entry) { 3513713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick this.key = key; 3523713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick this.size = entry.data.length; 3533713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick this.etag = entry.etag; 3543713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick this.serverDate = entry.serverDate; 3553713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick this.ttl = entry.ttl; 3563713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick this.softTtl = entry.softTtl; 3573713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 3583713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 3593713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick /** 3603713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * Reads the header off of an InputStream and returns a CacheHeader object. 3613713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * @param is The InputStream to read from. 3623713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * @throws IOException 3633713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick */ 3643713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick public static CacheHeader readHeader(InputStream is) throws IOException { 3653713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick CacheHeader entry = new CacheHeader(); 3663713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick ObjectInputStream ois = new ObjectInputStream(is); 3673713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick int version = ois.readByte(); 3683713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick if (version != CACHE_VERSION) { 3693713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick // don't bother deleting, it'll get pruned eventually 3703713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick throw new IOException(); 3713713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 3723713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick entry.key = ois.readUTF(); 3733713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick entry.etag = ois.readUTF(); 3743713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick if (entry.etag.equals("")) { 3753713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick entry.etag = null; 3763713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 3773713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick entry.serverDate = ois.readLong(); 3783713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick entry.ttl = ois.readLong(); 3793713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick entry.softTtl = ois.readLong(); 3803713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick return entry; 3813713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 3823713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 3833713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick /** 3843713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * Creates a cache entry for the specified data. 3853713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick */ 3863713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick public Entry toCacheEntry(byte[] data) { 3873713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick Entry e = new Entry(); 3883713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick e.data = data; 3893713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick e.etag = etag; 3903713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick e.serverDate = serverDate; 3913713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick e.ttl = ttl; 3923713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick e.softTtl = softTtl; 3933713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick return e; 3943713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 3953713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick 3963713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick /** 3973713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * Writes the contents of this CacheHeader to the specified OutputStream. 3983713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick */ 3993713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick public boolean writeHeader(OutputStream os) { 4003713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick try { 4013713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick ObjectOutputStream oos = new ObjectOutputStream(os); 4023713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick oos.writeByte(CACHE_VERSION); 4033713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick oos.writeUTF(key); 4043713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick oos.writeUTF(etag == null ? "" : etag); 4053713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick oos.writeLong(serverDate); 4063713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick oos.writeLong(ttl); 4073713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick oos.writeLong(softTtl); 4083713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick oos.flush(); 4093713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick return true; 4103713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } catch (IOException e) { 4113713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick VolleyLog.d("%s", e.toString()); 4123713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick return false; 4133713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 4143713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 4153713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick } 416c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta 417c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta private static class CountingInputStream extends FilterInputStream { 418c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta private int bytesRead = 0; 419c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta 420c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta private CountingInputStream(InputStream in) { 421c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta super(in); 422c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta } 423c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta 424c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta @Override 425c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta public int read() throws IOException { 426c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta int result = super.read(); 427c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta if (result != -1) { 428c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta bytesRead++; 429c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta } 430c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta return result; 431c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta } 432c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta 433c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta @Override 434c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta public int read(byte[] buffer, int offset, int count) throws IOException { 435c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta int result = super.read(buffer, offset, count); 436c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta if (result != -1) { 437c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta bytesRead += result; 438c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta } 439c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta return result; 440c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta } 441c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta } 4423713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick} 443