CacheManager.java revision a1ba11bd78f9540e58516dd742fe4cc8726e262f
19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/* 29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2006 The Android Open Source Project 39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); 59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License. 69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at 79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software 119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and 149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License. 159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.webkit; 189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Context; 209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.net.http.Headers; 219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.FileUtils; 229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Log; 239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.File; 249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.FileInputStream; 259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.FileNotFoundException; 269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.FileOutputStream; 279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.IOException; 289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.InputStream; 299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.OutputStream; 309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.ArrayList; 319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.Map; 329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport org.bouncycastle.crypto.Digest; 349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport org.bouncycastle.crypto.digests.SHA1Digest; 359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/** 379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * The class CacheManager provides the persistent cache of content that is 389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * received over the network. The component handles parsing of HTTP headers and 399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * utilizes the relevant cache headers to determine if the content should be 409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * stored and if so, how long it is valid for. Network requests are provided to 419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * this component and if they can not be resolved by the cache, the HTTP headers 429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * are attached, as appropriate, to the request for revalidation of content. The 439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * class also manages the cache size. 449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic final class CacheManager { 469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String LOGTAG = "cache"; 489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project static final String HEADER_KEY_IFMODIFIEDSINCE = "if-modified-since"; 509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project static final String HEADER_KEY_IFNONEMATCH = "if-none-match"; 519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String NO_STORE = "no-store"; 539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String NO_CACHE = "no-cache"; 549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String MAX_AGE = "max-age"; 55a1ba11bd78f9540e58516dd742fe4cc8726e262fAndrei Popescu private static final String MANIFEST_MIME = "text/cache-manifest"; 569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static long CACHE_THRESHOLD = 6 * 1024 * 1024; 589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static long CACHE_TRIM_AMOUNT = 2 * 1024 * 1024; 599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 60998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba // Limit the maximum cache file size to half of the normal capacity 61998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba static long CACHE_MAX_SIZE = (CACHE_THRESHOLD - CACHE_TRIM_AMOUNT) / 2; 62998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba 639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static boolean mDisabled; 649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Reference count the enable/disable transaction 669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static int mRefCount; 679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // trimCacheIfNeeded() is called when a page is fully loaded. But JavaScript 699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // can load the content, e.g. in a slideshow, continuously, so we need to 709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // trim the cache on a timer base too. endCacheTransaction() is called on a 719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // timer base. We share the same timer with less frequent update. 729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static int mTrimCacheCount = 0; 739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final int TRIM_CACHE_INTERVAL = 5; 749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static WebViewDatabase mDataBase; 769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static File mBaseDir; 779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Flag to clear the cache when the CacheManager is initialized 799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static boolean mClearCacheOnInit = false; 809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 81f0c443deca49d597c8268ef3b0f7198976073241Ben Murdoch /** 82f0c443deca49d597c8268ef3b0f7198976073241Ben Murdoch * This class represents a resource retrieved from the HTTP cache. 83f0c443deca49d597c8268ef3b0f7198976073241Ben Murdoch * Instances of this class can be obtained by invoking the 84f0c443deca49d597c8268ef3b0f7198976073241Ben Murdoch * CacheManager.getCacheFile() method. 85f0c443deca49d597c8268ef3b0f7198976073241Ben Murdoch */ 869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static class CacheResult { 879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // these fields are saved to the database 889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int httpStatusCode; 899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project long contentLength; 909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project long expires; 91e64c5567de20d06ac7ed1f5a01f018991cd40a52Grace Kloba String expiresString; 929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String localPath; 939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String lastModified; 949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String etag; 959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String mimeType; 969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String location; 979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String encoding; 980b956e1353a691674cb22c899c5a444b92532b60Grace Kloba String contentdisposition; 999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // these fields are NOT saved to the database 1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project InputStream inStream; 1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project OutputStream outStream; 1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project File outFile; 1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getHttpStatusCode() { 1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return httpStatusCode; 1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public long getContentLength() { 1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return contentLength; 1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public String getLocalPath() { 1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return localPath; 1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public long getExpires() { 1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return expires; 1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 121e64c5567de20d06ac7ed1f5a01f018991cd40a52Grace Kloba public String getExpiresString() { 122e64c5567de20d06ac7ed1f5a01f018991cd40a52Grace Kloba return expiresString; 123e64c5567de20d06ac7ed1f5a01f018991cd40a52Grace Kloba } 124e64c5567de20d06ac7ed1f5a01f018991cd40a52Grace Kloba 1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public String getLastModified() { 1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return lastModified; 1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public String getETag() { 1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return etag; 1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public String getMimeType() { 1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mimeType; 1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public String getLocation() { 1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return location; 1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public String getEncoding() { 1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return encoding; 1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1450b956e1353a691674cb22c899c5a444b92532b60Grace Kloba public String getContentDisposition() { 1460b956e1353a691674cb22c899c5a444b92532b60Grace Kloba return contentdisposition; 1470b956e1353a691674cb22c899c5a444b92532b60Grace Kloba } 1480b956e1353a691674cb22c899c5a444b92532b60Grace Kloba 1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // For out-of-package access to the underlying streams. 1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public InputStream getInputStream() { 1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return inStream; 1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public OutputStream getOutputStream() { 1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return outStream; 1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // These fields can be set manually. 1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void setInputStream(InputStream stream) { 1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project this.inStream = stream; 1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void setEncoding(String encoding) { 1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project this.encoding = encoding; 1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * initialize the CacheManager. WebView should handle this for each process. 1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param context The application context. 1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project static void init(Context context) { 17401d0fbfa683012623f030ec75a63e1a9fabcb916Romain Guy mDataBase = WebViewDatabase.getInstance(context.getApplicationContext()); 1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mBaseDir = new File(context.getCacheDir(), "webviewCache"); 1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (createCacheDirectory() && mClearCacheOnInit) { 1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project removeAllCacheFiles(); 1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mClearCacheOnInit = false; 1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Create the cache directory if it does not already exist. 1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @return true if the cache directory didn't exist and was created. 1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project static private boolean createCacheDirectory() { 1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (!mBaseDir.exists()) { 1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if(!mBaseDir.mkdirs()) { 1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Log.w(LOGTAG, "Unable to create webviewCache directory"); 1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return false; 1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project FileUtils.setPermissions( 1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mBaseDir.toString(), 1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH, 1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project -1, -1); 1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // If we did create the directory, we need to flush 1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // the cache database. The directory could be recreated 1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // because the system flushed all the data/cache directories 2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // to free up disk space. 2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project WebViewCore.endCacheTransaction(); 2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mDataBase.clearCache(); 2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project WebViewCore.startCacheTransaction(); 2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return true; 2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return false; 2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * get the base directory of the cache. With localPath of the CacheResult, 2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * it identifies the cache file. 2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @return File The base directory of the cache. 2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static File getCacheFileBaseDir() { 2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mBaseDir; 2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * set the flag to control whether cache is enabled or disabled 2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param disabled true to disable the cache 2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // only called from WebCore thread 2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project static void setCacheDisabled(boolean disabled) { 2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (disabled == mDisabled) { 2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return; 2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mDisabled = disabled; 2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mDisabled) { 2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project removeAllCacheFiles(); 2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * get the state of the current cache, enabled or disabled 2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @return return if it is disabled 2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static boolean cacheDisabled() { 2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mDisabled; 2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // only called from WebCore thread 2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // make sure to call enableTransaction/disableTransaction in pair 2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project static boolean enableTransaction() { 2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (++mRefCount == 1) { 2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mDataBase.startCacheTransaction(); 2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return true; 2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return false; 2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // only called from WebCore thread 2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // make sure to call enableTransaction/disableTransaction in pair 2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project static boolean disableTransaction() { 2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mRefCount == 0) { 2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Log.e(LOGTAG, "disableTransaction is out of sync"); 2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (--mRefCount == 0) { 2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mDataBase.endCacheTransaction(); 2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return true; 2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return false; 2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // only called from WebCore thread 2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // make sure to call startCacheTransaction/endCacheTransaction in pair 2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static boolean startCacheTransaction() { 2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mDataBase.startCacheTransaction(); 2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // only called from WebCore thread 2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // make sure to call startCacheTransaction/endCacheTransaction in pair 2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static boolean endCacheTransaction() { 2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean ret = mDataBase.endCacheTransaction(); 2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (++mTrimCacheCount >= TRIM_CACHE_INTERVAL) { 2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mTrimCacheCount = 0; 2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project trimCacheIfNeeded(); 2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return ret; 2829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 2859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Given a url, returns the CacheResult if exists. Otherwise returns null. 2869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * If headers are provided and a cache needs validation, 2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * HEADER_KEY_IFNONEMATCH or HEADER_KEY_IFMODIFIEDSINCE will be set in the 2889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * cached headers. 2899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 2909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @return the CacheResult for a given url 2919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 2929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // only called from WebCore thread 2939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static CacheResult getCacheFile(String url, 2949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Map<String, String> headers) { 2958c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba return getCacheFile(url, 0, headers); 2968c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba } 2978c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba 2988c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba // only called from WebCore thread 2998c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba static CacheResult getCacheFile(String url, long postIdentifier, 3008c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba Map<String, String> headers) { 3019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mDisabled) { 3029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return null; 3039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3058c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba String databaseKey = getDatabaseKey(url, postIdentifier); 3068c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba 3078c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba CacheResult result = mDataBase.getCache(databaseKey); 3089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (result != null) { 3099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (result.contentLength == 0) { 310105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project if (!checkCacheRedirect(result.httpStatusCode)) { 3119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // this should not happen. If it does, remove it. 3128c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba mDataBase.removeCache(databaseKey); 3139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return null; 3149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 3169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project File src = new File(mBaseDir, result.localPath); 3179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 3189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // open here so that even the file is deleted, the content 3199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // is still readable by the caller until close() is called 3209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project result.inStream = new FileInputStream(src); 3219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } catch (FileNotFoundException e) { 3229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // the files in the cache directory can be removed by the 3239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // system. If it is gone, clean up the database 3248c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba mDataBase.removeCache(databaseKey); 3259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return null; 3269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 3299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return null; 3309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // null headers request coming from CACHE_MODE_CACHE_ONLY 3339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // which implies that it needs cache even it is expired. 3349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // negative expires means time in the far future. 3359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (headers != null && result.expires >= 0 3369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project && result.expires <= System.currentTimeMillis()) { 3379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (result.lastModified == null && result.etag == null) { 3389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return null; 3399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // return HEADER_KEY_IFNONEMATCH or HEADER_KEY_IFMODIFIEDSINCE 3419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // for requesting validation 3429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (result.etag != null) { 3439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project headers.put(HEADER_KEY_IFNONEMATCH, result.etag); 3449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (result.lastModified != null) { 3469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project headers.put(HEADER_KEY_IFMODIFIEDSINCE, result.lastModified); 3479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3502e5c150e746647a1ce5c10e1708debbf06c45ea7Derek Sollenberger if (DebugFlags.CACHE_MANAGER) { 3519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Log.v(LOGTAG, "getCacheFile for url " + url); 3529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return result; 3559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 3589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Given a url and its full headers, returns CacheResult if a local cache 3599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * can be stored. Otherwise returns null. The mimetype is passed in so that 3609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * the function can use the mimetype that will be passed to WebCore which 3619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * could be different from the mimetype defined in the headers. 3629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * forceCache is for out-of-package callers to force creation of a 3639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * CacheResult, and is used to supply surrogate responses for URL 3649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * interception. 3659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @return CacheResult for a given url 3669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @hide - hide createCacheFile since it has a parameter of type headers, which is 3679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * in a hidden package. 3689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 3693afdd56470d6d4dcb20fe0f68ec9e54a167a9d74Grace Kloba // only called from WebCore thread 3709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static CacheResult createCacheFile(String url, int statusCode, 3719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Headers headers, String mimeType, boolean forceCache) { 3728c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba return createCacheFile(url, statusCode, headers, mimeType, 0, 3738c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba forceCache); 3748c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba } 3758c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba 3768c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba // only called from WebCore thread 3778c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba static CacheResult createCacheFile(String url, int statusCode, 3788c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba Headers headers, String mimeType, long postIdentifier, 3798c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba boolean forceCache) { 3809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (!forceCache && mDisabled) { 3819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return null; 3829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3848c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba String databaseKey = getDatabaseKey(url, postIdentifier); 3858c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba 386105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project // according to the rfc 2616, the 303 response MUST NOT be cached. 387105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project if (statusCode == 303) { 3883afdd56470d6d4dcb20fe0f68ec9e54a167a9d74Grace Kloba // remove the saved cache if there is any 3898c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba mDataBase.removeCache(databaseKey); 390105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project return null; 391105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project } 392105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project 393105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project // like the other browsers, do not cache redirects containing a cookie 394105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project // header. 395105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project if (checkCacheRedirect(statusCode) && !headers.getSetCookie().isEmpty()) { 3963afdd56470d6d4dcb20fe0f68ec9e54a167a9d74Grace Kloba // remove the saved cache if there is any 3978c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba mDataBase.removeCache(databaseKey); 398105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project return null; 399105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project } 400105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project 4019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project CacheResult ret = parseHeaders(statusCode, headers, mimeType); 4023afdd56470d6d4dcb20fe0f68ec9e54a167a9d74Grace Kloba if (ret == null) { 4033afdd56470d6d4dcb20fe0f68ec9e54a167a9d74Grace Kloba // this should only happen if the headers has "no-store" in the 4043afdd56470d6d4dcb20fe0f68ec9e54a167a9d74Grace Kloba // cache-control. remove the saved cache if there is any 4058c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba mDataBase.removeCache(databaseKey); 4063afdd56470d6d4dcb20fe0f68ec9e54a167a9d74Grace Kloba } else { 4078c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba setupFiles(databaseKey, ret); 4089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 4099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ret.outStream = new FileOutputStream(ret.outFile); 4109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } catch (FileNotFoundException e) { 4119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // This can happen with the system did a purge and our 4129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // subdirectory has gone, so lets try to create it again 4139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (createCacheDirectory()) { 4149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 4159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ret.outStream = new FileOutputStream(ret.outFile); 4169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } catch (FileNotFoundException e2) { 4179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // We failed to create the file again, so there 4189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // is something else wrong. Return null. 4199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return null; 4209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 4229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Failed to create cache directory 4239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return null; 4249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ret.mimeType = mimeType; 4279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 4299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return ret; 4309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 4329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 4339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Save the info of a cache file for a given url to the CacheMap so that it 4349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * can be reused later 4359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 4369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // only called from WebCore thread 4379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static void saveCacheFile(String url, CacheResult cacheRet) { 4388c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba saveCacheFile(url, 0, cacheRet); 4398c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba } 4408c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba 4418c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba // only called from WebCore thread 4428c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba static void saveCacheFile(String url, long postIdentifier, 4438c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba CacheResult cacheRet) { 4449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 4459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project cacheRet.outStream.close(); 4469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } catch (IOException e) { 4479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return; 4489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 4509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (!cacheRet.outFile.exists()) { 4519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // the file in the cache directory can be removed by the system 4529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return; 4539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 455543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark boolean redirect = checkCacheRedirect(cacheRet.httpStatusCode); 456543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark if (redirect) { 4579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // location is in database, no need to keep the file 4589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project cacheRet.contentLength = 0; 459686cf75d5cf447d34961f6217f2ea3ce3e484ac2Cary Clark cacheRet.localPath = ""; 460543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark } 461543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark if ((redirect || cacheRet.contentLength == 0) 462543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark && !cacheRet.outFile.delete()) { 463543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark Log.e(LOGTAG, cacheRet.outFile.getPath() + " delete failed."); 464543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark } 465543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark if (cacheRet.contentLength == 0) { 4669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return; 4679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 4698c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba mDataBase.addCache(getDatabaseKey(url, postIdentifier), cacheRet); 4709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 4712e5c150e746647a1ce5c10e1708debbf06c45ea7Derek Sollenberger if (DebugFlags.CACHE_MANAGER) { 4729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Log.v(LOGTAG, "saveCacheFile for url " + url); 4739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 476998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba static boolean cleanupCacheFile(CacheResult cacheRet) { 477998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba try { 478998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba cacheRet.outStream.close(); 479998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba } catch (IOException e) { 480998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba return false; 481998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba } 482998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba return cacheRet.outFile.delete(); 483998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba } 484998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba 4859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 4869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * remove all cache files 4879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 4889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @return true if it succeeds 4899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 4909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // only called from WebCore thread 4919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project static boolean removeAllCacheFiles() { 4929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Note, this is called before init() when the database is 4939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // created or upgraded. 4949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mBaseDir == null) { 4959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Init() has not been called yet, so just flag that 4969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // we need to clear the cache when init() is called. 4979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mClearCacheOnInit = true; 4989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return true; 4999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // delete cache in a separate thread to not block UI. 5019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final Runnable clearCache = new Runnable() { 5029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void run() { 5039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // delete all cache files 5049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 5059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String[] files = mBaseDir.list(); 5069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // if mBaseDir doesn't exist, files can be null. 5079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (files != null) { 5089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int i = 0; i < files.length; i++) { 509543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark File f = new File(mBaseDir, files[i]); 510543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark if (!f.delete()) { 511543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark Log.e(LOGTAG, f.getPath() + " delete failed."); 512543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark } 5139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } catch (SecurityException e) { 5169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Ignore SecurityExceptions. 5179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // delete database 5199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mDataBase.clearCache(); 5209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project }; 5229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project new Thread(clearCache).start(); 5239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return true; 5249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 5279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return true if the cache is empty. 5289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 5299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // only called from WebCore thread 5309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project static boolean cacheEmpty() { 5319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mDataBase.hasCache(); 5329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // only called from WebCore thread 5359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project static void trimCacheIfNeeded() { 5369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mDataBase.getCacheTotalSize() > CACHE_THRESHOLD) { 5379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ArrayList<String> pathList = mDataBase.trimCache(CACHE_TRIM_AMOUNT); 5389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int size = pathList.size(); 5399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int i = 0; i < size; i++) { 540543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark File f = new File(mBaseDir, pathList.get(i)); 541543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark if (!f.delete()) { 542543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark Log.e(LOGTAG, f.getPath() + " delete failed."); 543543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark } 5449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 548105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project private static boolean checkCacheRedirect(int statusCode) { 549105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project if (statusCode == 301 || statusCode == 302 || statusCode == 307) { 550105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project // as 303 can't be cached, we do not return true 551105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project return true; 552105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project } else { 553105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project return false; 554105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project } 555105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project } 556105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project 5578c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba private static String getDatabaseKey(String url, long postIdentifier) { 5588c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba if (postIdentifier == 0) return url; 5598c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba return postIdentifier + url; 5608c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba } 5618c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba 5629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project @SuppressWarnings("deprecation") 5639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static void setupFiles(String url, CacheResult cacheRet) { 5649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (true) { 5659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Note: SHA1 is much stronger hash. But the cost of setupFiles() is 5669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // 3.2% cpu time for a fresh load of nytimes.com. While a simple 5679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // String.hashCode() is only 0.6%. If adding the collision resolving 5689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // to String.hashCode(), it makes the cpu time to be 1.6% for a 5699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // fresh load, but 5.3% for the worst case where all the files 5709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // already exist in the file system, but database is gone. So it 5719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // needs to resolve collision for every file at least once. 5729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int hashCode = url.hashCode(); 5739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project StringBuffer ret = new StringBuffer(8); 5749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project appendAsHex(hashCode, ret); 5759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String path = ret.toString(); 5769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project File file = new File(mBaseDir, path); 5779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (true) { 5789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean checkOldPath = true; 5799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Check hash collision. If the hash file doesn't exist, just 5809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // continue. There is a chance that the old cache file is not 5819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // same as the hash file. As mDataBase.getCache() is more 5829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // expansive than "leak" a file until clear cache, don't bother. 5839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // If the hash file exists, make sure that it is same as the 5849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // cache file. If it is not, resolve the collision. 5859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project while (file.exists()) { 5869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (checkOldPath) { 5879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project CacheResult oldResult = mDataBase.getCache(url); 5889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (oldResult != null && oldResult.contentLength > 0) { 5899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (path.equals(oldResult.localPath)) { 5909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project path = oldResult.localPath; 5919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 5929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project path = oldResult.localPath; 5939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project file = new File(mBaseDir, path); 5949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 5969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project checkOldPath = false; 5989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ret = new StringBuffer(8); 6009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project appendAsHex(++hashCode, ret); 6019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project path = ret.toString(); 6029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project file = new File(mBaseDir, path); 6039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project cacheRet.localPath = path; 6069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project cacheRet.outFile = file; 6079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 6089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // get hash in byte[] 6099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Digest digest = new SHA1Digest(); 6109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int digestLen = digest.getDigestSize(); 6119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project byte[] hash = new byte[digestLen]; 6129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int urlLen = url.length(); 6139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project byte[] data = new byte[urlLen]; 6149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project url.getBytes(0, urlLen, data, 0); 6159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project digest.update(data, 0, urlLen); 6169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project digest.doFinal(hash, 0); 6179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // convert byte[] to hex String 6189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project StringBuffer result = new StringBuffer(2 * digestLen); 6199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int i = 0; i < digestLen; i = i + 4) { 6209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int h = (0x00ff & hash[i]) << 24 | (0x00ff & hash[i + 1]) << 16 6219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project | (0x00ff & hash[i + 2]) << 8 | (0x00ff & hash[i + 3]); 6229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project appendAsHex(h, result); 6239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project cacheRet.localPath = result.toString(); 6259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project cacheRet.outFile = new File(mBaseDir, cacheRet.localPath); 6269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static void appendAsHex(int i, StringBuffer ret) { 6309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String hex = Integer.toHexString(i); 6319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project switch (hex.length()) { 6329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project case 1: 6339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ret.append("0000000"); 6349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 6359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project case 2: 6369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ret.append("000000"); 6379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 6389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project case 3: 6399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ret.append("00000"); 6409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 6419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project case 4: 6429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ret.append("0000"); 6439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 6449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project case 5: 6459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ret.append("000"); 6469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 6479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project case 6: 6489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ret.append("00"); 6499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 6509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project case 7: 6519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ret.append("0"); 6529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 6539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ret.append(hex); 6559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static CacheResult parseHeaders(int statusCode, Headers headers, 6589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String mimeType) { 659998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba // if the contentLength is already larger than CACHE_MAX_SIZE, skip it 660998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba if (headers.getContentLength() > CACHE_MAX_SIZE) return null; 661998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba 662a1ba11bd78f9540e58516dd742fe4cc8726e262fAndrei Popescu // The HTML 5 spec, section 6.9.4, step 7.3 of the application cache 663a1ba11bd78f9540e58516dd742fe4cc8726e262fAndrei Popescu // process states that HTTP caching rules are ignored for the 664a1ba11bd78f9540e58516dd742fe4cc8726e262fAndrei Popescu // purposes of the application cache download process. 665a1ba11bd78f9540e58516dd742fe4cc8726e262fAndrei Popescu // At this point we can't tell that if a file is part of this process, 666a1ba11bd78f9540e58516dd742fe4cc8726e262fAndrei Popescu // except for the manifest, which has its own mimeType. 667a1ba11bd78f9540e58516dd742fe4cc8726e262fAndrei Popescu // TODO: work out a way to distinguish all responses that are part of 668a1ba11bd78f9540e58516dd742fe4cc8726e262fAndrei Popescu // the application download process and skip them. 669a1ba11bd78f9540e58516dd742fe4cc8726e262fAndrei Popescu if (MANIFEST_MIME.equals(mimeType)) return null; 670a1ba11bd78f9540e58516dd742fe4cc8726e262fAndrei Popescu 6719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // TODO: if authenticated or secure, return null 6729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project CacheResult ret = new CacheResult(); 6739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ret.httpStatusCode = statusCode; 6749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String location = headers.getLocation(); 6769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (location != null) ret.location = location; 6779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ret.expires = -1; 679e64c5567de20d06ac7ed1f5a01f018991cd40a52Grace Kloba ret.expiresString = headers.getExpires(); 680e64c5567de20d06ac7ed1f5a01f018991cd40a52Grace Kloba if (ret.expiresString != null) { 6819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 682e64c5567de20d06ac7ed1f5a01f018991cd40a52Grace Kloba ret.expires = HttpDateTime.parse(ret.expiresString); 6839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } catch (IllegalArgumentException ex) { 6849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Take care of the special "-1" and "0" cases 685e64c5567de20d06ac7ed1f5a01f018991cd40a52Grace Kloba if ("-1".equals(ret.expiresString) 686e64c5567de20d06ac7ed1f5a01f018991cd40a52Grace Kloba || "0".equals(ret.expiresString)) { 6879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // make it expired, but can be used for history navigation 6889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ret.expires = 0; 6899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 690e64c5567de20d06ac7ed1f5a01f018991cd40a52Grace Kloba Log.e(LOGTAG, "illegal expires: " + ret.expiresString); 6919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6950b956e1353a691674cb22c899c5a444b92532b60Grace Kloba String contentDisposition = headers.getContentDisposition(); 6960b956e1353a691674cb22c899c5a444b92532b60Grace Kloba if (contentDisposition != null) { 6970b956e1353a691674cb22c899c5a444b92532b60Grace Kloba ret.contentdisposition = contentDisposition; 6980b956e1353a691674cb22c899c5a444b92532b60Grace Kloba } 6990b956e1353a691674cb22c899c5a444b92532b60Grace Kloba 7009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String lastModified = headers.getLastModified(); 7019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (lastModified != null) ret.lastModified = lastModified; 7029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 7039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String etag = headers.getEtag(); 7049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (etag != null) ret.etag = etag; 7059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 7069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String cacheControl = headers.getCacheControl(); 7079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (cacheControl != null) { 7089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String[] controls = cacheControl.toLowerCase().split("[ ,;]"); 7099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int i = 0; i < controls.length; i++) { 7109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (NO_STORE.equals(controls[i])) { 7119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return null; 7129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // According to the spec, 'no-cache' means that the content 7149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // must be re-validated on every load. It does not mean that 7159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // the content can not be cached. set to expire 0 means it 7169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // can only be used in CACHE_MODE_CACHE_ONLY case 71752cf58a2a47e4dc975314fab44783c7e4654ca6dGrace Kloba if (NO_CACHE.equals(controls[i])) { 7189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ret.expires = 0; 7199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (controls[i].startsWith(MAX_AGE)) { 7209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int separator = controls[i].indexOf('='); 7219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (separator < 0) { 7229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project separator = controls[i].indexOf(':'); 7239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (separator > 0) { 7259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String s = controls[i].substring(separator + 1); 7269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 7279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project long sec = Long.parseLong(s); 7289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (sec >= 0) { 7299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ret.expires = System.currentTimeMillis() + 1000 7309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * sec; 7319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } catch (NumberFormatException ex) { 7339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if ("1d".equals(s)) { 7349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Take care of the special "1d" case 7359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ret.expires = System.currentTimeMillis() + 86400000; // 24*60*60*1000 7369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 7379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Log.e(LOGTAG, "exception in parseHeaders for " 7389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project + "max-age:" 7399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project + controls[i].substring(separator + 1)); 7409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ret.expires = 0; 7419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 7489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // According to RFC 2616 section 14.32: 7499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // HTTP/1.1 caches SHOULD treat "Pragma: no-cache" as if the 7509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // client had sent "Cache-Control: no-cache" 7519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (NO_CACHE.equals(headers.getPragma())) { 7529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ret.expires = 0; 7539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 7559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // According to RFC 2616 section 13.2.4, if an expiration has not been 7569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // explicitly defined a heuristic to set an expiration may be used. 7579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (ret.expires == -1) { 7589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (ret.httpStatusCode == 301) { 7599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // If it is a permanent redirect, and it did not have an 7609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // explicit cache directive, then it never expires 7619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ret.expires = Long.MAX_VALUE; 7629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (ret.httpStatusCode == 302 || ret.httpStatusCode == 307) { 7639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // If it is temporary redirect, expires 7649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ret.expires = 0; 7659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (ret.lastModified == null) { 7669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // When we have no last-modified, then expire the content with 7679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // in 24hrs as, according to the RFC, longer time requires a 7689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // warning 113 to be added to the response. 7699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 7709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Only add the default expiration for non-html markup. Some 7719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // sites like news.google.com have no cache directives. 7729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (!mimeType.startsWith("text/html")) { 7739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ret.expires = System.currentTimeMillis() + 86400000; // 24*60*60*1000 7749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 7759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Setting a expires as zero will cache the result for 7769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // forward/back nav. 7779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ret.expires = 0; 7789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 7809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // If we have a last-modified value, we could use it to set the 7819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // expiration. Suggestion from RFC is 10% of time since 7829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // last-modified. As we are on mobile, loads are expensive, 7839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // increasing this to 20%. 7849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 7859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // 24 * 60 * 60 * 1000 7869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project long lastmod = System.currentTimeMillis() + 86400000; 7879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 7889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project lastmod = HttpDateTime.parse(ret.lastModified); 7899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } catch (IllegalArgumentException ex) { 7909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Log.e(LOGTAG, "illegal lastModified: " + ret.lastModified); 7919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project long difference = System.currentTimeMillis() - lastmod; 7939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (difference > 0) { 7949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ret.expires = System.currentTimeMillis() + difference / 5; 7959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 7969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // last modified is in the future, expire the content 7979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // on the last modified 7989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ret.expires = lastmod; 7999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return ret; 8049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 806